From 871063cd5004f6f409f4fe55da36280d8fe117d0 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Tue, 8 Jun 2021 13:31:29 +0800 Subject: [PATCH 01/12] Upgraded version to 0.9.1 --- README.rst | 6 +++--- build.gradle.kts | 2 +- cpp/build.cmd | 2 +- cpp/build.sh | 2 +- cpp/jni/javet_resource_node.rc | 12 ++++++------ cpp/jni/javet_resource_v8.rc | 12 ++++++------ docs/tutorial/installation.rst | 6 +++--- pom.xml | 4 ++-- scripts/node/javet-rebuild/rebuild.cmd | 2 +- scripts/node/javet-rebuild/rebuild.sh | 2 +- scripts/python/change_javet_version.py | 2 +- .../com/caoccao/javet/interop/JavetLibLoader.java | 2 +- 12 files changed, 27 insertions(+), 27 deletions(-) diff --git a/README.rst b/README.rst index 8f548262c..802e97665 100644 --- a/README.rst +++ b/README.rst @@ -40,7 +40,7 @@ Maven com.caoccao.javet javet - 0.9.0 + 0.9.1 Gradle Kotlin DSL @@ -48,14 +48,14 @@ Gradle Kotlin DSL .. code-block:: kotlin - implementation("com.caoccao.javet:javet:0.9.0") + implementation("com.caoccao.javet:javet:0.9.1") Gradle Groovy DSL ^^^^^^^^^^^^^^^^^ .. code-block:: groovy - implementation 'com.caoccao.javet:javet:0.9.0' + implementation 'com.caoccao.javet:javet:0.9.1' Hello Javet ----------- diff --git a/build.gradle.kts b/build.gradle.kts index 7e453e896..b73ebf2bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ repositories { } group = "com.caoccao.javet" -version = "0.9.0" +version = "0.9.1" repositories { mavenCentral() diff --git a/cpp/build.cmd b/cpp/build.cmd index c3025d551..6f15f946d 100644 --- a/cpp/build.cmd +++ b/cpp/build.cmd @@ -1,7 +1,7 @@ @echo off REM Usage for V8: build -DV8_DIR=C:\v8 REM Usage for Node: build -DNODE_DIR=C:\node -SET JAVET_VERSION=0.9.0 +SET JAVET_VERSION=0.9.1 rd /s/q build mkdir build cd build diff --git a/cpp/build.sh b/cpp/build.sh index b97441795..bd7a17f96 100755 --- a/cpp/build.sh +++ b/cpp/build.sh @@ -2,7 +2,7 @@ # Usage for V8: build -DV8_DIR=~/v8 # Usage for Node: build -DNODE_DIR=~/node -JAVET_VERSION=0.9.0 +JAVET_VERSION=0.9.1 rm -rf build mkdir build cd build diff --git a/cpp/jni/javet_resource_node.rc b/cpp/jni/javet_resource_node.rc index a7705f775..a438f5b43 100644 --- a/cpp/jni/javet_resource_node.rc +++ b/cpp/jni/javet_resource_node.rc @@ -61,8 +61,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,0,0 - PRODUCTVERSION 0,9,0,0 + FILEVERSION 0,9,1,0 + PRODUCTVERSION 0,9,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "caoccao.com" VALUE "FileDescription", "caoccao.com" - VALUE "FileVersion", "0.9.0.0" - VALUE "InternalName", "libjavet-node-windows-x86_64.v.0.9.0.dll" + VALUE "FileVersion", "0.9.1.0" + VALUE "InternalName", "libjavet-node-windows-x86_64.v.0.9.1.dll" VALUE "LegalCopyright", "Copyright (C) 2021" - VALUE "OriginalFilename", "libjavet-node-windows-x86_64.v.0.9.0.dll" + VALUE "OriginalFilename", "libjavet-node-windows-x86_64.v.0.9.1.dll" VALUE "ProductName", "Javet Windows" - VALUE "ProductVersion", "0.9.0.0" + VALUE "ProductVersion", "0.9.1.0" END END BLOCK "VarFileInfo" diff --git a/cpp/jni/javet_resource_v8.rc b/cpp/jni/javet_resource_v8.rc index f111669f3..94ee1799e 100644 --- a/cpp/jni/javet_resource_v8.rc +++ b/cpp/jni/javet_resource_v8.rc @@ -61,8 +61,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,0,0 - PRODUCTVERSION 0,9,0,0 + FILEVERSION 0,9,1,0 + PRODUCTVERSION 0,9,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "caoccao.com" VALUE "FileDescription", "caoccao.com" - VALUE "FileVersion", "0.9.0.0" - VALUE "InternalName", "libjavet-v8-windows-x86_64.v.0.9.0.dll" + VALUE "FileVersion", "0.9.1.0" + VALUE "InternalName", "libjavet-v8-windows-x86_64.v.0.9.1.dll" VALUE "LegalCopyright", "Copyright (C) 2021" - VALUE "OriginalFilename", "libjavet-v8-windows-x86_64.v.0.9.0.dll" + VALUE "OriginalFilename", "libjavet-v8-windows-x86_64.v.0.9.1.dll" VALUE "ProductName", "Javet Windows" - VALUE "ProductVersion", "0.9.0.0" + VALUE "ProductVersion", "0.9.1.0" END END BLOCK "VarFileInfo" diff --git a/docs/tutorial/installation.rst b/docs/tutorial/installation.rst index 07efefa61..a32e2fb54 100644 --- a/docs/tutorial/installation.rst +++ b/docs/tutorial/installation.rst @@ -13,7 +13,7 @@ Maven com.caoccao.javet javet - 0.9.0 + 0.9.1 Gradle Kotlin DSL @@ -21,14 +21,14 @@ Gradle Kotlin DSL .. code-block:: kotlin - implementation("com.caoccao.javet:javet:0.9.0") + implementation("com.caoccao.javet:javet:0.9.1") Gradle Groovy DSL ----------------- .. code-block:: groovy - implementation 'com.caoccao.javet:javet:0.9.0' + implementation 'com.caoccao.javet:javet:0.9.1' OS Compatibility ================ diff --git a/pom.xml b/pom.xml index 6e668e9c1..a77e5cb31 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.caoccao.javet javet - 0.9.0 + 0.9.1 javet Javet is Java + V8 (JAVa + V + EighT). It is a way of embedding V8 in Java. https://github.com/caoccao/Javet @@ -28,7 +28,7 @@ scm:git:git://github.com/caoccao/Javet.git scm:git:git@github.com:caoccao/caoccao.git https://github.com/caoccao/Javet - javet-0.9.0 + javet-0.9.1 diff --git a/scripts/node/javet-rebuild/rebuild.cmd b/scripts/node/javet-rebuild/rebuild.cmd index ef5ee690a..cf489ae0b 100644 --- a/scripts/node/javet-rebuild/rebuild.cmd +++ b/scripts/node/javet-rebuild/rebuild.cmd @@ -1,5 +1,5 @@ @echo off -SET NODE_LIB_FILE="..\..\..\..\..\..\build\libs\libjavet-node-windows-x86_64.v.0.9.0.lib" +SET NODE_LIB_FILE="..\..\..\..\..\..\build\libs\libjavet-node-windows-x86_64.v.0.9.1.lib" cd %NODE_MODULE_ROOT% call node-gyp clean call node-gyp configure --module_name=%NODE_MODULE_NAME% --module_path=%NODE_MODULE_PATH% --node_lib_file=%NODE_LIB_FILE% diff --git a/scripts/node/javet-rebuild/rebuild.sh b/scripts/node/javet-rebuild/rebuild.sh index e1cca91e4..2d20f3753 100755 --- a/scripts/node/javet-rebuild/rebuild.sh +++ b/scripts/node/javet-rebuild/rebuild.sh @@ -1 +1 @@ -patchelf --add-needed libjavet-node-linux-x86_64.v.0.9.0.so ${NODE_MODULE_FILE} +patchelf --add-needed libjavet-node-linux-x86_64.v.0.9.1.so ${NODE_MODULE_FILE} diff --git a/scripts/python/change_javet_version.py b/scripts/python/change_javet_version.py index 3c30dca45..cdde6ce7c 100644 --- a/scripts/python/change_javet_version.py +++ b/scripts/python/change_javet_version.py @@ -109,7 +109,7 @@ def _update(self, relative_file_path: str, line_separator: str, *patterns: list) logging.info(' Updated.') def main(): - change_javet_version = ChangeJavetVersion('0.9.0') + change_javet_version = ChangeJavetVersion('0.9.1') change_javet_version.update() return 0 diff --git a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java index 754976b10..3459cd306 100644 --- a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java +++ b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java @@ -30,7 +30,7 @@ import java.util.Objects; public final class JavetLibLoader { - static final String LIB_VERSION = "0.9.0"; + static final String LIB_VERSION = "0.9.1"; private static final String CHMOD = "chmod"; private static final String XRR = "755"; private static final String LIB_FILE_NAME_FORMAT = "libjavet-{0}-{1}-x86_64.v.{2}.{3}"; From 75b422647e31e1af9cce24e85911c626f97f02d1 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Tue, 8 Jun 2021 13:31:29 +0800 Subject: [PATCH 02/12] Upgraded version to 0.9.1 --- README.rst | 6 +++--- build.gradle.kts | 2 +- cpp/build.cmd | 2 +- cpp/build.sh | 2 +- cpp/jni/javet_resource_node.rc | 12 ++++++------ cpp/jni/javet_resource_v8.rc | 12 ++++++------ docs/tutorial/installation.rst | 6 +++--- pom.xml | 4 ++-- scripts/node/javet-rebuild/rebuild.cmd | 2 +- scripts/node/javet-rebuild/rebuild.sh | 2 +- scripts/python/change_javet_version.py | 2 +- .../com/caoccao/javet/interop/JavetLibLoader.java | 2 +- 12 files changed, 27 insertions(+), 27 deletions(-) diff --git a/README.rst b/README.rst index aa5cb6ac5..019e34185 100644 --- a/README.rst +++ b/README.rst @@ -40,7 +40,7 @@ Maven com.caoccao.javet javet - 0.9.0 + 0.9.1 Gradle Kotlin DSL @@ -48,14 +48,14 @@ Gradle Kotlin DSL .. code-block:: kotlin - implementation("com.caoccao.javet:javet:0.9.0") + implementation("com.caoccao.javet:javet:0.9.1") Gradle Groovy DSL ^^^^^^^^^^^^^^^^^ .. code-block:: groovy - implementation 'com.caoccao.javet:javet:0.9.0' + implementation 'com.caoccao.javet:javet:0.9.1' Hello Javet ----------- diff --git a/build.gradle.kts b/build.gradle.kts index 7e453e896..b73ebf2bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ repositories { } group = "com.caoccao.javet" -version = "0.9.0" +version = "0.9.1" repositories { mavenCentral() diff --git a/cpp/build.cmd b/cpp/build.cmd index c3025d551..6f15f946d 100644 --- a/cpp/build.cmd +++ b/cpp/build.cmd @@ -1,7 +1,7 @@ @echo off REM Usage for V8: build -DV8_DIR=C:\v8 REM Usage for Node: build -DNODE_DIR=C:\node -SET JAVET_VERSION=0.9.0 +SET JAVET_VERSION=0.9.1 rd /s/q build mkdir build cd build diff --git a/cpp/build.sh b/cpp/build.sh index b97441795..bd7a17f96 100755 --- a/cpp/build.sh +++ b/cpp/build.sh @@ -2,7 +2,7 @@ # Usage for V8: build -DV8_DIR=~/v8 # Usage for Node: build -DNODE_DIR=~/node -JAVET_VERSION=0.9.0 +JAVET_VERSION=0.9.1 rm -rf build mkdir build cd build diff --git a/cpp/jni/javet_resource_node.rc b/cpp/jni/javet_resource_node.rc index a7705f775..a438f5b43 100644 --- a/cpp/jni/javet_resource_node.rc +++ b/cpp/jni/javet_resource_node.rc @@ -61,8 +61,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,0,0 - PRODUCTVERSION 0,9,0,0 + FILEVERSION 0,9,1,0 + PRODUCTVERSION 0,9,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "caoccao.com" VALUE "FileDescription", "caoccao.com" - VALUE "FileVersion", "0.9.0.0" - VALUE "InternalName", "libjavet-node-windows-x86_64.v.0.9.0.dll" + VALUE "FileVersion", "0.9.1.0" + VALUE "InternalName", "libjavet-node-windows-x86_64.v.0.9.1.dll" VALUE "LegalCopyright", "Copyright (C) 2021" - VALUE "OriginalFilename", "libjavet-node-windows-x86_64.v.0.9.0.dll" + VALUE "OriginalFilename", "libjavet-node-windows-x86_64.v.0.9.1.dll" VALUE "ProductName", "Javet Windows" - VALUE "ProductVersion", "0.9.0.0" + VALUE "ProductVersion", "0.9.1.0" END END BLOCK "VarFileInfo" diff --git a/cpp/jni/javet_resource_v8.rc b/cpp/jni/javet_resource_v8.rc index f111669f3..94ee1799e 100644 --- a/cpp/jni/javet_resource_v8.rc +++ b/cpp/jni/javet_resource_v8.rc @@ -61,8 +61,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,0,0 - PRODUCTVERSION 0,9,0,0 + FILEVERSION 0,9,1,0 + PRODUCTVERSION 0,9,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "caoccao.com" VALUE "FileDescription", "caoccao.com" - VALUE "FileVersion", "0.9.0.0" - VALUE "InternalName", "libjavet-v8-windows-x86_64.v.0.9.0.dll" + VALUE "FileVersion", "0.9.1.0" + VALUE "InternalName", "libjavet-v8-windows-x86_64.v.0.9.1.dll" VALUE "LegalCopyright", "Copyright (C) 2021" - VALUE "OriginalFilename", "libjavet-v8-windows-x86_64.v.0.9.0.dll" + VALUE "OriginalFilename", "libjavet-v8-windows-x86_64.v.0.9.1.dll" VALUE "ProductName", "Javet Windows" - VALUE "ProductVersion", "0.9.0.0" + VALUE "ProductVersion", "0.9.1.0" END END BLOCK "VarFileInfo" diff --git a/docs/tutorial/installation.rst b/docs/tutorial/installation.rst index 07efefa61..a32e2fb54 100644 --- a/docs/tutorial/installation.rst +++ b/docs/tutorial/installation.rst @@ -13,7 +13,7 @@ Maven com.caoccao.javet javet - 0.9.0 + 0.9.1 Gradle Kotlin DSL @@ -21,14 +21,14 @@ Gradle Kotlin DSL .. code-block:: kotlin - implementation("com.caoccao.javet:javet:0.9.0") + implementation("com.caoccao.javet:javet:0.9.1") Gradle Groovy DSL ----------------- .. code-block:: groovy - implementation 'com.caoccao.javet:javet:0.9.0' + implementation 'com.caoccao.javet:javet:0.9.1' OS Compatibility ================ diff --git a/pom.xml b/pom.xml index 6e668e9c1..a77e5cb31 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.caoccao.javet javet - 0.9.0 + 0.9.1 javet Javet is Java + V8 (JAVa + V + EighT). It is a way of embedding V8 in Java. https://github.com/caoccao/Javet @@ -28,7 +28,7 @@ scm:git:git://github.com/caoccao/Javet.git scm:git:git@github.com:caoccao/caoccao.git https://github.com/caoccao/Javet - javet-0.9.0 + javet-0.9.1 diff --git a/scripts/node/javet-rebuild/rebuild.cmd b/scripts/node/javet-rebuild/rebuild.cmd index ef5ee690a..cf489ae0b 100644 --- a/scripts/node/javet-rebuild/rebuild.cmd +++ b/scripts/node/javet-rebuild/rebuild.cmd @@ -1,5 +1,5 @@ @echo off -SET NODE_LIB_FILE="..\..\..\..\..\..\build\libs\libjavet-node-windows-x86_64.v.0.9.0.lib" +SET NODE_LIB_FILE="..\..\..\..\..\..\build\libs\libjavet-node-windows-x86_64.v.0.9.1.lib" cd %NODE_MODULE_ROOT% call node-gyp clean call node-gyp configure --module_name=%NODE_MODULE_NAME% --module_path=%NODE_MODULE_PATH% --node_lib_file=%NODE_LIB_FILE% diff --git a/scripts/node/javet-rebuild/rebuild.sh b/scripts/node/javet-rebuild/rebuild.sh index e1cca91e4..2d20f3753 100755 --- a/scripts/node/javet-rebuild/rebuild.sh +++ b/scripts/node/javet-rebuild/rebuild.sh @@ -1 +1 @@ -patchelf --add-needed libjavet-node-linux-x86_64.v.0.9.0.so ${NODE_MODULE_FILE} +patchelf --add-needed libjavet-node-linux-x86_64.v.0.9.1.so ${NODE_MODULE_FILE} diff --git a/scripts/python/change_javet_version.py b/scripts/python/change_javet_version.py index 3c30dca45..cdde6ce7c 100644 --- a/scripts/python/change_javet_version.py +++ b/scripts/python/change_javet_version.py @@ -109,7 +109,7 @@ def _update(self, relative_file_path: str, line_separator: str, *patterns: list) logging.info(' Updated.') def main(): - change_javet_version = ChangeJavetVersion('0.9.0') + change_javet_version = ChangeJavetVersion('0.9.1') change_javet_version.update() return 0 diff --git a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java index 754976b10..3459cd306 100644 --- a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java +++ b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java @@ -30,7 +30,7 @@ import java.util.Objects; public final class JavetLibLoader { - static final String LIB_VERSION = "0.9.0"; + static final String LIB_VERSION = "0.9.1"; private static final String CHMOD = "chmod"; private static final String XRR = "755"; private static final String LIB_FILE_NAME_FORMAT = "libjavet-{0}-{1}-x86_64.v.{2}.{3}"; From 71691c72eed9028d3bf26fabbfe4d7d26f46b4f2 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Tue, 8 Jun 2021 13:36:44 +0800 Subject: [PATCH 03/12] Updated doc --- docs/development/tools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/tools.rst b/docs/development/tools.rst index b8b20e7c1..d20e0caf3 100644 --- a/docs/development/tools.rst +++ b/docs/development/tools.rst @@ -20,7 +20,7 @@ For now, Gradle v6.7 + Kotlin DSL constructs the build system. Node.js ================= -Node.js 14.16.1+ is supported. +Node.js 14.17.0+ is supported. Maven (Optional) ================ From 4e0a509793c6fc6bdd8820382e392ff26d6c0244 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Tue, 8 Jun 2021 18:26:28 +0800 Subject: [PATCH 04/12] Moved V8 host to a custom class loader --- .../javet/interop/JavetClassLoader.java | 6 +- .../com/caoccao/javet/interop/NodeNative.java | 4 +- .../com/caoccao/javet/interop/V8Host.java | 58 ++++++++----------- .../com/caoccao/javet/interop/V8Runtime.java | 1 + 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java b/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java index 982c08134..b1b3ee325 100644 --- a/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java +++ b/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java @@ -25,6 +25,7 @@ import java.io.DataInputStream; import java.io.InputStream; import java.lang.reflect.Constructor; +import java.util.Objects; class JavetClassLoader extends ClassLoader { protected static final String JAVET_LIB_LOADER_CLASS_NAME = JavetLibLoader.class.getName(); @@ -35,14 +36,15 @@ class JavetClassLoader extends ClassLoader { JavetClassLoader(ClassLoader parent, JSRuntimeType jsRuntimeType) { super(parent); - assert jsRuntimeType.isNode(); // Prevent V8 from being dynamically loaded. + Objects.requireNonNull(jsRuntimeType); this.jsRuntimeType = jsRuntimeType; } IV8Native getNative() throws JavetException { try { Class classNative = loadClass(jsRuntimeType.isNode() ? NODE_NATIVE_CLASS_NAME : V8_NATIVE_CLASS_NAME); - Constructor constructor = classNative.getConstructor(); + Constructor constructor = classNative.getDeclaredConstructor(); + constructor.setAccessible(true); return (IV8Native) constructor.newInstance(); } catch (Exception e) { e.printStackTrace(System.err); diff --git a/src/main/java/com/caoccao/javet/interop/NodeNative.java b/src/main/java/com/caoccao/javet/interop/NodeNative.java index 1e7a83e6f..6cafa162a 100644 --- a/src/main/java/com/caoccao/javet/interop/NodeNative.java +++ b/src/main/java/com/caoccao/javet/interop/NodeNative.java @@ -21,8 +21,8 @@ * The type Node native is the pure interface that defines the JNI C++ implementation. * It has to be public so that dynamic library loading can work. */ -public class NodeNative extends V8Native implements INodeNative { - public NodeNative() { +class NodeNative extends V8Native implements INodeNative { + NodeNative() { } @Override diff --git a/src/main/java/com/caoccao/javet/interop/V8Host.java b/src/main/java/com/caoccao/javet/interop/V8Host.java index c77cda509..d625e0d15 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Host.java +++ b/src/main/java/com/caoccao/javet/interop/V8Host.java @@ -43,10 +43,7 @@ public final class V8Host implements AutoCloseable { private static final String FLAG_TRACK_RETAINING_PATH = "--track-retaining-path"; private static final String FLAG_USE_STRICT = "--use-strict"; private static final String SPACE = " "; - private static final Object nodeLock = new Object(); - private static final V8Host v8Instance = new V8Host(JSRuntimeType.V8); private static volatile double memoryUsageThresholdRatio = 0.7; - private static volatile V8Host nodeInstance; private final V8Flags flags; private final JSRuntimeType jsRuntimeType; private final IJavetLogger logger; @@ -91,25 +88,18 @@ public static V8Host getInstance(JSRuntimeType jsRuntimeType) { * @return the Node instance */ public static V8Host getNodeInstance() { - if (nodeInstance == null) { - synchronized (nodeLock) { - if (nodeInstance == null) { - nodeInstance = new V8Host(JSRuntimeType.Node); - } - } - } - return nodeInstance; + return NodeInstanceHolder.INSTANCE; } /** * Gets V8 instance. *

- * Note: V8 runtime library is loaded by the default class loader. + * Note: V8 runtime library is loaded by a custom class loader. * * @return the V8 instance */ public static V8Host getV8Instance() { - return v8Instance; + return V8InstanceHolder.INSTANCE; } public static double getMemoryUsageThresholdRatio() { @@ -243,38 +233,30 @@ public JSRuntimeType getJSRuntimeType() { return jsRuntimeType; } + // This function is not open because it may cause core dump. private void loadLibrary() { if (!libLoaded) { - if (jsRuntimeType.isNode()) { - try { - javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), jsRuntimeType); - javetClassLoader.load(); - v8Native = javetClassLoader.getNative(); - libLoaded = true; - } catch (JavetException e) { - logger.logError(e, "Failed to load Javet lib with error {0}.", e.getMessage()); - lastException = e; - } - } else { - try { - JavetLibLoader javetLibLoader = new JavetLibLoader(jsRuntimeType); - javetLibLoader.load(); - v8Native = new V8Native(); - libLoaded = true; - } catch (JavetException e) { - logger.logError(e, "Failed to load Javet lib with error {0}.", e.getMessage()); - lastException = e; - } + try { + javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), jsRuntimeType); + javetClassLoader.load(); + v8Native = javetClassLoader.getNative(); + libLoaded = true; + } catch (JavetException e) { + logger.logError(e, "Failed to load Javet lib with error {0}.", e.getMessage()); + lastException = e; } } } + // This function is not open because it may cause core dump. private void unloadLibrary() { - if (jsRuntimeType.isNode() && libLoaded) { + if (libLoaded) { + v8Native = null; javetClassLoader = null; System.gc(); System.runFinalization(); libLoaded = false; + lastException = null; } } @@ -318,4 +300,12 @@ public boolean setFlags() { } return false; } + + private static class NodeInstanceHolder { + private static V8Host INSTANCE = new V8Host(JSRuntimeType.Node); + } + + private static class V8InstanceHolder { + private static V8Host INSTANCE = new V8Host(JSRuntimeType.V8); + } } diff --git a/src/main/java/com/caoccao/javet/interop/V8Runtime.java b/src/main/java/com/caoccao/javet/interop/V8Runtime.java index 842ef7d74..137494849 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Runtime.java +++ b/src/main/java/com/caoccao/javet/interop/V8Runtime.java @@ -153,6 +153,7 @@ public void close(boolean forceClose) throws JavetException { removeAllReferences(); v8Host.closeV8Runtime(this); handle = INVALID_HANDLE; + v8Native = null; } } From 40ce78afb413a7dc29efb9217c6d2b4bbca4baf8 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Wed, 9 Jun 2021 13:58:36 +0800 Subject: [PATCH 05/12] Enabled unloadLibrary() and loadLibrary() in V8Host --- .../com_caoccao_javet_interop_V8Native.cpp | 58 ++++-- cpp/jni/javet_native.cpp | 13 +- cpp/jni/javet_native.h | 4 +- cpp/jni/javet_node.h | 2 + docs/development/design.rst | 6 +- docs/reference/index.rst | 1 + docs/reference/load_and_unload.rst | 44 +++++ docs/release_notes.rst | 6 + docs/resources/images/javet_modes.png | Bin 63625 -> 95301 bytes .../com/caoccao/javet/interop/V8Host.java | 183 ++++++++++++++++-- .../javet/interop/TestJavetLibLoader.java | 61 +++++- 11 files changed, 332 insertions(+), 46 deletions(-) create mode 100644 docs/reference/load_and_unload.rst diff --git a/cpp/jni/com_caoccao_javet_interop_V8Native.cpp b/cpp/jni/com_caoccao_javet_interop_V8Native.cpp index 8e60ed667..e52caba2e 100644 --- a/cpp/jni/com_caoccao_javet_interop_V8Native.cpp +++ b/cpp/jni/com_caoccao_javet_interop_V8Native.cpp @@ -43,14 +43,24 @@ namespace Javet { #ifdef ENABLE_NODE namespace NodeNative { + static jclass jclassV8Host; + static jmethodID jmethodIDV8HostIsLibraryReloadable; + static std::shared_ptr GlobalNodeArrayBufferAllocator; - void Dispose() { - GlobalNodeArrayBufferAllocator.reset(); + void Dispose(JNIEnv* jniEnv) { + if (!jniEnv->CallStaticBooleanMethod(jclassV8Host, jmethodIDV8HostIsLibraryReloadable)) { + GlobalNodeArrayBufferAllocator.reset(); + } } void Initialize(JNIEnv* jniEnv) { - GlobalNodeArrayBufferAllocator = node::ArrayBufferAllocator::Create(); + jclassV8Host = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/interop/V8Host")); + jmethodIDV8HostIsLibraryReloadable = jniEnv->GetStaticMethodID(jclassV8Host, "isLibraryReloadable", "()Z"); + + if (!GlobalNodeArrayBufferAllocator) { + GlobalNodeArrayBufferAllocator = node::ArrayBufferAllocator::Create(); + } } } #endif @@ -62,15 +72,20 @@ namespace Javet { static std::unique_ptr GlobalV8Platform; #endif + static jclass jclassV8Host; + static jmethodID jmethodIDV8HostIsLibraryReloadable; + static jclass jclassV8ValueInteger; static jmethodID jmethodIDV8ValueIntegerToPrimitive; static jclass jclassV8ValueString; static jmethodID jmethodIDV8ValueStringToPrimitive; - void Dispose() { - v8::V8::Dispose(); - v8::V8::ShutdownPlatform(); + void Dispose(JNIEnv* jniEnv) { + if (!jniEnv->CallStaticBooleanMethod(jclassV8Host, jmethodIDV8HostIsLibraryReloadable)) { + v8::V8::Dispose(); + v8::V8::ShutdownPlatform(); + } } /* @@ -79,6 +94,8 @@ namespace Javet { or runtime memory corruption will take place. */ void Initialize(JNIEnv* jniEnv) { + jclassV8Host = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/interop/V8Host")); + jmethodIDV8HostIsLibraryReloadable = jniEnv->GetStaticMethodID(jclassV8Host, "isLibraryReloadable", "()Z"); jclassV8ValueInteger = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueInteger")); jmethodIDV8ValueIntegerToPrimitive = jniEnv->GetMethodID(jclassV8ValueInteger, JAVA_METHOD_TO_PRIMITIVE, "()I"); @@ -90,21 +107,26 @@ namespace Javet { #ifdef ENABLE_I18N v8::V8::InitializeICU(); #endif -#ifdef ENABLE_NODE - uv_setup_args(0, nullptr); - std::vector args{ "" }; - std::vector execArgs{ "" }; - std::vector errors; - int errorCode = node::InitializeNodeWithArgs(&args, &execArgs, &errors); - if (errorCode != 0) { - LOG_ERROR("Failed to call node::InitializeNodeWithArgs()."); + if (Javet::V8Native::GlobalV8Platform) { + LOG_INFO("V8::Initialize() is skipped."); } - Javet::V8Native::GlobalV8Platform = node::MultiIsolatePlatform::Create(4); + else { +#ifdef ENABLE_NODE + uv_setup_args(0, nullptr); + std::vector args{ "" }; + std::vector execArgs{ "" }; + std::vector errors; + int errorCode = node::InitializeNodeWithArgs(&args, &execArgs, &errors); + if (errorCode != 0) { + LOG_ERROR("Failed to call node::InitializeNodeWithArgs()."); + } + Javet::V8Native::GlobalV8Platform = node::MultiIsolatePlatform::Create(4); #else - Javet::V8Native::GlobalV8Platform = v8::platform::NewDefaultPlatform(); + Javet::V8Native::GlobalV8Platform = v8::platform::NewDefaultPlatform(); #endif - v8::V8::InitializePlatform(Javet::V8Native::GlobalV8Platform.get()); - v8::V8::Initialize(); + v8::V8::InitializePlatform(Javet::V8Native::GlobalV8Platform.get()); + v8::V8::Initialize(); + } LOG_INFO("V8::Initialize() ends."); } } diff --git a/cpp/jni/javet_native.cpp b/cpp/jni/javet_native.cpp index f5d19b5f2..0d3fb6dca 100644 --- a/cpp/jni/javet_native.cpp +++ b/cpp/jni/javet_native.cpp @@ -51,10 +51,19 @@ jint JNI_OnLoad(JavaVM* javaVM, void* reserved) { void JNI_OnUnload(JavaVM* javaVM, void* reserved) { LOG_INFO("JNI_OnUnload() begins."); + JNIEnv* jniEnv; + if (javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_8) != JNI_OK) { + LOG_ERROR("Failed to call JavaVM.GetEnv()."); + } + if (jniEnv == nullptr) { + LOG_ERROR("Failed to get JNIEnv."); + } + else { #ifdef ENABLE_NODE - Javet::NodeNative::Dispose(); + Javet::NodeNative::Dispose(jniEnv); #endif - Javet::V8Native::Dispose(); + Javet::V8Native::Dispose(jniEnv); + } LOG_INFO("JNI_OnUnload() ends."); } diff --git a/cpp/jni/javet_native.h b/cpp/jni/javet_native.h index 8e10273bb..d78ef5b0b 100644 --- a/cpp/jni/javet_native.h +++ b/cpp/jni/javet_native.h @@ -92,13 +92,13 @@ namespace Javet { #ifdef ENABLE_NODE namespace NodeNative { - void Dispose(); + void Dispose(JNIEnv* jniEnv); void Initialize(JNIEnv* jniEnv); } #endif namespace V8Native { - void Dispose(); + void Dispose(JNIEnv* jniEnv); void Initialize(JNIEnv* jniEnv); } } diff --git a/cpp/jni/javet_node.h b/cpp/jni/javet_node.h index d34ce6879..a36b0a0fa 100644 --- a/cpp/jni/javet_node.h +++ b/cpp/jni/javet_node.h @@ -21,6 +21,8 @@ #pragma warning(disable: 4275) #pragma warning(disable: 4251) +// #define NODE_WANT_INTERNALS 1 +// #include #include #include #pragma warning(default: 4275) diff --git a/docs/development/design.rst b/docs/development/design.rst index 9c9cb5328..93f3be9d5 100644 --- a/docs/development/design.rst +++ b/docs/development/design.rst @@ -34,16 +34,16 @@ Javet supports both Node.js mode and V8 mode both of which can co-exist in one J .. image:: ../resources/images/javet_modes.png?raw=true :alt: Javet Modes -As the diagram shows, Javet loads V8 v8.9+ in the default classloader as an out-of-box solution. Node.js is lazy loaded in a custom classloader. Detailed comparisons are as following. +As the diagram shows, both Node.js and V8 are lazy loaded in dedicated custom classloaders. Detailed comparisons are as following. =========================== ======================= ============================== Feature Node.js Mode V8 Mode =========================== ======================= ============================== -Built-in No **Yes** +Lazy Loadable **Yes** **Yes** Customization **High** **High** Node.js Ecosystem **Complete** No Security Low **High** -Unload Potentially Yes No +Unload **Yes** **Yes** V8 Ecosystem **Complete** **Complete** V8 Version Low **High** =========================== ======================= ============================== diff --git a/docs/reference/index.rst b/docs/reference/index.rst index c8bc4a079..e69865fb9 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -9,6 +9,7 @@ Javet Reference * `Lock `_ * `Termination `_ * `Logging `_ +* `Load and Unload `_ * `Best Practices `_ * `Performance `_ * `Error Codes `_ diff --git a/docs/reference/load_and_unload.rst b/docs/reference/load_and_unload.rst new file mode 100644 index 000000000..5f5fed88f --- /dev/null +++ b/docs/reference/load_and_unload.rst @@ -0,0 +1,44 @@ +=============== +Load and Unload +=============== + +As documented in `design <../development/design.rst>`_, Javet supports loading and unloading the JNI libraries during runtime in both Node.js and V8 modes. + +How? +==== + +Unload +------ + +Assuming the JNI library per mode is already loaded, here are the step-by-step on how to unload it. + +.. code-block:: java + + // Step 1: Set library reloadable. Why? Because Javet defaults that switch to false. + V8Host.setLibraryReloadable(true); + // Step 2: Get V8Host per JS runtime type. + V8Host v8Host = V8Host.getInstance(jsRuntimeType); + // Step 3: Unload the library. + v8Host.unloadLibrary(); + // Step 4: Restore the switch. + V8Host.setLibraryReloadable(false); + +Load +---- + +Assuming the JNI library per mode is already unloaded, here are the step-by-step on how to load it again. + +.. code-block:: java + + // Step 1: Get V8Host per JS runtime type. + V8Host v8Host = V8Host.getInstance(jsRuntimeType); + // Step 2: Load the library. + v8Host.loadLibrary(); + +Notes +===== + +* ``loadLibrary()`` is internally called by Javet in the first time and only takes effect after ``unloadLibrary()`` is called. +* ``loadLibrary()`` and ``unloadLibrary()`` are for experiment only. **They may be unstable and crash JVM. Please use this feature at your own risk.** + +[`Home <../../README.rst>`_] [`Javet Reference `_] diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 3f4dbcaec..26902e997 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -2,6 +2,12 @@ Release Notes ============= +0.9.1 +----- + +* Moved V8 to a custom classloader +* Enabled ``unloadLibrary()`` and ``loadLibrary()`` in ``V8Host`` + 0.9.0 ----- diff --git a/docs/resources/images/javet_modes.png b/docs/resources/images/javet_modes.png index 3753bb5cde0f74c00ca354a959b72044e2645563..b650a3127aae0eb5abed80eeb899fba5bac995cc 100644 GIT binary patch literal 95301 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 PH;pODtG_CfeG~S7WrM(M literal 63625 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 v8RuntimeMap; private JavetClassLoader javetClassLoader; - private boolean libLoaded; + private boolean libraryLoaded; private JavetException lastException; private boolean isolateCreated; private IV8Native v8Native; @@ -59,7 +66,7 @@ private V8Host(JSRuntimeType jsRuntimeType) { Objects.requireNonNull(jsRuntimeType); javetClassLoader = null; lastException = null; - libLoaded = false; + libraryLoaded = false; flags = new V8Flags(); logger = new JavetDefaultLogger(getClass().getName()); v8RuntimeMap = new ConcurrentHashMap<>(); @@ -70,6 +77,13 @@ private V8Host(JSRuntimeType jsRuntimeType) { v8Notifier = new V8Notifier(v8RuntimeMap); } + /** + * Gets instance by JS runtime type. + * + * @param jsRuntimeType the JS runtime type + * @return the instance + * @since 0.7.0 + */ public static V8Host getInstance(JSRuntimeType jsRuntimeType) { Objects.requireNonNull(jsRuntimeType); if (jsRuntimeType.isV8()) { @@ -86,6 +100,7 @@ public static V8Host getInstance(JSRuntimeType jsRuntimeType) { * Note: Node runtime library is loaded by a custom class loader. * * @return the Node instance + * @since 0.8.0 */ public static V8Host getNodeInstance() { return NodeInstanceHolder.INSTANCE; @@ -97,11 +112,17 @@ public static V8Host getNodeInstance() { * Note: V8 runtime library is loaded by a custom class loader. * * @return the V8 instance + * @since 0.8.0 */ public static V8Host getV8Instance() { return V8InstanceHolder.INSTANCE; } + /** + * Gets memory usage threshold ratio. + * + * @return the memory usage threshold ratio + */ public static double getMemoryUsageThresholdRatio() { return memoryUsageThresholdRatio; } @@ -136,6 +157,29 @@ private static void setMemoryUsageThreshold() { } } + /** + * Determines whether the JNI library is reloadable or not. + * + * @return true : reloadable, false: not reloadable, default: false + * @since 0.9.1 + */ + public static boolean isLibraryReloadable() { + return libraryReloadable; + } + + /** + * Sets whether the JNI library is reloadable or not. + * + * @param libraryReloadable true: reloadable, false: not reloadable + * @since 0.9.1 + */ + public static void setLibraryReloadable(boolean libraryReloadable) { + V8Host.libraryReloadable = libraryReloadable; + } + + /** + * Clear internal statistic for internal test purpose. + */ public void clearInternalStatistic() { v8Native.clearInternalStatistic(); } @@ -151,12 +195,22 @@ public void close() throws JavetException { disableGCNotification(); } + /** + * Disable GC notification. + * + * @return the self + */ @SuppressWarnings("UnusedReturnValue") public V8Host disableGCNotification() { v8Notifier.unregisterListener(); return this; } + /** + * Enable GC notification. + * + * @return the self + */ public V8Host enableGCNotification() { setMemoryUsageThreshold(); // Javet {@link V8Notifier} listens to this notification to notify {@link V8Runtime} to perform GC. @@ -164,36 +218,85 @@ public V8Host enableGCNotification() { return this; } + /** + * Gets flags. + * + * @return the flags + */ public V8Flags getFlags() { return flags; } + /** + * Get internal statistic internal for test purpose. + * + * @return the long [ ] + */ public long[] getInternalStatistic() { return v8Native.getInternalStatistic(); } + /** + * Gets javet version. + * + * @return the javet version + */ public String getJavetVersion() { return JavetLibLoader.LIB_VERSION; } + /** + * Gets logger. + * + * @return the logger + */ public IJavetLogger getLogger() { return logger; } + /** + * Gets V8 native. + * + * @return the V8 native + */ IV8Native getV8Native() { return v8Native; } + /** + * Create V8 runtime. + * + * @param the type parameter + * @return the V8 runtime + * @throws JavetException the javet exception + */ public R createV8Runtime() throws JavetException { return createV8Runtime(GLOBAL_THIS); } + /** + * Create V8 runtime. + * + * @param the type parameter + * @param globalName the global name + * @return the V8 runtime + * @throws JavetException the javet exception + */ public R createV8Runtime(String globalName) throws JavetException { return createV8Runtime(false, globalName); } + /** + * Create V8 runtime. + * + * @param the type parameter + * @param pooled the pooled + * @param globalName the global name + * @return the V8 runtime + * @throws JavetException the javet exception + */ public R createV8Runtime(boolean pooled, String globalName) throws JavetException { - if (!libLoaded) { + if (!libraryLoaded) { if (lastException == null) { throw new JavetException( JavetError.LibraryNotLoaded, @@ -216,8 +319,13 @@ public R createV8Runtime(boolean pooled, String globalName return (R) v8Runtime; } + /** + * Close V8 runtime. + * + * @param v8Runtime the V8 runtime + */ public void closeV8Runtime(V8Runtime v8Runtime) { - if (!libLoaded) { + if (!libraryLoaded) { return; } if (v8Runtime != null) { @@ -229,18 +337,30 @@ public void closeV8Runtime(V8Runtime v8Runtime) { } } + /** + * Gets JS runtime type. + * + * @return the JS runtime type + */ public JSRuntimeType getJSRuntimeType() { return jsRuntimeType; } - // This function is not open because it may cause core dump. - private void loadLibrary() { - if (!libLoaded) { + /** + * Load library. + *

+ * Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. + * + * @since 0.9.1 + */ + public synchronized void loadLibrary() { + if (!libraryLoaded) { try { javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), jsRuntimeType); javetClassLoader.load(); v8Native = javetClassLoader.getNative(); - libLoaded = true; + libraryLoaded = true; + isolateCreated = false; } catch (JavetException e) { logger.logError(e, "Failed to load Javet lib with error {0}.", e.getMessage()); lastException = e; @@ -248,37 +368,70 @@ private void loadLibrary() { } } - // This function is not open because it may cause core dump. - private void unloadLibrary() { - if (libLoaded) { + /** + * Unload library. + *

+ * Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. + * + * @since 0.9.1 + */ + public synchronized void unloadLibrary() { + if (libraryLoaded) { + isolateCreated = false; v8Native = null; javetClassLoader = null; System.gc(); System.runFinalization(); - libLoaded = false; + libraryLoaded = false; lastException = null; } } + /** + * Gets last exception. + * + * @return the last exception + */ public JavetException getLastException() { return lastException; } + /** + * Gets V8 runtime count. + * + * @return the V8 runtime count + */ public int getV8RuntimeCount() { return v8RuntimeMap.size(); } - public boolean isLibLoaded() { - return libLoaded; + /** + * Is library loaded. + * + * @return true: loaded, false: not loaded + */ + public boolean isLibraryLoaded() { + return libraryLoaded; } + /** + * Is isolate created. + * + * @return true: created, false: not created + */ public boolean isIsolateCreated() { return isolateCreated; } + /** + * Sets flags. + * + * @return true: flags are set, false: flags are not set + * @since 0.7.0 + */ @SuppressWarnings("UnusedReturnValue") public boolean setFlags() { - if (libLoaded && !isolateCreated) { + if (libraryLoaded && !isolateCreated) { List flags = new ArrayList<>(); if (this.flags.isAllowNativesSyntax()) { flags.add(FLAG_ALLOW_NATIVES_SYNTAX); diff --git a/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java index d70ba0f27..da9783a9d 100644 --- a/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java +++ b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java @@ -18,20 +18,69 @@ package com.caoccao.javet.interop; import com.caoccao.javet.enums.JSRuntimeType; +import com.caoccao.javet.exceptions.JavetError; import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + public class TestJavetLibLoader { + @AfterAll + public static void afterAll() { + V8Host.setLibraryReloadable(false); + } + + @BeforeAll + public static void beforeAll() { + V8Host.setLibraryReloadable(true); + } + @Test - public void testLoadAndUnload() { + public void testCustomClassLoader() { + testCustomClassLoader(JSRuntimeType.Node); + testCustomClassLoader(JSRuntimeType.V8); + } + + protected void testCustomClassLoader(JSRuntimeType jsRuntimeType) { try { - JavetClassLoader javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), JSRuntimeType.Node); - javetClassLoader.load(); - javetClassLoader = null; - System.gc(); - System.runFinalization(); + for (int i = 0; i < 2; ++i) { + JavetClassLoader javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), jsRuntimeType); + javetClassLoader.load(); + javetClassLoader.getNative(); + javetClassLoader = null; + System.gc(); + System.runFinalization(); + } } catch (JavetException e) { // This is expected. } } + + @Test + public void testLoadAndUnload() throws JavetException { + testLoadAndUnload(JSRuntimeType.Node); + testLoadAndUnload(JSRuntimeType.V8); + } + + protected void testLoadAndUnload(JSRuntimeType jsRuntimeType) throws JavetException { + V8Host v8Host = V8Host.getInstance(jsRuntimeType); + assertTrue(v8Host.isLibraryLoaded()); + try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + } + v8Host.unloadLibrary(); + assertFalse(v8Host.isLibraryLoaded()); + try { + v8Host.createV8Runtime(); + } catch (JavetException e) { + assertEquals(JavetError.LibraryNotLoaded, e.getError()); + } + v8Host.loadLibrary(); + assertTrue(v8Host.isLibraryLoaded()); + try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + } + } } From 6ee08a8100c526379ba3065e8217e5a5f49568df Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Wed, 9 Jun 2021 14:16:57 +0800 Subject: [PATCH 06/12] Updated doc for unload --- docs/reference/load_and_unload.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/reference/load_and_unload.rst b/docs/reference/load_and_unload.rst index 5f5fed88f..93b0da454 100644 --- a/docs/reference/load_and_unload.rst +++ b/docs/reference/load_and_unload.rst @@ -23,6 +23,8 @@ Assuming the JNI library per mode is already loaded, here are the step-by-step o // Step 4: Restore the switch. V8Host.setLibraryReloadable(false); +How does ``unloadLibrary()`` work? There is no API that allows unloading a JNI library explicitly. The only way is GC will automatically unload the library if all references to that library are garbage collectable. So, application is supposed to close all V8 values, V8 runtimes prior to calling ``unloadLibrary()``. + Load ---- @@ -38,6 +40,7 @@ Assuming the JNI library per mode is already unloaded, here are the step-by-step Notes ===== +* ``unloadLibrary()`` can only take effect after all references are garbage collectable. * ``loadLibrary()`` is internally called by Javet in the first time and only takes effect after ``unloadLibrary()`` is called. * ``loadLibrary()`` and ``unloadLibrary()`` are for experiment only. **They may be unstable and crash JVM. Please use this feature at your own risk.** From 016b99b8e0593265a99ee10e56bca4ce90a65a28 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Thu, 10 Jun 2021 11:38:14 +0800 Subject: [PATCH 07/12] Updated loadLibrary() and unloadLibrary() to return boolean --- .../com/caoccao/javet/interop/V8Host.java | 30 ++++++++++++++++--- .../javet/interop/TestJavetLibLoader.java | 4 +-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/caoccao/javet/interop/V8Host.java b/src/main/java/com/caoccao/javet/interop/V8Host.java index b65747fe6..712fe65b3 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Host.java +++ b/src/main/java/com/caoccao/javet/interop/V8Host.java @@ -122,6 +122,7 @@ public static V8Host getV8Instance() { * Gets memory usage threshold ratio. * * @return the memory usage threshold ratio + * @since 0.8.3 */ public static double getMemoryUsageThresholdRatio() { return memoryUsageThresholdRatio; @@ -134,6 +135,7 @@ public static double getMemoryUsageThresholdRatio() { * the increasing trend of memory usage with low overhead. * * @param memoryUsageThresholdRatio the memory usage threshold ratio + * @since 0.8.3 */ public static void setMemoryUsageThresholdRatio(double memoryUsageThresholdRatio) { assert 0 <= memoryUsageThresholdRatio && memoryUsageThresholdRatio < 1; @@ -160,7 +162,7 @@ private static void setMemoryUsageThreshold() { /** * Determines whether the JNI library is reloadable or not. * - * @return true : reloadable, false: not reloadable, default: false + * @return true: reloadable, false: not reloadable, default: false * @since 0.9.1 */ public static boolean isLibraryReloadable() { @@ -199,6 +201,7 @@ public void close() throws JavetException { * Disable GC notification. * * @return the self + * @since 0.8.3 */ @SuppressWarnings("UnusedReturnValue") public V8Host disableGCNotification() { @@ -210,6 +213,7 @@ public V8Host disableGCNotification() { * Enable GC notification. * * @return the self + * @since 0.8.3 */ public V8Host enableGCNotification() { setMemoryUsageThreshold(); @@ -222,6 +226,7 @@ public V8Host enableGCNotification() { * Gets flags. * * @return the flags + * @since 0.7.0 */ public V8Flags getFlags() { return flags; @@ -231,6 +236,7 @@ public V8Flags getFlags() { * Get internal statistic internal for test purpose. * * @return the long [ ] + * @since 0.8.3 */ public long[] getInternalStatistic() { return v8Native.getInternalStatistic(); @@ -240,6 +246,7 @@ public long[] getInternalStatistic() { * Gets javet version. * * @return the javet version + * @since 0.7.1 */ public String getJavetVersion() { return JavetLibLoader.LIB_VERSION; @@ -249,6 +256,7 @@ public String getJavetVersion() { * Gets logger. * * @return the logger + * @since 0.7.3 */ public IJavetLogger getLogger() { return logger; @@ -258,6 +266,7 @@ public IJavetLogger getLogger() { * Gets V8 native. * * @return the V8 native + * @since 0.8.0 */ IV8Native getV8Native() { return v8Native; @@ -269,6 +278,7 @@ IV8Native getV8Native() { * @param the type parameter * @return the V8 runtime * @throws JavetException the javet exception + * @since 0.7.0 */ public R createV8Runtime() throws JavetException { return createV8Runtime(GLOBAL_THIS); @@ -281,6 +291,7 @@ public R createV8Runtime() throws JavetException { * @param globalName the global name * @return the V8 runtime * @throws JavetException the javet exception + * @since 0.7.0 */ public R createV8Runtime(String globalName) throws JavetException { return createV8Runtime(false, globalName); @@ -294,6 +305,7 @@ public R createV8Runtime(String globalName) throws JavetEx * @param globalName the global name * @return the V8 runtime * @throws JavetException the javet exception + * @since 0.7.0 */ public R createV8Runtime(boolean pooled, String globalName) throws JavetException { if (!libraryLoaded) { @@ -323,6 +335,7 @@ public R createV8Runtime(boolean pooled, String globalName * Close V8 runtime. * * @param v8Runtime the V8 runtime + * @since 0.7.0 */ public void closeV8Runtime(V8Runtime v8Runtime) { if (!libraryLoaded) { @@ -341,6 +354,7 @@ public void closeV8Runtime(V8Runtime v8Runtime) { * Gets JS runtime type. * * @return the JS runtime type + * @since 0.8.0 */ public JSRuntimeType getJSRuntimeType() { return jsRuntimeType; @@ -351,9 +365,10 @@ public JSRuntimeType getJSRuntimeType() { *

* Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. * + * @return true: library is loaded, false: library is not loaded * @since 0.9.1 */ - public synchronized void loadLibrary() { + public synchronized boolean loadLibrary() { if (!libraryLoaded) { try { javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), jsRuntimeType); @@ -366,6 +381,7 @@ public synchronized void loadLibrary() { lastException = e; } } + return libraryLoaded; } /** @@ -373,10 +389,11 @@ public synchronized void loadLibrary() { *

* Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. * + * @return true: library is unloaded, false: library is loaded * @since 0.9.1 */ - public synchronized void unloadLibrary() { - if (libraryLoaded) { + public synchronized boolean unloadLibrary() { + if (libraryLoaded && v8RuntimeMap.isEmpty()) { isolateCreated = false; v8Native = null; javetClassLoader = null; @@ -385,12 +402,14 @@ public synchronized void unloadLibrary() { libraryLoaded = false; lastException = null; } + return !libraryLoaded; } /** * Gets last exception. * * @return the last exception + * @since 0.7.0 */ public JavetException getLastException() { return lastException; @@ -400,6 +419,7 @@ public JavetException getLastException() { * Gets V8 runtime count. * * @return the V8 runtime count + * @since 0.8.0 */ public int getV8RuntimeCount() { return v8RuntimeMap.size(); @@ -409,6 +429,7 @@ public int getV8RuntimeCount() { * Is library loaded. * * @return true: loaded, false: not loaded + * @since 0.8.0 */ public boolean isLibraryLoaded() { return libraryLoaded; @@ -418,6 +439,7 @@ public boolean isLibraryLoaded() { * Is isolate created. * * @return true: created, false: not created + * @since 0.8.0 */ public boolean isIsolateCreated() { return isolateCreated; diff --git a/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java index da9783a9d..ebed6f98b 100644 --- a/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java +++ b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java @@ -70,14 +70,14 @@ protected void testLoadAndUnload(JSRuntimeType jsRuntimeType) throws JavetExcept try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); } - v8Host.unloadLibrary(); + assertTrue(v8Host.unloadLibrary()); assertFalse(v8Host.isLibraryLoaded()); try { v8Host.createV8Runtime(); } catch (JavetException e) { assertEquals(JavetError.LibraryNotLoaded, e.getError()); } - v8Host.loadLibrary(); + assertTrue(v8Host.loadLibrary()); assertTrue(v8Host.isLibraryLoaded()); try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); From 4d39d5662bbf5849519b277caca2edc7c773af5d Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Fri, 11 Jun 2021 09:54:10 +0800 Subject: [PATCH 08/12] Added JavaDoc for annotations and interfaces --- .../javet/annotations/CheckReturnValue.java | 6 + .../caoccao/javet/annotations/NodeModule.java | 11 ++ .../caoccao/javet/annotations/V8Function.java | 17 ++ .../caoccao/javet/annotations/V8Property.java | 17 ++ .../javet/annotations/V8RuntimeSetter.java | 5 + .../interception/BaseJavetInterceptor.java | 4 +- .../logging/BaseJavetConsoleInterceptor.java | 161 ++++++++++++++++-- .../JavetStandardConsoleInterceptor.java | 113 ++++++++++++ .../javet/interfaces/IJavetBiConsumer.java | 17 ++ .../interfaces/IJavetBiIndexedConsumer.java | 18 ++ .../javet/interfaces/IJavetClosable.java | 5 + .../javet/interfaces/IJavetInterceptor.java | 25 ++- .../javet/interfaces/IJavetLogger.java | 71 ++++++++ .../IJavetPromiseRejectCallback.java | 3 + .../javet/interfaces/IJavetResettable.java | 11 ++ .../javet/interfaces/IJavetUniConsumer.java | 15 ++ .../interfaces/IJavetUniIndexedConsumer.java | 16 ++ 17 files changed, 494 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/caoccao/javet/annotations/CheckReturnValue.java b/src/main/java/com/caoccao/javet/annotations/CheckReturnValue.java index 17ef7682e..705266353 100644 --- a/src/main/java/com/caoccao/javet/annotations/CheckReturnValue.java +++ b/src/main/java/com/caoccao/javet/annotations/CheckReturnValue.java @@ -2,6 +2,12 @@ import java.lang.annotation.*; +/** + * This annotation is for IDE to warn application developers to consume the return value. + * Memory leak may occur if the return value is not consumed. + * + * @since 0.8.10 + */ @Documented @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.PACKAGE}) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/com/caoccao/javet/annotations/NodeModule.java b/src/main/java/com/caoccao/javet/annotations/NodeModule.java index 66428fe0d..79fd9b5aa 100644 --- a/src/main/java/com/caoccao/javet/annotations/NodeModule.java +++ b/src/main/java/com/caoccao/javet/annotations/NodeModule.java @@ -19,9 +19,20 @@ import java.lang.annotation.*; +/** + * The annotation Node module. + * + * @since 0.8.1 + */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface NodeModule { + /** + * Name string. + * + * @return the string + * @since 0.8.1 + */ String name() default ""; } diff --git a/src/main/java/com/caoccao/javet/annotations/V8Function.java b/src/main/java/com/caoccao/javet/annotations/V8Function.java index 310c014c3..97de21794 100644 --- a/src/main/java/com/caoccao/javet/annotations/V8Function.java +++ b/src/main/java/com/caoccao/javet/annotations/V8Function.java @@ -19,11 +19,28 @@ import java.lang.annotation.*; +/** + * The annotation V8 function is for auto-registering JS function interception. + * + * @since 0.8.1 + */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface V8Function { + /** + * JS function name. + * + * @return the name of the JS function to be injected + * @since 0.8.1 + */ String name() default ""; + /** + * This object required. + * + * @return true: this object is required, false: this object is ignored + * @since 0.9.0 + */ boolean thisObjectRequired() default false; } diff --git a/src/main/java/com/caoccao/javet/annotations/V8Property.java b/src/main/java/com/caoccao/javet/annotations/V8Property.java index ab00a1efe..298c4f521 100644 --- a/src/main/java/com/caoccao/javet/annotations/V8Property.java +++ b/src/main/java/com/caoccao/javet/annotations/V8Property.java @@ -19,11 +19,28 @@ import java.lang.annotation.*; +/** + * The interface V8 property is for auto-registering JS property interception. + * + * @since 0.9.0 + */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface V8Property { + /** + * JS property name. + * + * @return the name of the JS property to be injected + * @since 0.9.0 + */ String name() default ""; + /** + * This object required. + * + * @return true: this object is required, false: this object is ignored + * @since 0.9.0 + */ boolean thisObjectRequired() default false; } diff --git a/src/main/java/com/caoccao/javet/annotations/V8RuntimeSetter.java b/src/main/java/com/caoccao/javet/annotations/V8RuntimeSetter.java index 598f39f8a..51d4c86f3 100644 --- a/src/main/java/com/caoccao/javet/annotations/V8RuntimeSetter.java +++ b/src/main/java/com/caoccao/javet/annotations/V8RuntimeSetter.java @@ -19,6 +19,11 @@ import java.lang.annotation.*; +/** + * The interface V8 runtime setter. + * + * @since 0.8.1 + */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java b/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java index 6544b9de2..3ead8f1c1 100644 --- a/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java +++ b/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java @@ -30,10 +30,10 @@ public BaseJavetInterceptor(V8Runtime v8Runtime) { } @Override - public abstract boolean register(IV8ValueObject iV8ValueObject) throws JavetException; + public abstract boolean register(IV8ValueObject... iV8ValueObjects) throws JavetException; @Override - public abstract boolean unregister(IV8ValueObject iV8ValueObject) throws JavetException; + public abstract boolean unregister(IV8ValueObject... iV8ValueObjects) throws JavetException; public V8Runtime getV8Runtime() { return v8Runtime; diff --git a/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java b/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java index 385e5a311..c48c16cbf 100644 --- a/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java +++ b/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java @@ -28,56 +28,179 @@ import com.caoccao.javet.values.reference.IV8ValueObject; import com.caoccao.javet.values.reference.V8ValueObject; +/** + * The type Base javet console interceptor. + * + * @since 0.7.0 + */ public abstract class BaseJavetConsoleInterceptor extends BaseJavetInterceptor { + /** + * The constant SPACE. + * + * @since 0.7.1 + */ protected static final String SPACE = " "; + /** + * The constant JS_FUNCTION_DEBUG. + * + * @since 0.7.0 + */ protected static final String JS_FUNCTION_DEBUG = "debug"; + /** + * The constant JS_FUNCTION_ERROR. + * @since 0.7.0 + */ protected static final String JS_FUNCTION_ERROR = "error"; + /** + * The constant JS_FUNCTION_INFO. + * @since 0.7.0 + */ protected static final String JS_FUNCTION_INFO = "info"; + /** + * The constant JS_FUNCTION_LOG. + * @since 0.7.0 + */ protected static final String JS_FUNCTION_LOG = "log"; + /** + * The constant JS_FUNCTION_TRACE. + * @since 0.7.0 + */ protected static final String JS_FUNCTION_TRACE = "trace"; + /** + * The constant JS_FUNCTION_WARN. + * @since 0.7.0 + */ protected static final String JS_FUNCTION_WARN = "warn"; + /** + * The constant JAVA_CONSOLE_DEBUG. + * @since 0.7.0 + */ protected static final String JAVA_CONSOLE_DEBUG = "consoleDebug"; + /** + * The constant JAVA_CONSOLE_ERROR. + * @since 0.7.0 + */ protected static final String JAVA_CONSOLE_ERROR = "consoleError"; + /** + * The constant JAVA_CONSOLE_INFO. + * @since 0.7.0 + */ protected static final String JAVA_CONSOLE_INFO = "consoleInfo"; + /** + * The constant JAVA_CONSOLE_LOG. + * @since 0.7.0 + */ protected static final String JAVA_CONSOLE_LOG = "consoleLog"; + /** + * The constant JAVA_CONSOLE_TRACE. + * @since 0.7.0 + */ protected static final String JAVA_CONSOLE_TRACE = "consoleTrace"; + /** + * The constant JAVA_CONSOLE_WARN. + * @since 0.7.0 + */ protected static final String JAVA_CONSOLE_WARN = "consoleWarn"; + /** + * The constant PROPERTY_CONSOLE. + * @since 0.7.0 + */ protected static final String PROPERTY_CONSOLE = "console"; + /** + * Instantiates a new Base javet console interceptor. + * + * @param v8Runtime the V8 runtime + * @since 0.7.0 + */ public BaseJavetConsoleInterceptor(V8Runtime v8Runtime) { super(v8Runtime); } + /** + * Concat string. + * + * @param v8Values the V8 values + * @return the string + * @since 0.7.0 + */ public String concat(V8Value... v8Values) { return V8ValueUtils.concat(SPACE, v8Values); } + /** + * Console debug. + * + * @param v8Values the V8 values + * @since 0.7.0 + */ public abstract void consoleDebug(V8Value... v8Values); + /** + * Console error. + * + * @param v8Values the V8 values + * @since 0.7.0 + */ public abstract void consoleError(V8Value... v8Values); + /** + * Console info. + * + * @param v8Values the V8 values + * @since 0.7.0 + */ public abstract void consoleInfo(V8Value... v8Values); + /** + * Console log. + * + * @param v8Values the V8 values + * @since 0.7.0 + */ public abstract void consoleLog(V8Value... v8Values); + /** + * Console trace. + * + * @param v8Values the V8 values + * @since 0.7.0 + */ public abstract void consoleTrace(V8Value... v8Values); + /** + * Console warn. + * + * @param v8Values the V8 values + * @since 0.7.0 + */ public abstract void consoleWarn(V8Value... v8Values); @Override - public boolean register(IV8ValueObject iV8ValueObject) throws JavetException { + public boolean register(IV8ValueObject... iV8ValueObjects) throws JavetException { try (V8ValueObject console = v8Runtime.createV8ValueObject()) { - iV8ValueObject.set(PROPERTY_CONSOLE, console); - register(console, JS_FUNCTION_DEBUG, JAVA_CONSOLE_DEBUG); - register(console, JS_FUNCTION_ERROR, JAVA_CONSOLE_ERROR); - register(console, JS_FUNCTION_INFO, JAVA_CONSOLE_INFO); - register(console, JS_FUNCTION_LOG, JAVA_CONSOLE_LOG); - register(console, JS_FUNCTION_TRACE, JAVA_CONSOLE_TRACE); - register(console, JS_FUNCTION_WARN, JAVA_CONSOLE_WARN); + for (IV8ValueObject iV8ValueObject : iV8ValueObjects) { + iV8ValueObject.set(PROPERTY_CONSOLE, console); + register(console, JS_FUNCTION_DEBUG, JAVA_CONSOLE_DEBUG); + register(console, JS_FUNCTION_ERROR, JAVA_CONSOLE_ERROR); + register(console, JS_FUNCTION_INFO, JAVA_CONSOLE_INFO); + register(console, JS_FUNCTION_LOG, JAVA_CONSOLE_LOG); + register(console, JS_FUNCTION_TRACE, JAVA_CONSOLE_TRACE); + register(console, JS_FUNCTION_WARN, JAVA_CONSOLE_WARN); + } return true; } } + /** + * Register. + * + * @param iV8ValueObject the V8 value object + * @param jsFunctionName the js function name + * @param javaFunctionName the java function name + * @throws JavetException the javet exception + * @since 0.7.0 + */ protected void register(IV8ValueObject iV8ValueObject, String jsFunctionName, String javaFunctionName) throws JavetException { try { @@ -96,15 +219,19 @@ protected void register(IV8ValueObject iV8ValueObject, String jsFunctionName, St } @Override - public boolean unregister(IV8ValueObject iV8ValueObject) throws JavetException { - try (V8ValueObject console = iV8ValueObject.get(PROPERTY_CONSOLE)) { - console.delete(JS_FUNCTION_DEBUG); - console.delete(JS_FUNCTION_ERROR); - console.delete(JS_FUNCTION_INFO); - console.delete(JS_FUNCTION_LOG); - console.delete(JS_FUNCTION_TRACE); - console.delete(JS_FUNCTION_WARN); + public boolean unregister(IV8ValueObject... iV8ValueObjects) throws JavetException { + boolean successful = true; + for (IV8ValueObject iV8ValueObject : iV8ValueObjects) { + try (V8ValueObject console = iV8ValueObject.get(PROPERTY_CONSOLE)) { + console.delete(JS_FUNCTION_DEBUG); + console.delete(JS_FUNCTION_ERROR); + console.delete(JS_FUNCTION_INFO); + console.delete(JS_FUNCTION_LOG); + console.delete(JS_FUNCTION_TRACE); + console.delete(JS_FUNCTION_WARN); + } + successful &= iV8ValueObject.delete(PROPERTY_CONSOLE); } - return iV8ValueObject.delete(PROPERTY_CONSOLE); + return successful; } } diff --git a/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java b/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java index db58c72d1..f0d6021cf 100644 --- a/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java +++ b/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java @@ -22,64 +22,177 @@ import java.io.PrintStream; +/** + * The type Javet standard console interceptor. + * + * @since 0.7.0 + */ public class JavetStandardConsoleInterceptor extends BaseJavetConsoleInterceptor { + /** + * The Debug. + * + * @since 0.7.0 + */ protected PrintStream debug; + /** + * The Error. + * + * @since 0.7.0 + */ protected PrintStream error; + /** + * The Info. + * + * @since 0.7.0 + */ protected PrintStream info; + /** + * The Log. + * + * @since 0.7.0 + */ protected PrintStream log; + /** + * The Trace. + * + * @since 0.7.0 + */ protected PrintStream trace; + /** + * The Warn. + * + * @since 0.7.0 + */ protected PrintStream warn; + /** + * Instantiates a new Javet standard console interceptor. + * + * @param v8Runtime the V8 runtime + * @since 0.7.0 + */ public JavetStandardConsoleInterceptor(V8Runtime v8Runtime) { super(v8Runtime); debug = info = log = trace = warn = System.out; error = System.err; } + /** + * Gets debug. + * + * @return the debug + * @since 0.7.0 + */ public PrintStream getDebug() { return debug; } + /** + * Sets debug. + * + * @param debug the debug + * @since 0.7.0 + */ public void setDebug(PrintStream debug) { this.debug = debug; } + /** + * Gets error. + * + * @return the error + * @since 0.7.0 + */ public PrintStream getError() { return error; } + /** + * Sets error. + * + * @param error the error + * @since 0.7.0 + */ public void setError(PrintStream error) { this.error = error; } + /** + * Gets info. + * + * @return the info + * @since 0.7.0 + */ public PrintStream getInfo() { return info; } + /** + * Sets info. + * + * @param info the info + * @since 0.7.0 + */ public void setInfo(PrintStream info) { this.info = info; } + /** + * Gets log. + * + * @return the log + * @since 0.7.0 + */ public PrintStream getLog() { return log; } + /** + * Sets log. + * + * @param log the log + * @since 0.7.0 + */ public void setLog(PrintStream log) { this.log = log; } + /** + * Gets trace. + * + * @return the trace + * @since 0.7.0 + */ public PrintStream getTrace() { return trace; } + /** + * Sets trace. + * + * @param trace the trace + * @since 0.7.0 + */ public void setTrace(PrintStream trace) { this.trace = trace; } + /** + * Gets warn. + * + * @return the warn + * @since 0.7.0 + */ public PrintStream getWarn() { return warn; } + /** + * Sets warn. + * + * @param warn the warn + * @since 0.7.0 + */ public void setWarn(PrintStream warn) { this.warn = warn; } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetBiConsumer.java b/src/main/java/com/caoccao/javet/interfaces/IJavetBiConsumer.java index 0c36a85c3..83c9ac064 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetBiConsumer.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetBiConsumer.java @@ -20,6 +20,23 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.V8Value; +/** + * The interface Javet bi-consumer. + * + * @param the type parameter + * @param the type parameter + * @param the type parameter + * @since 0.8.9 + */ public interface IJavetBiConsumer { + /** + * Accept. + * + * @param value1 the value 1 + * @param value2 the value 2 + * @throws JavetException the javet exception + * @throws E the e + * @since 0.8.9 + */ void accept(T1 value1, T2 value2) throws JavetException, E; } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetBiIndexedConsumer.java b/src/main/java/com/caoccao/javet/interfaces/IJavetBiIndexedConsumer.java index 50da96547..4424bf8a9 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetBiIndexedConsumer.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetBiIndexedConsumer.java @@ -20,6 +20,24 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.V8Value; +/** + * The interface Javet bi-indexed consumer. + * + * @param the type parameter + * @param the type parameter + * @param the type parameter + * @since 0.8.10 + */ public interface IJavetBiIndexedConsumer { + /** + * Accept. + * + * @param index the index + * @param value1 the value 1 + * @param value2 the value 2 + * @throws JavetException the javet exception + * @throws E the e + * @since 0.8.10 + */ void accept(int index, T1 value1, T2 value2) throws JavetException, E; } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetClosable.java b/src/main/java/com/caoccao/javet/interfaces/IJavetClosable.java index 26c25082f..75457bb2b 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetClosable.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetClosable.java @@ -19,6 +19,11 @@ import com.caoccao.javet.exceptions.JavetException; +/** + * The interface Javet closable. + * + * @since 0.7.0 + */ public interface IJavetClosable extends AutoCloseable { void close() throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetInterceptor.java b/src/main/java/com/caoccao/javet/interfaces/IJavetInterceptor.java index 90a3ece54..fb182c203 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetInterceptor.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetInterceptor.java @@ -20,8 +20,29 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.reference.IV8ValueObject; +/** + * The interface Javet interceptor. + * + * @since 0.7.0 + */ public interface IJavetInterceptor { - boolean register(IV8ValueObject iV8ValueObject) throws JavetException; + /** + * Register boolean. + * + * @param iV8ValueObjects the V8 value objects + * @return the boolean + * @throws JavetException the javet exception + * @since 0.7.0 + */ + boolean register(IV8ValueObject... iV8ValueObjects) throws JavetException; - boolean unregister(IV8ValueObject iV8ValueObject) throws JavetException; + /** + * Unregister boolean. + * + * @param iV8ValueObjects the V8 value objects + * @return the boolean + * @throws JavetException the javet exception + * @since 0.7.0 + */ + boolean unregister(IV8ValueObject... iV8ValueObjects) throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetLogger.java b/src/main/java/com/caoccao/javet/interfaces/IJavetLogger.java index c5061ef40..37b88e629 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetLogger.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetLogger.java @@ -19,34 +19,105 @@ import java.text.MessageFormat; +/** + * The interface Javet logger. + * @since 0.7.0 + */ public interface IJavetLogger { + /** + * Debug. + * + * @param message the message + * @since 0.7.0 + */ void debug(String message); + /** + * Error. + * + * @param message the message + * @since 0.7.0 + */ void error(String message); + /** + * Error. + * + * @param message the message + * @param cause the cause + * @since 0.7.0 + */ void error(String message, Throwable cause); + /** + * Info. + * + * @param message the message + * @since 0.7.0 + */ void info(String message); + /** + * Log debug. + * + * @param format the format + * @param objects the objects + * @since 0.7.0 + */ default void logDebug(String format, Object... objects) { debug(MessageFormat.format(format, objects)); } + /** + * Log error. + * + * @param cause the cause + * @param format the format + * @param objects the objects + * @since 0.7.0 + */ default void logError(Throwable cause, String format, Object... objects) { error(MessageFormat.format(format, objects), cause); } + /** + * Log error. + * + * @param format the format + * @param objects the objects + * @since 0.7.0 + */ default void logError(String format, Object... objects) { error(MessageFormat.format(format, objects)); } + /** + * Log info. + * + * @param format the format + * @param objects the objects + * @since 0.7.0 + */ default void logInfo(String format, Object... objects) { info(MessageFormat.format(format, objects)); } + /** + * Log warn. + * + * @param format the format + * @param objects the objects + * @since 0.7.0 + */ default void logWarn(String format, Object... objects) { warn(MessageFormat.format(format, objects)); } + /** + * Warn. + * + * @param message the message + * @since 0.7.0 + */ void warn(String message); } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetPromiseRejectCallback.java b/src/main/java/com/caoccao/javet/interfaces/IJavetPromiseRejectCallback.java index 20fa4e080..598fdea89 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetPromiseRejectCallback.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetPromiseRejectCallback.java @@ -23,6 +23,8 @@ /** * The interface Javet promise reject callback. + * + * @since 0.8.3 */ public interface IJavetPromiseRejectCallback { @@ -32,6 +34,7 @@ public interface IJavetPromiseRejectCallback { * @param event the event * @param promise the promise * @param value the value + * @since 0.8.3 */ void callback(JavetPromiseRejectEvent event, V8ValuePromise promise, V8Value value); } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetResettable.java b/src/main/java/com/caoccao/javet/interfaces/IJavetResettable.java index 2e2489b56..a18867625 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetResettable.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetResettable.java @@ -19,6 +19,17 @@ import com.caoccao.javet.exceptions.JavetException; +/** + * The interface Javet resettable. + * + * @since 0.7.0 + */ public interface IJavetResettable { + /** + * Reset. + * + * @throws JavetException the javet exception + * @since 0.7.0 + */ void reset() throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetUniConsumer.java b/src/main/java/com/caoccao/javet/interfaces/IJavetUniConsumer.java index f686366b9..5b0d5bd5f 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetUniConsumer.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetUniConsumer.java @@ -20,6 +20,21 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.V8Value; +/** + * The interface Javet uni consumer. + * + * @param the type parameter + * @param the type parameter + * @since 0.8.10 + */ public interface IJavetUniConsumer { + /** + * Accept. + * + * @param value the value + * @throws JavetException the javet exception + * @throws E the e + * @since 0.8.10 + */ void accept(T value) throws JavetException, E; } diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetUniIndexedConsumer.java b/src/main/java/com/caoccao/javet/interfaces/IJavetUniIndexedConsumer.java index 39363ea6c..ecc854d7d 100644 --- a/src/main/java/com/caoccao/javet/interfaces/IJavetUniIndexedConsumer.java +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetUniIndexedConsumer.java @@ -20,6 +20,22 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.V8Value; +/** + * The interface Javet uni indexed consumer. + * + * @param the type parameter + * @param the type parameter + * @since 0.8.10 + */ public interface IJavetUniIndexedConsumer { + /** + * Accept. + * + * @param index the index + * @param value the value + * @throws JavetException the javet exception + * @throws E the e + * @since 0.8.10 + */ void accept(int index, T value) throws JavetException, E; } From dea73b0fac8e16bc5200e762bf7bbe71529c8d02 Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Fri, 11 Jun 2021 14:36:06 +0800 Subject: [PATCH 09/12] Arranged code --- cpp/jni/com_caoccao_javet_interop_V8Native.h | 16 +- .../javet/entities/JavetEntityMap.java | 30 ++ .../caoccao/javet/enums/JavetErrorType.java | 45 +++ .../javet/exceptions/JavetScriptingError.java | 70 ++-- .../interception/BaseJavetInterceptor.java | 8 +- .../logging/BaseJavetConsoleInterceptor.java | 72 ++-- .../JavetStandardConsoleInterceptor.java | 140 ++++---- .../com/caoccao/javet/interop/IV8Native.java | 4 +- .../javet/interop/JavetClassLoader.java | 2 +- .../caoccao/javet/interop/JavetLibLoader.java | 24 +- .../caoccao/javet/interop/NodeRuntime.java | 12 +- .../com/caoccao/javet/interop/V8Flags.java | 48 +-- .../com/caoccao/javet/interop/V8Host.java | 318 +++++++++--------- .../caoccao/javet/interop/V8Inspector.java | 14 +- .../com/caoccao/javet/interop/V8Native.java | 4 +- .../com/caoccao/javet/interop/V8Runtime.java | 148 ++++---- .../caoccao/javet/interop/V8ScriptOrigin.java | 56 +-- .../javet/interop/engine/IJavetEngine.java | 4 +- .../javet/interop/engine/JavetEngine.java | 36 +- .../interop/engine/JavetEngineConfig.java | 188 +++++------ .../interop/engine/JavetEngineGuard.java | 14 +- .../javet/interop/engine/JavetEnginePool.java | 16 +- .../interop/engine/JavetEngineUsage.java | 16 +- .../javet/interop/executors/IV8Executor.java | 30 +- .../interop/executors/V8PathExecutor.java | 8 +- .../interop/executors/V8StringExecutor.java | 10 +- .../javet/node/modules/NodeModuleProcess.java | 8 +- .../javet/utils/JavetCallbackContext.java | 22 +- .../javet/utils/JavetDateTimeUtils.java | 26 +- .../javet/utils/JavetDefaultLogger.java | 16 +- .../com/caoccao/javet/utils/JavetOSUtils.java | 3 +- .../javet/utils/SimpleFreeMarkerFormat.java | 2 +- .../receivers/JavetCallbackReceiver.java | 10 +- .../com/caoccao/javet/values/IV8Value.java | 14 +- .../com/caoccao/javet/values/V8Value.java | 12 +- .../javet/values/reference/IV8Module.java | 4 +- .../values/reference/IV8ValueObject.java | 24 +- .../javet/values/reference/V8Module.java | 10 +- .../javet/values/reference/V8Script.java | 12 +- .../javet/values/reference/V8ValueArray.java | 2 +- .../values/reference/V8ValueArrayBuffer.java | 49 ++- .../values/reference/V8ValueDataView.java | 44 +-- .../javet/values/reference/V8ValueError.java | 14 +- .../values/reference/V8ValueFunction.java | 28 +- .../values/reference/V8ValueIterator.java | 12 +- .../javet/values/reference/V8ValueMap.java | 2 +- .../javet/values/reference/V8ValueObject.java | 26 +- .../values/reference/V8ValuePromise.java | 4 +- .../values/reference/V8ValueReference.java | 6 +- .../javet/values/reference/V8ValueSet.java | 10 +- .../javet/values/reference/V8ValueSymbol.java | 10 +- .../values/reference/V8ValueTypedArray.java | 164 ++++----- .../builtin/V8ValueBuiltInPromise.java | 24 +- .../values/virtual/V8VirtualValueList.java | 1 - .../java/com/caoccao/javet/BaseTestJavet.java | 2 +- .../com/caoccao/javet/BaseTestJavetPool.java | 14 +- .../caoccao/javet/BaseTestJavetRuntime.java | 2 +- .../javet/interop/TestNodeRuntime.java | 16 +- .../com/caoccao/javet/interop/TestV8Host.java | 8 +- .../javet/interop/TestV8Inspector.java | 1 + .../converters/TestJavetCustomConverter.java | 8 +- .../converters/TestJavetObjectConverter.java | 36 +- .../interop/engine/TestJavetEngineGuard.java | 30 +- .../interop/engine/TestJavetEnginePool.java | 44 +-- .../javet/interop/engine/TestPerformance.java | 82 ++--- .../MockAnnotationBasedCallbackReceiver.java | 94 +++--- .../javet/mock/MockCallbackReceiver.java | 54 +-- .../tutorial/DecimalJavetInNodeJSMode.java | 32 +- .../javet/tutorial/DecimalJavetInV8Mode.java | 30 +- .../caoccao/javet/tutorial/HelloJavet.java | 36 +- .../com/caoccao/javet/tutorial/TestES6.java | 1 - .../javet/tutorial/TestInterception.java | 28 +- .../caoccao/javet/tutorial/cdt/CDTConfig.java | 16 +- .../utils/TestSimpleFreeMarkerFormat.java | 26 +- .../values/primitive/TestV8ValueDouble.java | 1 - .../values/primitive/TestV8ValueInteger.java | 1 - .../values/primitive/TestV8ValueString.java | 1 - .../javet/values/reference/TestV8Module.java | 78 ++--- .../reference/TestV8ValueArguments.java | 2 +- .../values/reference/TestV8ValueArray.java | 50 +-- .../values/reference/TestV8ValueError.java | 2 +- .../values/reference/TestV8ValueFunction.java | 176 +++++----- .../values/reference/TestV8ValueObject.java | 160 ++++----- .../values/reference/TestV8ValueProxy.java | 2 +- .../values/reference/TestV8ValueRegExp.java | 1 - .../values/reference/TestV8ValueSymbol.java | 6 +- .../values/reference/TestV8ValueWeakMap.java | 2 +- .../values/reference/TestV8ValueWeakSet.java | 3 - 88 files changed, 1512 insertions(+), 1425 deletions(-) diff --git a/cpp/jni/com_caoccao_javet_interop_V8Native.h b/cpp/jni/com_caoccao_javet_interop_V8Native.h index bd4a8a9ae..e95b076f2 100644 --- a/cpp/jni/com_caoccao_javet_interop_V8Native.h +++ b/cpp/jni/com_caoccao_javet_interop_V8Native.h @@ -207,14 +207,6 @@ JNIEXPORT jint JNICALL Java_com_caoccao_javet_interop_V8Native_getLength JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getOwnPropertyNames (JNIEnv *, jobject, jlong, jlong, jint); -/* - * Class: com_caoccao_javet_interop_V8Native - * Method: getPropertyNames - * Signature: (JJI)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getPropertyNames - (JNIEnv *, jobject, jlong, jlong, jint); - /* * Class: com_caoccao_javet_interop_V8Native * Method: getProperty @@ -223,6 +215,14 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getPropertyNam JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getProperty (JNIEnv *, jobject, jlong, jlong, jint, jobject); +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getPropertyNames + * Signature: (JJI)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getPropertyNames + (JNIEnv *, jobject, jlong, jlong, jint); + /* * Class: com_caoccao_javet_interop_V8Native * Method: getSize diff --git a/src/main/java/com/caoccao/javet/entities/JavetEntityMap.java b/src/main/java/com/caoccao/javet/entities/JavetEntityMap.java index 5c20a3f17..7afe1c106 100644 --- a/src/main/java/com/caoccao/javet/entities/JavetEntityMap.java +++ b/src/main/java/com/caoccao/javet/entities/JavetEntityMap.java @@ -20,18 +20,48 @@ import java.util.HashMap; import java.util.Map; +/** + * The type Javet entity map. + * + * @since 0.7.2 + */ public class JavetEntityMap extends HashMap { + /** + * Instantiates a new Javet entity map. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * @since 0.7.2 + */ public JavetEntityMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); } + /** + * Instantiates a new Javet entity map. + * + * @param initialCapacity the initial capacity + * @since 0.7.2 + */ public JavetEntityMap(int initialCapacity) { super(initialCapacity); } + /** + * Instantiates a new Javet entity map. + * + * @since 0.7.2 + */ public JavetEntityMap() { + super(); } + /** + * Instantiates a new Javet entity map. + * + * @param m the m + * @since 0.7.2 + */ public JavetEntityMap(Map m) { super(m); } diff --git a/src/main/java/com/caoccao/javet/enums/JavetErrorType.java b/src/main/java/com/caoccao/javet/enums/JavetErrorType.java index eeef8f2ab..1bffc570e 100644 --- a/src/main/java/com/caoccao/javet/enums/JavetErrorType.java +++ b/src/main/java/com/caoccao/javet/enums/JavetErrorType.java @@ -17,13 +17,58 @@ package com.caoccao.javet.enums; +/** + * The enum Javet error type. + * + * @since 0.8.5 + */ public enum JavetErrorType { + /** + * System javet error type. + * + * @since 0.8.5 + */ System, + /** + * Compilation javet error type. + * + * @since 0.8.5 + */ Compilation, + /** + * Execution javet error type. + * + * @since 0.8.5 + */ Execution, + /** + * Callback javet error type. + * + * @since 0.8.5 + */ Callback, + /** + * Converter javet error type. + * + * @since 0.8.5 + */ Converter, + /** + * Module javet error type. + * + * @since 0.8.5 + */ Module, + /** + * Lock javet error type. + * + * @since 0.8.5 + */ Lock, + /** + * Runtime javet error type. + * + * @since 0.8.5 + */ Runtime, } diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetScriptingError.java b/src/main/java/com/caoccao/javet/exceptions/JavetScriptingError.java index bcacc9b5e..bc323aa30 100644 --- a/src/main/java/com/caoccao/javet/exceptions/JavetScriptingError.java +++ b/src/main/java/com/caoccao/javet/exceptions/JavetScriptingError.java @@ -20,14 +20,14 @@ import java.text.MessageFormat; public final class JavetScriptingError { + private int endColumn; + private int endPosition; + private int lineNumber; private String message; private String resourceName; private String sourceLine; - private int lineNumber; private int startColumn; - private int endColumn; private int startPosition; - private int endPosition; public JavetScriptingError( String message, String resourceName, String sourceLine, @@ -42,68 +42,68 @@ public JavetScriptingError( this.endPosition = endPosition; } - public String getMessage() { - return message; + public int getEndColumn() { + return endColumn; } - public void setMessage(String message) { - this.message = message; + public int getEndPosition() { + return endPosition; } - public String getResourceName() { - return resourceName; + public int getLineNumber() { + return lineNumber; } - public void setResourceName(String resourceName) { - this.resourceName = resourceName; + public String getMessage() { + return message; } - public String getSourceLine() { - return sourceLine; + public String getResourceName() { + return resourceName; } - public void setSourceLine(String sourceLine) { - this.sourceLine = sourceLine; + public String getSourceLine() { + return sourceLine; } - public int getLineNumber() { - return lineNumber; + public int getStartColumn() { + return startColumn; } - public void setLineNumber(int lineNumber) { - this.lineNumber = lineNumber; + public int getStartPosition() { + return startPosition; } - public int getStartColumn() { - return startColumn; + public void setEndColumn(int endColumn) { + this.endColumn = endColumn; } - public void setStartColumn(int startColumn) { - this.startColumn = startColumn; + public void setEndPosition(int endPosition) { + this.endPosition = endPosition; } - public int getEndColumn() { - return endColumn; + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; } - public void setEndColumn(int endColumn) { - this.endColumn = endColumn; + public void setMessage(String message) { + this.message = message; } - public int getStartPosition() { - return startPosition; + public void setResourceName(String resourceName) { + this.resourceName = resourceName; } - public void setStartPosition(int startPosition) { - this.startPosition = startPosition; + public void setSourceLine(String sourceLine) { + this.sourceLine = sourceLine; } - public int getEndPosition() { - return endPosition; + public void setStartColumn(int startColumn) { + this.startColumn = startColumn; } - public void setEndPosition(int endPosition) { - this.endPosition = endPosition; + public void setStartPosition(int startPosition) { + this.startPosition = startPosition; } @Override diff --git a/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java b/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java index 3ead8f1c1..09619ce8f 100644 --- a/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java +++ b/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java @@ -29,13 +29,13 @@ public BaseJavetInterceptor(V8Runtime v8Runtime) { this.v8Runtime = v8Runtime; } + public V8Runtime getV8Runtime() { + return v8Runtime; + } + @Override public abstract boolean register(IV8ValueObject... iV8ValueObjects) throws JavetException; @Override public abstract boolean unregister(IV8ValueObject... iV8ValueObjects) throws JavetException; - - public V8Runtime getV8Runtime() { - return v8Runtime; - } } diff --git a/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java b/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java index c48c16cbf..820284d9e 100644 --- a/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java +++ b/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java @@ -35,77 +35,89 @@ */ public abstract class BaseJavetConsoleInterceptor extends BaseJavetInterceptor { /** - * The constant SPACE. - * - * @since 0.7.1 - */ - protected static final String SPACE = " "; - /** - * The constant JS_FUNCTION_DEBUG. + * The constant JAVA_CONSOLE_DEBUG. * * @since 0.7.0 */ - protected static final String JS_FUNCTION_DEBUG = "debug"; + protected static final String JAVA_CONSOLE_DEBUG = "consoleDebug"; /** - * The constant JS_FUNCTION_ERROR. + * The constant JAVA_CONSOLE_ERROR. + * * @since 0.7.0 */ - protected static final String JS_FUNCTION_ERROR = "error"; + protected static final String JAVA_CONSOLE_ERROR = "consoleError"; /** - * The constant JS_FUNCTION_INFO. + * The constant JAVA_CONSOLE_INFO. + * * @since 0.7.0 */ - protected static final String JS_FUNCTION_INFO = "info"; + protected static final String JAVA_CONSOLE_INFO = "consoleInfo"; /** - * The constant JS_FUNCTION_LOG. + * The constant JAVA_CONSOLE_LOG. + * * @since 0.7.0 */ - protected static final String JS_FUNCTION_LOG = "log"; + protected static final String JAVA_CONSOLE_LOG = "consoleLog"; /** - * The constant JS_FUNCTION_TRACE. + * The constant JAVA_CONSOLE_TRACE. + * * @since 0.7.0 */ - protected static final String JS_FUNCTION_TRACE = "trace"; + protected static final String JAVA_CONSOLE_TRACE = "consoleTrace"; /** - * The constant JS_FUNCTION_WARN. + * The constant JAVA_CONSOLE_WARN. + * * @since 0.7.0 */ - protected static final String JS_FUNCTION_WARN = "warn"; + protected static final String JAVA_CONSOLE_WARN = "consoleWarn"; /** - * The constant JAVA_CONSOLE_DEBUG. + * The constant JS_FUNCTION_DEBUG. + * * @since 0.7.0 */ - protected static final String JAVA_CONSOLE_DEBUG = "consoleDebug"; + protected static final String JS_FUNCTION_DEBUG = "debug"; /** - * The constant JAVA_CONSOLE_ERROR. + * The constant JS_FUNCTION_ERROR. + * * @since 0.7.0 */ - protected static final String JAVA_CONSOLE_ERROR = "consoleError"; + protected static final String JS_FUNCTION_ERROR = "error"; /** - * The constant JAVA_CONSOLE_INFO. + * The constant JS_FUNCTION_INFO. + * * @since 0.7.0 */ - protected static final String JAVA_CONSOLE_INFO = "consoleInfo"; + protected static final String JS_FUNCTION_INFO = "info"; /** - * The constant JAVA_CONSOLE_LOG. + * The constant JS_FUNCTION_LOG. + * * @since 0.7.0 */ - protected static final String JAVA_CONSOLE_LOG = "consoleLog"; + protected static final String JS_FUNCTION_LOG = "log"; /** - * The constant JAVA_CONSOLE_TRACE. + * The constant JS_FUNCTION_TRACE. + * * @since 0.7.0 */ - protected static final String JAVA_CONSOLE_TRACE = "consoleTrace"; + protected static final String JS_FUNCTION_TRACE = "trace"; /** - * The constant JAVA_CONSOLE_WARN. + * The constant JS_FUNCTION_WARN. + * * @since 0.7.0 */ - protected static final String JAVA_CONSOLE_WARN = "consoleWarn"; + protected static final String JS_FUNCTION_WARN = "warn"; /** * The constant PROPERTY_CONSOLE. + * * @since 0.7.0 */ protected static final String PROPERTY_CONSOLE = "console"; + /** + * The constant SPACE. + * + * @since 0.7.1 + */ + protected static final String SPACE = " "; /** * Instantiates a new Base javet console interceptor. diff --git a/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java b/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java index f0d6021cf..a389e014a 100644 --- a/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java +++ b/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java @@ -77,6 +77,36 @@ public JavetStandardConsoleInterceptor(V8Runtime v8Runtime) { error = System.err; } + @Override + public void consoleDebug(V8Value... v8Values) { + debug.println(concat(v8Values)); + } + + @Override + public void consoleError(V8Value... v8Values) { + error.println(concat(v8Values)); + } + + @Override + public void consoleInfo(V8Value... v8Values) { + info.println(concat(v8Values)); + } + + @Override + public void consoleLog(V8Value... v8Values) { + log.println(concat(v8Values)); + } + + @Override + public void consoleTrace(V8Value... v8Values) { + trace.println(concat(v8Values)); + } + + @Override + public void consoleWarn(V8Value... v8Values) { + warn.println(concat(v8Values)); + } + /** * Gets debug. * @@ -88,103 +118,103 @@ public PrintStream getDebug() { } /** - * Sets debug. + * Gets error. * - * @param debug the debug + * @return the error * @since 0.7.0 */ - public void setDebug(PrintStream debug) { - this.debug = debug; + public PrintStream getError() { + return error; } /** - * Gets error. + * Gets info. * - * @return the error + * @return the info * @since 0.7.0 */ - public PrintStream getError() { - return error; + public PrintStream getInfo() { + return info; } /** - * Sets error. + * Gets log. * - * @param error the error + * @return the log * @since 0.7.0 */ - public void setError(PrintStream error) { - this.error = error; + public PrintStream getLog() { + return log; } /** - * Gets info. + * Gets trace. * - * @return the info + * @return the trace * @since 0.7.0 */ - public PrintStream getInfo() { - return info; + public PrintStream getTrace() { + return trace; } /** - * Sets info. + * Gets warn. * - * @param info the info + * @return the warn * @since 0.7.0 */ - public void setInfo(PrintStream info) { - this.info = info; + public PrintStream getWarn() { + return warn; } /** - * Gets log. + * Sets debug. * - * @return the log + * @param debug the debug * @since 0.7.0 */ - public PrintStream getLog() { - return log; + public void setDebug(PrintStream debug) { + this.debug = debug; } /** - * Sets log. + * Sets error. * - * @param log the log + * @param error the error * @since 0.7.0 */ - public void setLog(PrintStream log) { - this.log = log; + public void setError(PrintStream error) { + this.error = error; } /** - * Gets trace. + * Sets info. * - * @return the trace + * @param info the info * @since 0.7.0 */ - public PrintStream getTrace() { - return trace; + public void setInfo(PrintStream info) { + this.info = info; } /** - * Sets trace. + * Sets log. * - * @param trace the trace + * @param log the log * @since 0.7.0 */ - public void setTrace(PrintStream trace) { - this.trace = trace; + public void setLog(PrintStream log) { + this.log = log; } /** - * Gets warn. + * Sets trace. * - * @return the warn + * @param trace the trace * @since 0.7.0 */ - public PrintStream getWarn() { - return warn; + public void setTrace(PrintStream trace) { + this.trace = trace; } /** @@ -196,34 +226,4 @@ public PrintStream getWarn() { public void setWarn(PrintStream warn) { this.warn = warn; } - - @Override - public void consoleDebug(V8Value... v8Values) { - debug.println(concat(v8Values)); - } - - @Override - public void consoleError(V8Value... v8Values) { - error.println(concat(v8Values)); - } - - @Override - public void consoleInfo(V8Value... v8Values) { - info.println(concat(v8Values)); - } - - @Override - public void consoleLog(V8Value... v8Values) { - log.println(concat(v8Values)); - } - - @Override - public void consoleTrace(V8Value... v8Values) { - trace.println(concat(v8Values)); - } - - @Override - public void consoleWarn(V8Value... v8Values) { - warn.println(concat(v8Values)); - } } diff --git a/src/main/java/com/caoccao/javet/interop/IV8Native.java b/src/main/java/com/caoccao/javet/interop/IV8Native.java index 1d2c76797..3995872b7 100644 --- a/src/main/java/com/caoccao/javet/interop/IV8Native.java +++ b/src/main/java/com/caoccao/javet/interop/IV8Native.java @@ -77,10 +77,10 @@ Object execute( Object getOwnPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); - Object getPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); - Object getProperty(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key); + Object getPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + int getSize(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); String getSourceCode(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); diff --git a/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java b/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java index b1b3ee325..fe9dadd0f 100644 --- a/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java +++ b/src/main/java/com/caoccao/javet/interop/JavetClassLoader.java @@ -29,9 +29,9 @@ class JavetClassLoader extends ClassLoader { protected static final String JAVET_LIB_LOADER_CLASS_NAME = JavetLibLoader.class.getName(); + protected static final String METHOD_LOAD = "load"; protected static final String NODE_NATIVE_CLASS_NAME = NodeNative.class.getName(); protected static final String V8_NATIVE_CLASS_NAME = V8Native.class.getName(); - protected static final String METHOD_LOAD = "load"; protected JSRuntimeType jsRuntimeType; JavetClassLoader(ClassLoader parent, JSRuntimeType jsRuntimeType) { diff --git a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java index 3459cd306..c772ee439 100644 --- a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java +++ b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java @@ -31,15 +31,15 @@ public final class JavetLibLoader { static final String LIB_VERSION = "0.9.1"; + private static final int BUFFER_LENGTH = 4096; private static final String CHMOD = "chmod"; - private static final String XRR = "755"; - private static final String LIB_FILE_NAME_FORMAT = "libjavet-{0}-{1}-x86_64.v.{2}.{3}"; - private static final String RESOURCE_NAME_FORMAT = "/{0}"; private static final String LIB_FILE_EXTENSION_LINUX = "so"; private static final String LIB_FILE_EXTENSION_WINDOWS = "dll"; + private static final String LIB_FILE_NAME_FORMAT = "libjavet-{0}-{1}-x86_64.v.{2}.{3}"; private static final String OS_LINUX = "linux"; private static final String OS_WINDOWS = "windows"; - private static final int BUFFER_LENGTH = 4096; + private static final String RESOURCE_NAME_FORMAT = "/{0}"; + private static final String XRR = "755"; private final JSRuntimeType jsRuntimeType; private boolean loaded; @@ -49,14 +49,6 @@ public JavetLibLoader(JSRuntimeType jsRuntimeType) { loaded = false; } - public boolean isLoaded() { - return loaded; - } - - public JSRuntimeType getJSRuntimeType() { - return jsRuntimeType; - } - private void deployLibFile(String resourceFileName, File libFile) { boolean isLibFileLocked = false; if (libFile.exists()) { @@ -92,6 +84,10 @@ private void deployLibFile(String resourceFileName, File libFile) { } } + public JSRuntimeType getJSRuntimeType() { + return jsRuntimeType; + } + public String getResourceFileName() throws JavetException { String fileName, resourceFileName, osName, fileExtension; @@ -117,6 +113,10 @@ public String getResourceFileName() return resourceFileName; } + public boolean isLoaded() { + return loaded; + } + public void load() throws JavetException { if (!loaded) { diff --git a/src/main/java/com/caoccao/javet/interop/NodeRuntime.java b/src/main/java/com/caoccao/javet/interop/NodeRuntime.java index 82e3ee11e..020000354 100644 --- a/src/main/java/com/caoccao/javet/interop/NodeRuntime.java +++ b/src/main/java/com/caoccao/javet/interop/NodeRuntime.java @@ -101,6 +101,12 @@ public int getNodeModuleCount() { return nodeModuleMap.size(); } + @Override + protected void removeAllReferences() throws JavetException { + removeNodeModules(); + super.removeAllReferences(); + } + public void removeNodeModule(INodeModule iNodeModule) throws JavetException { Objects.requireNonNull(iNodeModule); if (nodeModuleMap.containsKey(iNodeModule.getName())) { @@ -115,10 +121,4 @@ protected void removeNodeModules() { nodeModuleMap.clear(); } } - - @Override - protected void removeAllReferences() throws JavetException { - removeNodeModules(); - super.removeAllReferences(); - } } diff --git a/src/main/java/com/caoccao/javet/interop/V8Flags.java b/src/main/java/com/caoccao/javet/interop/V8Flags.java index 42e2720dc..972a3c05a 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Flags.java +++ b/src/main/java/com/caoccao/javet/interop/V8Flags.java @@ -34,28 +34,28 @@ public final class V8Flags { useStrict = true; } + public boolean isAllowNativesSyntax() { + return allowNativesSyntax; + } + + public boolean isExposeGC() { + return exposeGC; + } + public boolean isExposeInspectorScripts() { return exposeInspectorScripts; } - public void setExposeInspectorScripts(boolean exposeInspectorScripts) { - if (!sealed) { - this.exposeInspectorScripts = exposeInspectorScripts; - } + public boolean isSealed() { + return sealed; } public boolean isTrackRetainingPath() { return trackRetainingPath; } - public void setTrackRetainingPath(boolean trackRetainingPath) { - if (!sealed) { - this.trackRetainingPath = trackRetainingPath; - } - } - - public boolean isSealed() { - return sealed; + public boolean isUseStrict() { + return useStrict; } public void seal() { @@ -64,8 +64,10 @@ public void seal() { } } - public boolean isExposeGC() { - return exposeGC; + public void setAllowNativesSyntax(boolean allowNativesSyntax) { + if (!sealed) { + this.allowNativesSyntax = allowNativesSyntax; + } } public void setExposeGC(boolean exposeGC) { @@ -74,23 +76,21 @@ public void setExposeGC(boolean exposeGC) { } } - public boolean isUseStrict() { - return useStrict; - } - - public void setUseStrict(boolean useStrict) { + public void setExposeInspectorScripts(boolean exposeInspectorScripts) { if (!sealed) { - this.useStrict = useStrict; + this.exposeInspectorScripts = exposeInspectorScripts; } } - public boolean isAllowNativesSyntax() { - return allowNativesSyntax; + public void setTrackRetainingPath(boolean trackRetainingPath) { + if (!sealed) { + this.trackRetainingPath = trackRetainingPath; + } } - public void setAllowNativesSyntax(boolean allowNativesSyntax) { + public void setUseStrict(boolean useStrict) { if (!sealed) { - this.allowNativesSyntax = allowNativesSyntax; + this.useStrict = useStrict; } } } diff --git a/src/main/java/com/caoccao/javet/interop/V8Host.java b/src/main/java/com/caoccao/javet/interop/V8Host.java index 712fe65b3..a7de1c236 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Host.java +++ b/src/main/java/com/caoccao/javet/interop/V8Host.java @@ -42,12 +42,12 @@ public final class V8Host implements AutoCloseable { * The constant GLOBAL_THIS. */ public static final String GLOBAL_THIS = "globalThis"; - private static final long INVALID_HANDLE = 0L; private static final String FLAG_ALLOW_NATIVES_SYNTAX = "--allow-natives-syntax"; private static final String FLAG_EXPOSE_GC = "--expose-gc"; private static final String FLAG_EXPOSE_INSPECTOR_SCRIPTS = "--expose-inspector-scripts"; private static final String FLAG_TRACK_RETAINING_PATH = "--track-retaining-path"; private static final String FLAG_USE_STRICT = "--use-strict"; + private static final long INVALID_HANDLE = 0L; private static final String SPACE = " "; private static boolean libraryReloadable = false; private static volatile double memoryUsageThresholdRatio = 0.7; @@ -56,10 +56,10 @@ public final class V8Host implements AutoCloseable { private final IJavetLogger logger; private final V8Notifier v8Notifier; private final ConcurrentHashMap v8RuntimeMap; + private boolean isolateCreated; private JavetClassLoader javetClassLoader; - private boolean libraryLoaded; private JavetException lastException; - private boolean isolateCreated; + private boolean libraryLoaded; private IV8Native v8Native; private V8Host(JSRuntimeType jsRuntimeType) { @@ -94,6 +94,16 @@ public static V8Host getInstance(JSRuntimeType jsRuntimeType) { return null; } + /** + * Gets memory usage threshold ratio. + * + * @return the memory usage threshold ratio + * @since 0.8.3 + */ + public static double getMemoryUsageThresholdRatio() { + return memoryUsageThresholdRatio; + } + /** * Gets Node instance. *

@@ -119,27 +129,23 @@ public static V8Host getV8Instance() { } /** - * Gets memory usage threshold ratio. + * Determines whether the JNI library is reloadable or not. * - * @return the memory usage threshold ratio - * @since 0.8.3 + * @return true: reloadable, false: not reloadable, default: false + * @since 0.9.1 */ - public static double getMemoryUsageThresholdRatio() { - return memoryUsageThresholdRatio; + public static boolean isLibraryReloadable() { + return libraryReloadable; } /** - * Sets memory usage threshold ratio. - *

- * This manageable usage threshold attribute is designed for monitoring - * the increasing trend of memory usage with low overhead. + * Sets whether the JNI library is reloadable or not. * - * @param memoryUsageThresholdRatio the memory usage threshold ratio - * @since 0.8.3 + * @param libraryReloadable true: reloadable, false: not reloadable + * @since 0.9.1 */ - public static void setMemoryUsageThresholdRatio(double memoryUsageThresholdRatio) { - assert 0 <= memoryUsageThresholdRatio && memoryUsageThresholdRatio < 1; - V8Host.memoryUsageThresholdRatio = memoryUsageThresholdRatio; + public static void setLibraryReloadable(boolean libraryReloadable) { + V8Host.libraryReloadable = libraryReloadable; } private static void setMemoryUsageThreshold() { @@ -160,23 +166,17 @@ private static void setMemoryUsageThreshold() { } /** - * Determines whether the JNI library is reloadable or not. - * - * @return true: reloadable, false: not reloadable, default: false - * @since 0.9.1 - */ - public static boolean isLibraryReloadable() { - return libraryReloadable; - } - - /** - * Sets whether the JNI library is reloadable or not. + * Sets memory usage threshold ratio. + *

+ * This manageable usage threshold attribute is designed for monitoring + * the increasing trend of memory usage with low overhead. * - * @param libraryReloadable true: reloadable, false: not reloadable - * @since 0.9.1 + * @param memoryUsageThresholdRatio the memory usage threshold ratio + * @since 0.8.3 */ - public static void setLibraryReloadable(boolean libraryReloadable) { - V8Host.libraryReloadable = libraryReloadable; + public static void setMemoryUsageThresholdRatio(double memoryUsageThresholdRatio) { + assert 0 <= memoryUsageThresholdRatio && memoryUsageThresholdRatio < 1; + V8Host.memoryUsageThresholdRatio = memoryUsageThresholdRatio; } /** @@ -198,78 +198,22 @@ public void close() throws JavetException { } /** - * Disable GC notification. - * - * @return the self - * @since 0.8.3 - */ - @SuppressWarnings("UnusedReturnValue") - public V8Host disableGCNotification() { - v8Notifier.unregisterListener(); - return this; - } - - /** - * Enable GC notification. - * - * @return the self - * @since 0.8.3 - */ - public V8Host enableGCNotification() { - setMemoryUsageThreshold(); - // Javet {@link V8Notifier} listens to this notification to notify {@link V8Runtime} to perform GC. - v8Notifier.registerListeners(); - return this; - } - - /** - * Gets flags. + * Close V8 runtime. * - * @return the flags + * @param v8Runtime the V8 runtime * @since 0.7.0 */ - public V8Flags getFlags() { - return flags; - } - - /** - * Get internal statistic internal for test purpose. - * - * @return the long [ ] - * @since 0.8.3 - */ - public long[] getInternalStatistic() { - return v8Native.getInternalStatistic(); - } - - /** - * Gets javet version. - * - * @return the javet version - * @since 0.7.1 - */ - public String getJavetVersion() { - return JavetLibLoader.LIB_VERSION; - } - - /** - * Gets logger. - * - * @return the logger - * @since 0.7.3 - */ - public IJavetLogger getLogger() { - return logger; - } - - /** - * Gets V8 native. - * - * @return the V8 native - * @since 0.8.0 - */ - IV8Native getV8Native() { - return v8Native; + public void closeV8Runtime(V8Runtime v8Runtime) { + if (!libraryLoaded) { + return; + } + if (v8Runtime != null) { + final long handle = v8Runtime.getHandle(); + if (handle > INVALID_HANDLE && v8RuntimeMap.containsKey(handle)) { + v8Native.closeV8Runtime(v8Runtime.getHandle()); + v8RuntimeMap.remove(handle); + } + } } /** @@ -332,77 +276,68 @@ public R createV8Runtime(boolean pooled, String globalName } /** - * Close V8 runtime. + * Disable GC notification. * - * @param v8Runtime the V8 runtime + * @return the self + * @since 0.8.3 + */ + @SuppressWarnings("UnusedReturnValue") + public V8Host disableGCNotification() { + v8Notifier.unregisterListener(); + return this; + } + + /** + * Enable GC notification. + * + * @return the self + * @since 0.8.3 + */ + public V8Host enableGCNotification() { + setMemoryUsageThreshold(); + // Javet {@link V8Notifier} listens to this notification to notify {@link V8Runtime} to perform GC. + v8Notifier.registerListeners(); + return this; + } + + /** + * Gets flags. + * + * @return the flags * @since 0.7.0 */ - public void closeV8Runtime(V8Runtime v8Runtime) { - if (!libraryLoaded) { - return; - } - if (v8Runtime != null) { - final long handle = v8Runtime.getHandle(); - if (handle > INVALID_HANDLE && v8RuntimeMap.containsKey(handle)) { - v8Native.closeV8Runtime(v8Runtime.getHandle()); - v8RuntimeMap.remove(handle); - } - } + public V8Flags getFlags() { + return flags; } /** - * Gets JS runtime type. + * Get internal statistic internal for test purpose. * - * @return the JS runtime type - * @since 0.8.0 + * @return the long [ ] + * @since 0.8.3 */ - public JSRuntimeType getJSRuntimeType() { - return jsRuntimeType; + public long[] getInternalStatistic() { + return v8Native.getInternalStatistic(); } /** - * Load library. - *

- * Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. + * Gets JS runtime type. * - * @return true: library is loaded, false: library is not loaded - * @since 0.9.1 + * @return the JS runtime type + * @since 0.8.0 */ - public synchronized boolean loadLibrary() { - if (!libraryLoaded) { - try { - javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), jsRuntimeType); - javetClassLoader.load(); - v8Native = javetClassLoader.getNative(); - libraryLoaded = true; - isolateCreated = false; - } catch (JavetException e) { - logger.logError(e, "Failed to load Javet lib with error {0}.", e.getMessage()); - lastException = e; - } - } - return libraryLoaded; + public JSRuntimeType getJSRuntimeType() { + return jsRuntimeType; } /** - * Unload library. - *

- * Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. + * Gets javet version. * - * @return true: library is unloaded, false: library is loaded - * @since 0.9.1 + * @return the javet version + * @since 0.7.1 */ - public synchronized boolean unloadLibrary() { - if (libraryLoaded && v8RuntimeMap.isEmpty()) { - isolateCreated = false; - v8Native = null; - javetClassLoader = null; - System.gc(); - System.runFinalization(); - libraryLoaded = false; - lastException = null; - } - return !libraryLoaded; + public String getJavetVersion() { + return JavetLibLoader.LIB_VERSION; } /** @@ -415,6 +350,26 @@ public JavetException getLastException() { return lastException; } + /** + * Gets logger. + * + * @return the logger + * @since 0.7.3 + */ + public IJavetLogger getLogger() { + return logger; + } + + /** + * Gets V8 native. + * + * @return the V8 native + * @since 0.8.0 + */ + IV8Native getV8Native() { + return v8Native; + } + /** * Gets V8 runtime count. * @@ -425,6 +380,16 @@ public int getV8RuntimeCount() { return v8RuntimeMap.size(); } + /** + * Is isolate created. + * + * @return true: created, false: not created + * @since 0.8.0 + */ + public boolean isIsolateCreated() { + return isolateCreated; + } + /** * Is library loaded. * @@ -436,13 +401,27 @@ public boolean isLibraryLoaded() { } /** - * Is isolate created. + * Load library. + *

+ * Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. * - * @return true: created, false: not created - * @since 0.8.0 + * @return true: library is loaded, false: library is not loaded + * @since 0.9.1 */ - public boolean isIsolateCreated() { - return isolateCreated; + public synchronized boolean loadLibrary() { + if (!libraryLoaded) { + try { + javetClassLoader = new JavetClassLoader(getClass().getClassLoader(), jsRuntimeType); + javetClassLoader.load(); + v8Native = javetClassLoader.getNative(); + libraryLoaded = true; + isolateCreated = false; + } catch (JavetException e) { + logger.logError(e, "Failed to load Javet lib with error {0}.", e.getMessage()); + lastException = e; + } + } + return libraryLoaded; } /** @@ -476,6 +455,27 @@ public boolean setFlags() { return false; } + /** + * Unload library. + *

+ * Note: setLibraryReloadable(true) must be called, otherwise, JVM will crash. + * + * @return true: library is unloaded, false: library is loaded + * @since 0.9.1 + */ + public synchronized boolean unloadLibrary() { + if (libraryLoaded && v8RuntimeMap.isEmpty()) { + isolateCreated = false; + v8Native = null; + javetClassLoader = null; + System.gc(); + System.runFinalization(); + libraryLoaded = false; + lastException = null; + } + return !libraryLoaded; + } + private static class NodeInstanceHolder { private static V8Host INSTANCE = new V8Host(JSRuntimeType.Node); } diff --git a/src/main/java/com/caoccao/javet/interop/V8Inspector.java b/src/main/java/com/caoccao/javet/interop/V8Inspector.java index cc4ed715e..61b80a06c 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Inspector.java +++ b/src/main/java/com/caoccao/javet/interop/V8Inspector.java @@ -26,11 +26,11 @@ import java.util.Objects; public final class V8Inspector { - private IJavetLogger logger; - private final String name; private final List listeners; + private final String name; private final IV8Native v8Native; private final V8Runtime v8Runtime; + private IJavetLogger logger; V8Inspector(V8Runtime v8Runtime, String name, IV8Native v8Native) { logger = v8Runtime.getLogger(); @@ -61,11 +61,6 @@ public IJavetLogger getLogger() { return logger; } - public void setLogger(IJavetLogger logger) { - Objects.requireNonNull(logger); - this.logger = logger; - } - public String getName() { return name; } @@ -120,4 +115,9 @@ public void sendRequest(String message) throws JavetException { } v8Native.v8InspectorSend(v8Runtime.getHandle(), message); } + + public void setLogger(IJavetLogger logger) { + Objects.requireNonNull(logger); + this.logger = logger; + } } diff --git a/src/main/java/com/caoccao/javet/interop/V8Native.java b/src/main/java/com/caoccao/javet/interop/V8Native.java index 3df5f51ae..5839fd5ad 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Native.java +++ b/src/main/java/com/caoccao/javet/interop/V8Native.java @@ -117,10 +117,10 @@ public native Object execute( public native Object getOwnPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); @Override - public native Object getPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + public native Object getProperty(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key); @Override - public native Object getProperty(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key); + public native Object getPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); @Override public native int getSize(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); diff --git a/src/main/java/com/caoccao/javet/interop/V8Runtime.java b/src/main/java/com/caoccao/javet/interop/V8Runtime.java index 137494849..8bd71b9c3 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Runtime.java +++ b/src/main/java/com/caoccao/javet/interop/V8Runtime.java @@ -45,10 +45,10 @@ @SuppressWarnings("unchecked") public class V8Runtime implements IJavetClosable, IV8Creatable, IV8Convertible { - protected static final long INVALID_HANDLE = 0L; - protected static final String PROPERTY_DATA_VIEW = "DataView"; protected static final IJavetConverter DEFAULT_CONVERTER = new JavetObjectConverter(); protected static final String DEFAULT_MESSAGE_FORMAT_JAVET_INSPECTOR = "Javet Inspector {0}"; + protected static final long INVALID_HANDLE = 0L; + protected static final String PROPERTY_DATA_VIEW = "DataView"; protected static final int V8_VALUE_BOOLEAN_FALSE_INDEX = 0; protected static final int V8_VALUE_BOOLEAN_TRUE_INDEX = 1; protected static final int V8_VALUE_NUMBER_LOWER_BOUND = -128; // Inclusive @@ -71,13 +71,13 @@ public class V8Runtime implements IJavetClosable, IV8Creatable, IV8Convertible { protected String globalName; protected long handle; protected IJavetLogger logger; - protected Map v8ModuleMap; protected boolean pooled; protected IJavetPromiseRejectCallback promiseRejectCallback; protected Map referenceMap; protected V8Host v8Host; - protected IV8Native v8Native; protected V8Inspector v8Inspector; + protected Map v8ModuleMap; + protected IV8Native v8Native; V8Runtime(V8Host v8Host, long handle, boolean pooled, IV8Native v8Native, String globalName) { assert handle != 0; @@ -102,14 +102,14 @@ public void add(IV8ValueSet iV8ValueKeySet, V8Value value) throws JavetException v8Native.add(handle, iV8ValueKeySet.getHandle(), iV8ValueKeySet.getType().getId(), value); } - public void addV8Module(IV8Module iV8Module) { - v8ModuleMap.put(iV8Module.getResourceName(), iV8Module); - } - public void addReference(IV8ValueReference iV8ValueReference) { referenceMap.put(iV8ValueReference.getHandle(), iV8ValueReference); } + public void addV8Module(IV8Module iV8Module) { + v8ModuleMap.put(iV8Module.getResourceName(), iV8Module); + } + public void allowEval(boolean allow) { v8Native.allowCodeGenerationFromStrings(handle, allow); } @@ -203,11 +203,6 @@ public V8ValueBoolean createV8ValueBoolean(boolean booleanValue) throws JavetExc cachedV8ValueBooleans[V8_VALUE_BOOLEAN_FALSE_INDEX]; } - @Override - public V8ValueDouble createV8ValueDouble(double doubleValue) throws JavetException { - return decorateV8Value(new V8ValueDouble(doubleValue)); - } - @Override public V8ValueDataView createV8ValueDataView(V8ValueArrayBuffer v8ValueArrayBuffer) throws JavetException { Objects.requireNonNull(v8ValueArrayBuffer); @@ -216,6 +211,11 @@ public V8ValueDataView createV8ValueDataView(V8ValueArrayBuffer v8ValueArrayBuff } } + @Override + public V8ValueDouble createV8ValueDouble(double doubleValue) throws JavetException { + return decorateV8Value(new V8ValueDouble(doubleValue)); + } + @Override public V8ValueFunction createV8ValueFunction(JavetCallbackContext javetCallbackContext) throws JavetException { Objects.requireNonNull(javetCallbackContext); @@ -342,19 +342,6 @@ public IJavetConverter getConverter() { return converter; } - public void setConverter(IJavetConverter converter) { - Objects.requireNonNull(converter); - this.converter = converter; - } - - public String getGlobalName() { - return globalName; - } - - public void setGlobalName(String globalName) { - this.globalName = globalName; - } - public IV8Executor getExecutor(File scriptFile) throws JavetException { return getExecutor(scriptFile.toPath()); } @@ -367,17 +354,8 @@ public IV8Executor getExecutor(String scriptString) { return new V8StringExecutor(this, scriptString); } - @SuppressWarnings("RedundantThrows") - public int getIdentityHash(IV8ValueReference iV8ValueReference) throws JavetException { - return v8Native.getIdentityHash(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType().getId()); - } - - public IJavetLogger getLogger() { - return logger; - } - - public void setLogger(IJavetLogger logger) { - this.logger = logger; + public String getGlobalName() { + return globalName; } public V8ValueGlobalObject getGlobalObject() throws JavetException { @@ -388,6 +366,11 @@ public long getHandle() { return handle; } + @SuppressWarnings("RedundantThrows") + public int getIdentityHash(IV8ValueReference iV8ValueReference) throws JavetException { + return v8Native.getIdentityHash(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType().getId()); + } + public IV8ValueArray getInternalProperties(IV8ValueFunction iV8ValueFunction) throws JavetException { return decorateV8Value((V8ValueArray) v8Native.getInternalProperties( handle, iV8ValueFunction.getHandle(), iV8ValueFunction.getType().getId())); @@ -398,15 +381,15 @@ public JSFunctionType getJSFunctionType(IV8ValueFunction iV8ValueFunction) { handle, iV8ValueFunction.getHandle(), iV8ValueFunction.getType().getId())); } + public JSRuntimeType getJSRuntimeType() { + return JSRuntimeType.V8; + } + public JSScopeType getJSScopeType(IV8ValueFunction iV8ValueFunction) { return JSScopeType.parse(v8Native.getJSScopeType( handle, iV8ValueFunction.getHandle(), iV8ValueFunction.getType().getId())); } - public JSRuntimeType getJSRuntimeType() { - return JSRuntimeType.V8; - } - public int getLength(IV8ValueArray iV8ValueArray) throws JavetException { return v8Native.getLength(handle, iV8ValueArray.getHandle(), iV8ValueArray.getType().getId()); } @@ -415,6 +398,10 @@ public int getLength(IV8ValueTypedArray iV8ValueTypedArray) throws JavetExceptio return v8Native.getLength(handle, iV8ValueTypedArray.getHandle(), iV8ValueTypedArray.getType().getId()); } + public IJavetLogger getLogger() { + return logger; + } + public IV8ValueArray getOwnPropertyNames( IV8ValueObject iV8ValueObject) throws JavetException { return decorateV8Value((V8ValueArray) v8Native.getOwnPropertyNames( @@ -425,11 +412,6 @@ public IJavetPromiseRejectCallback getPromiseRejectCallback() { return promiseRejectCallback; } - public void setPromiseRejectCallback(IJavetPromiseRejectCallback promiseRejectCallback) { - Objects.requireNonNull(promiseRejectCallback); - this.promiseRejectCallback = promiseRejectCallback; - } - public T getProperty( IV8ValueObject iV8ValueObject, V8Value key) throws JavetException { decorateV8Value(key); @@ -493,6 +475,19 @@ public boolean hasOwnProperty(IV8ValueObject iV8ValueObject, V8Value key) throws return v8Native.hasOwnProperty(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType().getId(), key); } + /** + * Idle notification deadline. + *

+ * Note: This API is the recommended one that notifies V8 to perform GC. + * + * @param deadlineInMillis the deadline in millis + */ + public void idleNotificationDeadline(long deadlineInMillis) { + if (handle != INVALID_HANDLE && deadlineInMillis > 0) { + v8Native.idleNotificationDeadline(handle, deadlineInMillis); + } + } + protected void initializeV8ValueCache() { try { cachedV8ValueNull = decorateV8Value(new V8ValueNull()); @@ -531,23 +526,6 @@ public boolean isGCScheduled() { return gcScheduled; } - public void setGCScheduled(boolean gcScheduled) { - this.gcScheduled = gcScheduled; - } - - /** - * Idle notification deadline. - *

- * Note: This API is the recommended one that notifies V8 to perform GC. - * - * @param deadlineInMillis the deadline in millis - */ - public void idleNotificationDeadline(long deadlineInMillis) { - if (handle != INVALID_HANDLE && deadlineInMillis > 0) { - v8Native.idleNotificationDeadline(handle, deadlineInMillis); - } - } - public boolean isInUse() { return v8Native.isInUse(handle); } @@ -631,6 +609,17 @@ public T promiseThen( functionRejectedHandle == null ? 0L : functionRejectedHandle.getHandle())); } + protected void receivePromiseRejectCallback(int event, V8ValuePromise promise, V8Value value) { + try { + decorateV8Values(promise, value); + promiseRejectCallback.callback(JavetPromiseRejectEvent.parse(event), promise, value); + } catch (Throwable t) { + logger.logError(t, "Failed to process promise reject callback {0}.", event); + } finally { + JavetResourceUtils.safeClose(promise, value); + } + } + protected void removeAllReferences() throws JavetException { removeReferences(); removeCallbackContexts(); @@ -756,17 +745,6 @@ public V8Runtime resetIsolate() throws JavetException { return this; } - protected void receivePromiseRejectCallback(int event, V8ValuePromise promise, V8Value value) { - try { - decorateV8Values(promise, value); - promiseRejectCallback.callback(JavetPromiseRejectEvent.parse(event), promise, value); - } catch (Throwable t) { - logger.logError(t, "Failed to process promise reject callback {0}.", event); - } finally { - JavetResourceUtils.safeClose(promise, value); - } - } - public boolean sameValue(IV8ValueObject iV8ValueObject1, IV8ValueObject iV8ValueObject2) { return v8Native.sameValue(handle, iV8ValueObject1.getHandle(), iV8ValueObject2.getHandle()); } @@ -798,6 +776,28 @@ public boolean setAccessor( return isAccessorSet; } + public void setConverter(IJavetConverter converter) { + Objects.requireNonNull(converter); + this.converter = converter; + } + + public void setGCScheduled(boolean gcScheduled) { + this.gcScheduled = gcScheduled; + } + + public void setGlobalName(String globalName) { + this.globalName = globalName; + } + + public void setLogger(IJavetLogger logger) { + this.logger = logger; + } + + public void setPromiseRejectCallback(IJavetPromiseRejectCallback promiseRejectCallback) { + Objects.requireNonNull(promiseRejectCallback); + this.promiseRejectCallback = promiseRejectCallback; + } + public boolean setProperty(IV8ValueObject iV8ValueObject, V8Value key, V8Value value) throws JavetException { decorateV8Values(key, value); return v8Native.setProperty(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType().getId(), key, value); diff --git a/src/main/java/com/caoccao/javet/interop/V8ScriptOrigin.java b/src/main/java/com/caoccao/javet/interop/V8ScriptOrigin.java index 0630f71a4..4a15cdb50 100644 --- a/src/main/java/com/caoccao/javet/interop/V8ScriptOrigin.java +++ b/src/main/java/com/caoccao/javet/interop/V8ScriptOrigin.java @@ -19,12 +19,12 @@ public final class V8ScriptOrigin { - private String resourceName; - private int resourceLineOffset; + private boolean module; private int resourceColumnOffset; + private int resourceLineOffset; + private String resourceName; private int scriptId; private boolean wasm; - private boolean module; public V8ScriptOrigin( String resourceName, @@ -65,26 +65,33 @@ public V8ScriptOrigin( scriptId, false, false); } + public int getResourceColumnOffset() { + return resourceColumnOffset; + } + + public int getResourceLineOffset() { + return resourceLineOffset; + } + public String getResourceName() { return resourceName; } - public V8ScriptOrigin setResourceName(String resourceName) { - this.resourceName = resourceName; - return this; + public int getScriptId() { + return scriptId; } - public int getResourceLineOffset() { - return resourceLineOffset; + public boolean isModule() { + return module; } - public V8ScriptOrigin setResourceLineOffset(int resourceLineOffset) { - this.resourceLineOffset = resourceLineOffset; - return this; + public boolean isWasm() { + return wasm; } - public int getResourceColumnOffset() { - return resourceColumnOffset; + public V8ScriptOrigin setModule(boolean module) { + this.module = module; + return this; } public V8ScriptOrigin setResourceColumnOffset(int resourceColumnOffset) { @@ -92,30 +99,23 @@ public V8ScriptOrigin setResourceColumnOffset(int resourceColumnOffset) { return this; } - public int getScriptId() { - return scriptId; + public V8ScriptOrigin setResourceLineOffset(int resourceLineOffset) { + this.resourceLineOffset = resourceLineOffset; + return this; } - public V8ScriptOrigin setScriptId(int scriptId) { - this.scriptId = scriptId; + public V8ScriptOrigin setResourceName(String resourceName) { + this.resourceName = resourceName; return this; } - public boolean isWasm() { - return wasm; + public V8ScriptOrigin setScriptId(int scriptId) { + this.scriptId = scriptId; + return this; } public V8ScriptOrigin setWasm(boolean wasm) { this.wasm = wasm; return this; } - - public boolean isModule() { - return module; - } - - public V8ScriptOrigin setModule(boolean module) { - this.module = module; - return this; - } } diff --git a/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java index ae32144cb..d36edf99e 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java +++ b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java @@ -26,14 +26,14 @@ public interface IJavetEngine extends IJavetClosable { JavetEngineConfig getConfig(); - R getV8Runtime() throws JavetException; - @CheckReturnValue IJavetEngineGuard getGuard(); @CheckReturnValue IJavetEngineGuard getGuard(long timeoutMillis); + R getV8Runtime() throws JavetException; + boolean isActive(); void resetContext() throws JavetException; diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java index 069865b91..fbe4eed53 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java @@ -56,11 +56,6 @@ protected void close(boolean forceClose) throws JavetException { } } - @Override - public void sendGCNotification() { - v8Runtime.lowMemoryNotification(); - } - @Override public JavetEngineConfig getConfig() { return iJavetEnginePool.getConfig(); @@ -78,6 +73,15 @@ public IJavetEngineGuard getGuard(long timeoutMillis) { return new JavetEngineGuard(this, v8Runtime, timeoutMillis); } + /** + * Gets utc now. It's designed for mocking the time in test scenario. + * + * @return the utc now + */ + protected ZonedDateTime getUTCNow() { + return JavetDateTimeUtils.getUTCNow(); + } + protected JavetEngineUsage getUsage() { return usage; } @@ -88,13 +92,9 @@ public R getV8Runtime() throws JavetException { return v8Runtime; } - /** - * Gets utc now. It's designed for mocking the time in test scenario. - * - * @return the utc now - */ - protected ZonedDateTime getUTCNow() { - return JavetDateTimeUtils.getUTCNow(); + @Override + public boolean isActive() { + return active; } @Override @@ -109,13 +109,9 @@ public void resetIsolate() throws JavetException { usage.reset(); } - protected void touchLastActiveZonedDateTime() { - usage.setLastActiveZonedDatetime(getUTCNow()); - } - @Override - public boolean isActive() { - return active; + public void sendGCNotification() { + v8Runtime.lowMemoryNotification(); } protected void setActive(boolean active) { @@ -123,4 +119,8 @@ protected void setActive(boolean active) { touchLastActiveZonedDateTime(); } + protected void touchLastActiveZonedDateTime() { + usage.setLastActiveZonedDatetime(getUTCNow()); + } + } diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java index d15ffe518..401eb293e 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java @@ -37,125 +37,120 @@ public final class JavetEngineConfig { public static final String DEFAULT_GLOBAL_NAME = "window"; public static final int DEFAULT_POOL_SHUTDOWN_TIMEOUT_SECONDS = 5; public static IJavetLogger DEFAULT_JAVET_LOGGER = new JavetDefaultLogger(JavetEnginePool.class.getName()); - private IJavetLogger javetLogger; - private String globalName; private boolean allowEval; private boolean autoSendGCNotification; private int defaultEngineGuardTimeoutMillis; private int engineGuardCheckIntervalMillis; + private ExecutorService executorService; private boolean gcBeforeEngineClose; + private String globalName; + private IJavetLogger javetLogger; private JSRuntimeType jsRuntimeType; private int maxEngineUsedCount; private int poolDaemonCheckIntervalMillis; + private int poolIdleTimeoutSeconds; private int poolMaxSize; private int poolMinSize; - private int poolIdleTimeoutSeconds; private int poolShutdownTimeoutSeconds; private int resetEngineTimeoutSeconds; - private ExecutorService executorService; public JavetEngineConfig() { reset(); } - @SuppressWarnings("UnusedReturnValue") - public JavetEngineConfig reset() { - javetLogger = DEFAULT_JAVET_LOGGER; - globalName = DEFAULT_GLOBAL_NAME; - allowEval = false; - autoSendGCNotification = true; - defaultEngineGuardTimeoutMillis = DEFAULT_ENGINE_GUARD_TIMEOUT_MILLIS; - engineGuardCheckIntervalMillis = DEFAULT_ENGINE_GUARD_CHECK_INTERVAL_MILLIS; - gcBeforeEngineClose = false; - jsRuntimeType = DEFAULT_JS_RUNTIME_TYPE; - maxEngineUsedCount = DEFAULT_MAX_ENGINE_USED_COUNT; - final int cpuCount = JavetOSUtils.getCPUCount(); - poolMinSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount >> 1); - poolMaxSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount); - poolIdleTimeoutSeconds = DEFAULT_POOL_IDLE_TIMEOUT_SECONDS; - poolShutdownTimeoutSeconds = DEFAULT_POOL_SHUTDOWN_TIMEOUT_SECONDS; - poolDaemonCheckIntervalMillis = DEFAULT_POOL_DAEMON_CHECK_INTERVAL_MILLIS; - resetEngineTimeoutSeconds = DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS; - return this; + public int getDefaultEngineGuardTimeoutMillis() { + return defaultEngineGuardTimeoutMillis; } - public boolean isGcBeforeEngineClose() { - return gcBeforeEngineClose; + public int getEngineGuardCheckIntervalMillis() { + return engineGuardCheckIntervalMillis; } - public JavetEngineConfig setGcBeforeEngineClose(boolean gcBeforeEngineClose) { - this.gcBeforeEngineClose = gcBeforeEngineClose; - return this; + public ExecutorService getExecutorService() { + return executorService; } - public boolean isAllowEval() { - return allowEval; + public String getGlobalName() { + return globalName; } - public JavetEngineConfig setAllowEval(boolean allowEval) { - this.allowEval = allowEval; - return this; + public JSRuntimeType getJSRuntimeType() { + return jsRuntimeType; } - public boolean isAutoSendGCNotification() { - return autoSendGCNotification; + public IJavetLogger getJavetLogger() { + return javetLogger; } - public JavetEngineConfig setAutoSendGCNotification(boolean autoSendGCNotification) { - this.autoSendGCNotification = autoSendGCNotification; - return this; + public int getMaxEngineUsedCount() { + return maxEngineUsedCount; } - public ExecutorService getExecutorService() { - return executorService; + public int getPoolDaemonCheckIntervalMillis() { + return poolDaemonCheckIntervalMillis; } - @SuppressWarnings("UnusedReturnValue") - JavetEngineConfig setExecutorService(ExecutorService executorService) { - this.executorService = executorService; - return this; + public int getPoolIdleTimeoutSeconds() { + return poolIdleTimeoutSeconds; } - public JSRuntimeType getJSRuntimeType() { - return jsRuntimeType; + public int getPoolMaxSize() { + return poolMaxSize; } - public JavetEngineConfig setJSRuntimeType(JSRuntimeType jsRuntimeType) { - Objects.requireNonNull(jsRuntimeType); - this.jsRuntimeType = jsRuntimeType; - return this; + public int getPoolMinSize() { + return poolMinSize; } public int getPoolShutdownTimeoutSeconds() { return poolShutdownTimeoutSeconds; } - public JavetEngineConfig setPoolShutdownTimeoutSeconds(int poolShutdownTimeoutSeconds) { - this.poolShutdownTimeoutSeconds = poolShutdownTimeoutSeconds; - return this; + public int getResetEngineTimeoutSeconds() { + return resetEngineTimeoutSeconds; } - public int getEngineGuardCheckIntervalMillis() { - return engineGuardCheckIntervalMillis; + public boolean isAllowEval() { + return allowEval; } - @SuppressWarnings("UnusedReturnValue") - public JavetEngineConfig setEngineGuardCheckIntervalMillis(int engineGuardCheckIntervalMillis) { - this.engineGuardCheckIntervalMillis = engineGuardCheckIntervalMillis; - return this; + public boolean isAutoSendGCNotification() { + return autoSendGCNotification; } - public String getGlobalName() { - return globalName; + public boolean isGcBeforeEngineClose() { + return gcBeforeEngineClose; } - public JavetEngineConfig setGlobalName(String globalName) { - this.globalName = globalName; + @SuppressWarnings("UnusedReturnValue") + public JavetEngineConfig reset() { + javetLogger = DEFAULT_JAVET_LOGGER; + globalName = DEFAULT_GLOBAL_NAME; + allowEval = false; + autoSendGCNotification = true; + defaultEngineGuardTimeoutMillis = DEFAULT_ENGINE_GUARD_TIMEOUT_MILLIS; + engineGuardCheckIntervalMillis = DEFAULT_ENGINE_GUARD_CHECK_INTERVAL_MILLIS; + gcBeforeEngineClose = false; + jsRuntimeType = DEFAULT_JS_RUNTIME_TYPE; + maxEngineUsedCount = DEFAULT_MAX_ENGINE_USED_COUNT; + final int cpuCount = JavetOSUtils.getCPUCount(); + poolMinSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount >> 1); + poolMaxSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount); + poolIdleTimeoutSeconds = DEFAULT_POOL_IDLE_TIMEOUT_SECONDS; + poolShutdownTimeoutSeconds = DEFAULT_POOL_SHUTDOWN_TIMEOUT_SECONDS; + poolDaemonCheckIntervalMillis = DEFAULT_POOL_DAEMON_CHECK_INTERVAL_MILLIS; + resetEngineTimeoutSeconds = DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS; return this; } - public int getDefaultEngineGuardTimeoutMillis() { - return defaultEngineGuardTimeoutMillis; + public JavetEngineConfig setAllowEval(boolean allowEval) { + this.allowEval = allowEval; + return this; + } + + public JavetEngineConfig setAutoSendGCNotification(boolean autoSendGCNotification) { + this.autoSendGCNotification = autoSendGCNotification; + return this; } public JavetEngineConfig setDefaultEngineGuardTimeoutMillis(int defaultEngineGuardTimeoutMillis) { @@ -163,26 +158,32 @@ public JavetEngineConfig setDefaultEngineGuardTimeoutMillis(int defaultEngineGua return this; } - public int getResetEngineTimeoutSeconds() { - return resetEngineTimeoutSeconds; + @SuppressWarnings("UnusedReturnValue") + public JavetEngineConfig setEngineGuardCheckIntervalMillis(int engineGuardCheckIntervalMillis) { + this.engineGuardCheckIntervalMillis = engineGuardCheckIntervalMillis; + return this; } - public JavetEngineConfig setResetEngineTimeoutSeconds(int resetEngineTimeoutSeconds) { - this.resetEngineTimeoutSeconds = resetEngineTimeoutSeconds; + @SuppressWarnings("UnusedReturnValue") + JavetEngineConfig setExecutorService(ExecutorService executorService) { + this.executorService = executorService; return this; } - public int getMaxEngineUsedCount() { - return maxEngineUsedCount; + public JavetEngineConfig setGcBeforeEngineClose(boolean gcBeforeEngineClose) { + this.gcBeforeEngineClose = gcBeforeEngineClose; + return this; } - public JavetEngineConfig setMaxEngineUsedCount(int maxEngineUsedCount) { - this.maxEngineUsedCount = maxEngineUsedCount; + public JavetEngineConfig setGlobalName(String globalName) { + this.globalName = globalName; return this; } - public IJavetLogger getJavetLogger() { - return javetLogger; + public JavetEngineConfig setJSRuntimeType(JSRuntimeType jsRuntimeType) { + Objects.requireNonNull(jsRuntimeType); + this.jsRuntimeType = jsRuntimeType; + return this; } public JavetEngineConfig setJavetLogger(IJavetLogger javetLogger) { @@ -191,40 +192,39 @@ public JavetEngineConfig setJavetLogger(IJavetLogger javetLogger) { return this; } - public int getPoolMaxSize() { - return poolMaxSize; - } - - public JavetEngineConfig setPoolMaxSize(int poolMaxSize) { - this.poolMaxSize = poolMaxSize; + public JavetEngineConfig setMaxEngineUsedCount(int maxEngineUsedCount) { + this.maxEngineUsedCount = maxEngineUsedCount; return this; } - public int getPoolMinSize() { - return poolMinSize; + @SuppressWarnings("UnusedReturnValue") + public JavetEngineConfig setPoolDaemonCheckIntervalMillis(int poolDaemonCheckIntervalMillis) { + this.poolDaemonCheckIntervalMillis = poolDaemonCheckIntervalMillis; + return this; } - public JavetEngineConfig setPoolMinSize(int poolMinSize) { - this.poolMinSize = poolMinSize; + public JavetEngineConfig setPoolIdleTimeoutSeconds(int poolIdleTimeoutSeconds) { + this.poolIdleTimeoutSeconds = poolIdleTimeoutSeconds; return this; } - public int getPoolIdleTimeoutSeconds() { - return poolIdleTimeoutSeconds; + public JavetEngineConfig setPoolMaxSize(int poolMaxSize) { + this.poolMaxSize = poolMaxSize; + return this; } - public JavetEngineConfig setPoolIdleTimeoutSeconds(int poolIdleTimeoutSeconds) { - this.poolIdleTimeoutSeconds = poolIdleTimeoutSeconds; + public JavetEngineConfig setPoolMinSize(int poolMinSize) { + this.poolMinSize = poolMinSize; return this; } - public int getPoolDaemonCheckIntervalMillis() { - return poolDaemonCheckIntervalMillis; + public JavetEngineConfig setPoolShutdownTimeoutSeconds(int poolShutdownTimeoutSeconds) { + this.poolShutdownTimeoutSeconds = poolShutdownTimeoutSeconds; + return this; } - @SuppressWarnings("UnusedReturnValue") - public JavetEngineConfig setPoolDaemonCheckIntervalMillis(int poolDaemonCheckIntervalMillis) { - this.poolDaemonCheckIntervalMillis = poolDaemonCheckIntervalMillis; + public JavetEngineConfig setResetEngineTimeoutSeconds(int resetEngineTimeoutSeconds) { + this.resetEngineTimeoutSeconds = resetEngineTimeoutSeconds; return this; } } diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineGuard.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineGuard.java index 6636b7203..fcd7a487f 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineGuard.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineGuard.java @@ -78,19 +78,14 @@ public long getTimeoutMillis() { return timeoutMillis; } - @Override - public void setTimeoutMillis(long timeoutSeconds) { - this.timeoutMillis = timeoutSeconds; + protected ZonedDateTime getUTCNow() { + return JavetDateTimeUtils.getUTCNow(); } public boolean isQuitting() { return quitting; } - protected ZonedDateTime getUTCNow() { - return JavetDateTimeUtils.getUTCNow(); - } - @Override public void run() { JavetEngineConfig config = iJavetEngine.getConfig(); @@ -125,4 +120,9 @@ public void run() { } quitting = true; } + + @Override + public void setTimeoutMillis(long timeoutSeconds) { + this.timeoutMillis = timeoutSeconds; + } } diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java index 06f4d1a23..151bfaa5a 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java @@ -32,12 +32,12 @@ public class JavetEnginePool implements IJavetEnginePool, Runnable { protected static final String JAVET_DAEMON_THREAD_NAME = "Javet Daemon"; + protected final ConcurrentLinkedQueue> activeEngineList; protected final Object externalLock; + protected final ConcurrentLinkedQueue> idleEngineList; + protected boolean active; protected JavetEngineConfig config; - protected ConcurrentLinkedQueue> activeEngineList; protected Thread daemonThread; - protected ConcurrentLinkedQueue> idleEngineList; - protected boolean active; protected boolean quitting; public JavetEnginePool() { @@ -55,6 +55,11 @@ public JavetEnginePool(JavetEngineConfig config) { startDaemon(); } + @Override + public void close() throws JavetException { + stopDaemon(); + } + protected JavetEngine createEngine() throws JavetException { V8Host v8Host = V8Host.getInstance(config.getJSRuntimeType()); @SuppressWarnings("ConstantConditions") @@ -64,11 +69,6 @@ protected JavetEngine createEngine() throws JavetException { return new JavetEngine<>(this, v8Runtime); } - @Override - public void close() throws JavetException { - stopDaemon(); - } - @Override public int getActiveEngineCount() { return activeEngineList.size(); diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineUsage.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineUsage.java index fc21b23f2..e24827d9e 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineUsage.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineUsage.java @@ -27,18 +27,14 @@ public JavetEngineUsage() { reset(); } - public ZonedDateTime getLastActiveZonedDatetime() { - return lastActiveZonedDatetime; - } - - public void setLastActiveZonedDatetime(ZonedDateTime lastActiveZonedDatetime) { - this.lastActiveZonedDatetime = lastActiveZonedDatetime; - } - public int getEngineUsedCount() { return engineUsedCount; } + public ZonedDateTime getLastActiveZonedDatetime() { + return lastActiveZonedDatetime; + } + public void increaseUsedCount() { ++engineUsedCount; } @@ -46,4 +42,8 @@ public void increaseUsedCount() { protected void reset() { engineUsedCount = 0; } + + public void setLastActiveZonedDatetime(ZonedDateTime lastActiveZonedDatetime) { + this.lastActiveZonedDatetime = lastActiveZonedDatetime; + } } diff --git a/src/main/java/com/caoccao/javet/interop/executors/IV8Executor.java b/src/main/java/com/caoccao/javet/interop/executors/IV8Executor.java index 143073989..b883b57a3 100644 --- a/src/main/java/com/caoccao/javet/interop/executors/IV8Executor.java +++ b/src/main/java/com/caoccao/javet/interop/executors/IV8Executor.java @@ -63,6 +63,21 @@ default String getResourceName() { return getV8ScriptOrigin().getResourceName(); } + String getScriptString() throws JavetException; + + V8Runtime getV8Runtime(); + + V8ScriptOrigin getV8ScriptOrigin(); + + default boolean isModule() { + return getV8ScriptOrigin().isModule(); + } + + default IV8Executor setModule(boolean module) { + getV8ScriptOrigin().setModule(module); + return this; + } + default IV8Executor setResourceName(String resourceName) throws JavetException { getV8ScriptOrigin().setResourceName(resourceName); V8Runtime v8Runtime = getV8Runtime(); @@ -77,21 +92,6 @@ default IV8Executor setResourceName(String resourceName) throws JavetException { return this; } - default boolean isModule() { - return getV8ScriptOrigin().isModule(); - } - - default IV8Executor setModule(boolean module) { - getV8ScriptOrigin().setModule(module); - return this; - } - - String getScriptString() throws JavetException; - - V8Runtime getV8Runtime(); - - V8ScriptOrigin getV8ScriptOrigin(); - @Override default T toObject(V v8Value) throws JavetException { return getV8Runtime().toObject(v8Value); diff --git a/src/main/java/com/caoccao/javet/interop/executors/V8PathExecutor.java b/src/main/java/com/caoccao/javet/interop/executors/V8PathExecutor.java index 5e13535ba..907ff8e59 100644 --- a/src/main/java/com/caoccao/javet/interop/executors/V8PathExecutor.java +++ b/src/main/java/com/caoccao/javet/interop/executors/V8PathExecutor.java @@ -36,6 +36,10 @@ public V8PathExecutor(V8Runtime v8Runtime, Path scriptPath) throws JavetExceptio setResourceName(scriptPath.toFile().getAbsolutePath()); } + public Path getScriptPath() { + return scriptPath; + } + @Override public String getScriptString() throws JavetException { if (scriptString == null) { @@ -50,8 +54,4 @@ public String getScriptString() throws JavetException { } return scriptString; } - - public Path getScriptPath() { - return scriptPath; - } } diff --git a/src/main/java/com/caoccao/javet/interop/executors/V8StringExecutor.java b/src/main/java/com/caoccao/javet/interop/executors/V8StringExecutor.java index 90ace05e0..92ac60f05 100644 --- a/src/main/java/com/caoccao/javet/interop/executors/V8StringExecutor.java +++ b/src/main/java/com/caoccao/javet/interop/executors/V8StringExecutor.java @@ -48,14 +48,14 @@ public V8Module compileV8Module(boolean resultRequired) throws JavetException { return v8Runtime.compileV8Module(getScriptString(), v8ScriptOrigin, resultRequired); } - @Override - public String getScriptString() throws JavetException { - return scriptString; - } - @Override @CheckReturnValue public T execute(boolean resultRequired) throws JavetException { return v8Runtime.execute(getScriptString(), v8ScriptOrigin, resultRequired); } + + @Override + public String getScriptString() throws JavetException { + return scriptString; + } } diff --git a/src/main/java/com/caoccao/javet/node/modules/NodeModuleProcess.java b/src/main/java/com/caoccao/javet/node/modules/NodeModuleProcess.java index 8c12443c7..ffb331a63 100644 --- a/src/main/java/com/caoccao/javet/node/modules/NodeModuleProcess.java +++ b/src/main/java/com/caoccao/javet/node/modules/NodeModuleProcess.java @@ -40,11 +40,11 @@ public Path getWorkingDirectory() throws JavetException { return new File(moduleObject.invokeString(FUNCTION_CWD)).toPath(); } - public void setWorkingDirectory(Path path) throws JavetException { - moduleObject.invokeVoid(FUNCTION_CHDIR, new V8ValueString(path.toAbsolutePath().toString())); - } - public void on(String eventName, V8ValueFunction v8ValueFunction) throws JavetException { moduleObject.invokeVoid(FUNCTION_ON, new V8ValueString(eventName), v8ValueFunction); } + + public void setWorkingDirectory(Path path) throws JavetException { + moduleObject.invokeVoid(FUNCTION_CHDIR, new V8ValueString(path.toAbsolutePath().toString())); + } } diff --git a/src/main/java/com/caoccao/javet/utils/JavetCallbackContext.java b/src/main/java/com/caoccao/javet/utils/JavetCallbackContext.java index cfaf4249c..f3e16a4d3 100644 --- a/src/main/java/com/caoccao/javet/utils/JavetCallbackContext.java +++ b/src/main/java/com/caoccao/javet/utils/JavetCallbackContext.java @@ -28,9 +28,9 @@ public final class JavetCallbackContext { "Javet callback context handle is invalid"; private final Method callbackMethod; private final Object callbackReceiver; - private long handle; private final boolean returnResult; private final boolean thisObjectRequired; + private long handle; public JavetCallbackContext( Object callbackReceiver, @@ -53,28 +53,28 @@ public JavetCallbackContext( this.thisObjectRequired = thisObjectRequired; } - public boolean isThisObjectRequired() { - return thisObjectRequired; + public Method getCallbackMethod() { + return callbackMethod; } public Object getCallbackReceiver() { return callbackReceiver; } - public Method getCallbackMethod() { - return callbackMethod; - } - public long getHandle() { return handle; } + public boolean isReturnResult() { + return returnResult; + } + + public boolean isThisObjectRequired() { + return thisObjectRequired; + } + public void setHandle(long handle) { assert handle > 0L : ERROR_JAVET_CALLBACK_CONTEXT_HANDLE_IS_INVALID; this.handle = handle; } - - public boolean isReturnResult() { - return returnResult; - } } diff --git a/src/main/java/com/caoccao/javet/utils/JavetDateTimeUtils.java b/src/main/java/com/caoccao/javet/utils/JavetDateTimeUtils.java index f28c61bd0..6ff72e2d0 100644 --- a/src/main/java/com/caoccao/javet/utils/JavetDateTimeUtils.java +++ b/src/main/java/com/caoccao/javet/utils/JavetDateTimeUtils.java @@ -26,21 +26,21 @@ */ public final class JavetDateTimeUtils { + /** + * The constant ZONE_ID_UTC. + */ public static final ZoneId ZONE_ID_UTC = ZoneId.of("UTC"); private JavetDateTimeUtils() { } /** - * From JS timestamp to zoned date time. - *

- * Note: the ZoneId needs to be system default because that's what V8 sees. + * Gets utc now. * - * @param jsTimestamp the JS timestamp - * @return the zoned date time + * @return the utc now */ - public static ZonedDateTime toZonedDateTime(long jsTimestamp) { - return toZonedDateTime(jsTimestamp, ZoneId.systemDefault()); + public static ZonedDateTime getUTCNow() { + return ZonedDateTime.now(ZONE_ID_UTC); } /** @@ -54,7 +54,15 @@ public static ZonedDateTime toZonedDateTime(long jsTimestamp, ZoneId zoneId) { return ZonedDateTime.ofInstant(Instant.ofEpochMilli(jsTimestamp), zoneId); } - public static ZonedDateTime getUTCNow() { - return ZonedDateTime.now(ZONE_ID_UTC); + /** + * From JS timestamp to zoned date time. + *

+ * Note: the ZoneId needs to be system default because that's what V8 sees. + * + * @param jsTimestamp the JS timestamp + * @return the zoned date time + */ + public static ZonedDateTime toZonedDateTime(long jsTimestamp) { + return toZonedDateTime(jsTimestamp, ZoneId.systemDefault()); } } diff --git a/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java b/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java index 393bf29a1..145737f62 100644 --- a/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java +++ b/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java @@ -35,14 +35,6 @@ public JavetDefaultLogger(String name) { this.name = name; } - public String getName() { - return name; - } - - public Logger getLogger() { - return logger; - } - @Override public void debug(String message) { logger.log(Level.FINE, message); @@ -65,6 +57,14 @@ public void error(String message, Throwable cause) { } } + public Logger getLogger() { + return logger; + } + + public String getName() { + return name; + } + @Override public void info(String message) { logger.info(message); diff --git a/src/main/java/com/caoccao/javet/utils/JavetOSUtils.java b/src/main/java/com/caoccao/javet/utils/JavetOSUtils.java index 1d0b0c24e..ce4893c75 100644 --- a/src/main/java/com/caoccao/javet/utils/JavetOSUtils.java +++ b/src/main/java/com/caoccao/javet/utils/JavetOSUtils.java @@ -25,7 +25,8 @@ public final class JavetOSUtils { public static final String TEMP_DIRECTORY = System.getProperty("java.io.tmpdir"); public static final String WORKING_DIRECTORY = System.getProperty("user.dir"); - private JavetOSUtils() {} + private JavetOSUtils() { + } public static int getCPUCount() { return Runtime.getRuntime().availableProcessors(); diff --git a/src/main/java/com/caoccao/javet/utils/SimpleFreeMarkerFormat.java b/src/main/java/com/caoccao/javet/utils/SimpleFreeMarkerFormat.java index f8acb2124..09c1bc060 100644 --- a/src/main/java/com/caoccao/javet/utils/SimpleFreeMarkerFormat.java +++ b/src/main/java/com/caoccao/javet/utils/SimpleFreeMarkerFormat.java @@ -23,8 +23,8 @@ public final class SimpleFreeMarkerFormat { public static final String STRING_NULL = ""; private static final char CHAR_DOLLAR = '$'; - private static final char CHAR_VARIABLE_OPEN = '{'; private static final char CHAR_VARIABLE_CLOSE = '}'; + private static final char CHAR_VARIABLE_OPEN = '{'; public static String format(final String format, final Map parameters) { if (format == null || format.length() == 0 || parameters == null || parameters.isEmpty()) { diff --git a/src/main/java/com/caoccao/javet/utils/receivers/JavetCallbackReceiver.java b/src/main/java/com/caoccao/javet/utils/receivers/JavetCallbackReceiver.java index 8743a8d93..6a89146c3 100644 --- a/src/main/java/com/caoccao/javet/utils/receivers/JavetCallbackReceiver.java +++ b/src/main/java/com/caoccao/javet/utils/receivers/JavetCallbackReceiver.java @@ -49,11 +49,6 @@ public JavetCallbackReceiver(V8Runtime v8Runtime) { this.v8Runtime = v8Runtime; } - @Override - public V8Runtime getV8Runtime() { - return v8Runtime; - } - /** * Echo the given V8 value. * @@ -121,4 +116,9 @@ public String echoString(V8Value... args) { } return String.join(COMMA, stringList); } + + @Override + public V8Runtime getV8Runtime() { + return v8Runtime; + } } diff --git a/src/main/java/com/caoccao/javet/values/IV8Value.java b/src/main/java/com/caoccao/javet/values/IV8Value.java index b7dfd9e98..71fa65c8b 100644 --- a/src/main/java/com/caoccao/javet/values/IV8Value.java +++ b/src/main/java/com/caoccao/javet/values/IV8Value.java @@ -28,13 +28,6 @@ * The interface V8 value. */ public interface IV8Value extends IJavetClosable, IV8Cloneable { - /** - * Gets V8 runtime. - * - * @return the V8 runtime - */ - V8Runtime getV8Runtime(); - /** * Equals. *

@@ -46,6 +39,13 @@ public interface IV8Value extends IJavetClosable, IV8Cloneable { */ boolean equals(V8Value v8Value) throws JavetException; + /** + * Gets V8 runtime. + * + * @return the V8 runtime + */ + V8Runtime getV8Runtime(); + /** * Is null. * diff --git a/src/main/java/com/caoccao/javet/values/V8Value.java b/src/main/java/com/caoccao/javet/values/V8Value.java index d01d15c9e..c31583f1c 100644 --- a/src/main/java/com/caoccao/javet/values/V8Value.java +++ b/src/main/java/com/caoccao/javet/values/V8Value.java @@ -43,14 +43,13 @@ protected void checkV8Runtime() throws JavetException { @Override public abstract boolean equals(V8Value v8Value) throws JavetException; - @Override - @CheckReturnValue - public abstract T toClone() throws JavetException; - public V8Runtime getV8Runtime() { return v8Runtime; } + @Override + public abstract boolean sameValue(V8Value v8Value) throws JavetException; + public void setV8Runtime(V8Runtime v8Runtime) throws JavetException { Objects.requireNonNull(v8Runtime); if (this.v8Runtime != null) { @@ -60,8 +59,9 @@ public void setV8Runtime(V8Runtime v8Runtime) throws JavetException { } @Override - public abstract boolean sameValue(V8Value v8Value) throws JavetException; + public abstract boolean strictEquals(V8Value v8Value) throws JavetException; @Override - public abstract boolean strictEquals(V8Value v8Value) throws JavetException; + @CheckReturnValue + public abstract T toClone() throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8Module.java b/src/main/java/com/caoccao/javet/values/reference/IV8Module.java index 07a671329..fe42fa58c 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8Module.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8Module.java @@ -58,8 +58,6 @@ default T execute(boolean resultRequired) throws JavetExcept String getResourceName(); - void setResourceName(String resourceName); - /** * Gets script id. *

@@ -74,6 +72,8 @@ default T execute(boolean resultRequired) throws JavetExcept boolean instantiate() throws JavetException; + void setResourceName(String resourceName); + @Override default T toObject(V v8Value) throws JavetException { return getV8Runtime().toObject(v8Value); diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java index 00bbda07b..5bbba3de9 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java @@ -560,6 +560,18 @@ default Boolean invokeBoolean(String functionName, Object... objects) throws Jav return invokePrimitive(functionName, objects); } + /** + * Invoke double double. + * + * @param functionName the function name + * @param objects the objects + * @return the double + * @throws JavetException the javet exception + */ + default Double invokeDouble(String functionName, Object... objects) throws JavetException { + return invokePrimitive(functionName, objects); + } + /** * Invoke extended and return V8 value which must be consumed, * otherwise memory leak may occur. @@ -588,18 +600,6 @@ default Boolean invokeBoolean(String functionName, Object... objects) throws Jav @CheckReturnValue T invokeExtended(String functionName, boolean returnResult, V8Value... v8Values) throws JavetException; - /** - * Invoke double double. - * - * @param functionName the function name - * @param objects the objects - * @return the double - * @throws JavetException the javet exception - */ - default Double invokeDouble(String functionName, Object... objects) throws JavetException { - return invokePrimitive(functionName, objects); - } - /** * Invoke float float. * diff --git a/src/main/java/com/caoccao/javet/values/reference/V8Module.java b/src/main/java/com/caoccao/javet/values/reference/V8Module.java index e38f9cc96..4872b2e1b 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8Module.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8Module.java @@ -66,11 +66,6 @@ public String getResourceName() { return resourceName; } - public void setResourceName(String resourceName) { - Objects.requireNonNull(resourceName); - this.resourceName = resourceName; - } - @Override public int getScriptId() throws JavetException { checkV8Runtime(); @@ -94,6 +89,11 @@ public boolean instantiate() throws JavetException { return v8Runtime.moduleInstantiate(this); } + public void setResourceName(String resourceName) { + Objects.requireNonNull(resourceName); + this.resourceName = resourceName; + } + @Override @CheckReturnValue public V8Module toClone() throws JavetException { diff --git a/src/main/java/com/caoccao/javet/values/reference/V8Script.java b/src/main/java/com/caoccao/javet/values/reference/V8Script.java index 72f5c5b35..2441d219b 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8Script.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8Script.java @@ -18,9 +18,9 @@ package com.caoccao.javet.values.reference; import com.caoccao.javet.annotations.CheckReturnValue; +import com.caoccao.javet.enums.V8ValueReferenceType; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.V8Value; -import com.caoccao.javet.enums.V8ValueReferenceType; import java.util.Objects; @@ -44,16 +44,16 @@ public String getResourceName() { return resourceName; } - public void setResourceName(String resourceName) { - Objects.requireNonNull(resourceName); - this.resourceName = resourceName; - } - @Override public V8ValueReferenceType getType() { return V8ValueReferenceType.Script; } + public void setResourceName(String resourceName) { + Objects.requireNonNull(resourceName); + this.resourceName = resourceName; + } + @Override public V8Script toClone() throws JavetException { return this; diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java index c6734e8ae..c986ad73b 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java @@ -29,8 +29,8 @@ import java.util.Objects; public class V8ValueArray extends V8ValueObject implements IV8ValueArray { - protected static final String FUNCTION_NEXT = "next"; protected static final String FUNCTION_KEYS = "keys"; + protected static final String FUNCTION_NEXT = "next"; protected static final String FUNCTION_POP = "pop"; protected static final String FUNCTION_PUSH = "push"; protected static final String PROPERTY_DONE = "done"; diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueArrayBuffer.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueArrayBuffer.java index 516eedcac..c85b548c5 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueArrayBuffer.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueArrayBuffer.java @@ -17,19 +17,18 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.enums.V8ValueReferenceType; +import com.caoccao.javet.exceptions.JavetException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Objects; public class V8ValueArrayBuffer extends V8ValueObject { - protected static final String PROPERTY_BYTE_LENGTH = "byteLength"; protected static final int BYTE_LENGTH_1 = 1; protected static final int BYTE_LENGTH_2 = 2; protected static final int BYTE_LENGTH_3 = 3; - + protected static final String PROPERTY_BYTE_LENGTH = "byteLength"; protected ByteBuffer byteBuffer; protected ByteOrder byteOrder; @@ -39,28 +38,6 @@ public class V8ValueArrayBuffer extends V8ValueObject { byteOrder = ByteOrder.nativeOrder(); } - public ByteOrder getByteOrder() { - return byteOrder; - } - - public void setByteOrder(ByteOrder byteOrder) { - Objects.requireNonNull(byteOrder); - this.byteOrder = byteOrder; - } - - public ByteBuffer getByteBuffer() { - return byteBuffer; - } - - public int getByteLength() throws JavetException { - return getInteger(PROPERTY_BYTE_LENGTH); - } - - @Override - public V8ValueReferenceType getType() { - return V8ValueReferenceType.ArrayBuffer; - } - public boolean fromBytes(byte[] bytes) { if (bytes != null && bytes.length > 0 && bytes.length == byteBuffer.capacity()) { byteBuffer.put(bytes); @@ -109,6 +86,28 @@ public boolean fromShorts(short[] shorts) { return false; } + public ByteBuffer getByteBuffer() { + return byteBuffer; + } + + public int getByteLength() throws JavetException { + return getInteger(PROPERTY_BYTE_LENGTH); + } + + public ByteOrder getByteOrder() { + return byteOrder; + } + + @Override + public V8ValueReferenceType getType() { + return V8ValueReferenceType.ArrayBuffer; + } + + public void setByteOrder(ByteOrder byteOrder) { + Objects.requireNonNull(byteOrder); + this.byteOrder = byteOrder; + } + public byte[] toBytes() { byte[] bytes = new byte[byteBuffer.capacity()]; byteBuffer.get(bytes); diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueDataView.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueDataView.java index 4876cfb18..d50c10505 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueDataView.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueDataView.java @@ -21,30 +21,30 @@ import com.caoccao.javet.exceptions.JavetException; public class V8ValueDataView extends V8ValueObject { - /** - * The constant PROPERTY_BYTE_LENGTH. - */ - protected static final String PROPERTY_BYTE_LENGTH = "byteLength"; - /** - * The constant PROPERTY_BUFFER. - */ - protected static final String PROPERTY_BUFFER = "buffer"; - /** - * The constant PROPERTY_BYTE_OFFSET. - */ - protected static final String PROPERTY_BYTE_OFFSET = "byteOffset"; protected static final String FUNCTION_GET_BIG_INT_64 = "getBigInt64"; protected static final String FUNCTION_GET_FLOAT_32 = "getFloat32"; protected static final String FUNCTION_GET_FLOAT_64 = "getFloat64"; - protected static final String FUNCTION_GET_INT_8 = "getInt8"; protected static final String FUNCTION_GET_INT_16 = "getInt16"; protected static final String FUNCTION_GET_INT_32 = "getInt32"; + protected static final String FUNCTION_GET_INT_8 = "getInt8"; protected static final String FUNCTION_SET_BIG_INT_64 = "setBigInt64"; protected static final String FUNCTION_SET_FLOAT_32 = "setFloat32"; protected static final String FUNCTION_SET_FLOAT_64 = "setFloat64"; - protected static final String FUNCTION_SET_INT_8 = "setInt8"; protected static final String FUNCTION_SET_INT_16 = "setInt16"; protected static final String FUNCTION_SET_INT_32 = "setInt32"; + protected static final String FUNCTION_SET_INT_8 = "setInt8"; + /** + * The constant PROPERTY_BUFFER. + */ + protected static final String PROPERTY_BUFFER = "buffer"; + /** + * The constant PROPERTY_BYTE_LENGTH. + */ + protected static final String PROPERTY_BYTE_LENGTH = "byteLength"; + /** + * The constant PROPERTY_BYTE_OFFSET. + */ + protected static final String PROPERTY_BYTE_OFFSET = "byteOffset"; V8ValueDataView(long handle) { super(handle); @@ -135,14 +135,6 @@ public void setFloat64(int byteOffset, double value, boolean littleEndian) throw invokeVoid(FUNCTION_SET_FLOAT_64, byteOffset, value, littleEndian); } - public void setInt32(int byteOffset, int value) throws JavetException { - setInt32(byteOffset, value, true); - } - - public void setInt32(int byteOffset, int value, boolean littleEndian) throws JavetException { - invokeVoid(FUNCTION_SET_INT_32, byteOffset, value, littleEndian); - } - public void setInt16(int byteOffset, short value) throws JavetException { setInt16(byteOffset, value, true); } @@ -151,6 +143,14 @@ public void setInt16(int byteOffset, short value, boolean littleEndian) throws J invokeVoid(FUNCTION_SET_INT_16, byteOffset, value, littleEndian); } + public void setInt32(int byteOffset, int value) throws JavetException { + setInt32(byteOffset, value, true); + } + + public void setInt32(int byteOffset, int value, boolean littleEndian) throws JavetException { + invokeVoid(FUNCTION_SET_INT_32, byteOffset, value, littleEndian); + } + public void setInt8(int byteOffset, byte value) throws JavetException { invokeVoid(FUNCTION_SET_INT_8, byteOffset, value); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java index 7393ac70a..031c1a7d8 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java @@ -17,22 +17,17 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.enums.V8ValueReferenceType; +import com.caoccao.javet.exceptions.JavetException; public class V8ValueError extends V8ValueObject { - protected static final String STACK = "stack"; protected static final String MESSAGE = "message"; + protected static final String STACK = "stack"; V8ValueError(long handle) { super(handle); } - @Override - public V8ValueReferenceType getType() { - return V8ValueReferenceType.Error; - } - public String getMessage() throws JavetException { return getPropertyString(MESSAGE); } @@ -40,4 +35,9 @@ public String getMessage() throws JavetException { public String getStack() throws JavetException { return getPropertyString(STACK); } + + @Override + public V8ValueReferenceType getType() { + return V8ValueReferenceType.Error; + } } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java index 101eff76c..c09118e7b 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java @@ -37,42 +37,37 @@ public class V8ValueFunction extends V8ValueObject implements IV8ValueFunction { @Override @CheckReturnValue - public T callExtended(IV8ValueObject receiver, boolean returnResult, Object... objects) - throws JavetException { + public T callAsConstructor(Object... objects) throws JavetException { checkV8Runtime(); try (V8VirtualValueList virtualValueList = new V8VirtualValueList(v8Runtime, objects)) { - return v8Runtime.call(this, receiver, returnResult, virtualValueList.get()); + return v8Runtime.callAsConstructor(this, virtualValueList.get()); } } @Override @CheckReturnValue - public T callExtended(IV8ValueObject receiver, boolean returnResult, V8Value... v8Values) throws JavetException { + public T callAsConstructor(V8Value... v8Values) throws JavetException { checkV8Runtime(); v8Runtime.decorateV8Values(v8Values); - return v8Runtime.call(this, receiver, returnResult, v8Values); + return v8Runtime.callAsConstructor(this, v8Values); } @Override @CheckReturnValue - public T callAsConstructor(Object... objects) throws JavetException { + public T callExtended(IV8ValueObject receiver, boolean returnResult, Object... objects) + throws JavetException { checkV8Runtime(); try (V8VirtualValueList virtualValueList = new V8VirtualValueList(v8Runtime, objects)) { - return v8Runtime.callAsConstructor(this, virtualValueList.get()); + return v8Runtime.call(this, receiver, returnResult, virtualValueList.get()); } } @Override @CheckReturnValue - public T callAsConstructor(V8Value... v8Values) throws JavetException { + public T callExtended(IV8ValueObject receiver, boolean returnResult, V8Value... v8Values) throws JavetException { checkV8Runtime(); v8Runtime.decorateV8Values(v8Values); - return v8Runtime.callAsConstructor(this, v8Values); - } - - @Override - public V8ValueReferenceType getType() { - return V8ValueReferenceType.Function; + return v8Runtime.call(this, receiver, returnResult, v8Values); } @Override @@ -106,6 +101,11 @@ public String getSourceCode() throws JavetException { return null; } + @Override + public V8ValueReferenceType getType() { + return V8ValueReferenceType.Function; + } + @Override public boolean setSourceCode(String sourceCodeString) throws JavetException { checkV8Runtime(); diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueIterator.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueIterator.java index ef5d70434..c6befc8f3 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueIterator.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueIterator.java @@ -18,9 +18,9 @@ package com.caoccao.javet.values.reference; import com.caoccao.javet.annotations.CheckReturnValue; +import com.caoccao.javet.enums.V8ValueReferenceType; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.V8Value; -import com.caoccao.javet.enums.V8ValueReferenceType; public class V8ValueIterator extends V8ValueObject implements IV8ValueIterator { protected static final String FUNCTION_NEXT = "next"; @@ -31,11 +31,6 @@ public class V8ValueIterator extends V8ValueObject implements super(handle); } - @Override - public V8ValueReferenceType getType() { - return V8ValueReferenceType.Iterator; - } - @Override @CheckReturnValue public T getNext() { @@ -47,4 +42,9 @@ public T getNext() { } return null; } + + @Override + public V8ValueReferenceType getType() { + return V8ValueReferenceType.Iterator; + } } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java index dd63bcfb1..b384e1bcf 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java @@ -30,9 +30,9 @@ @SuppressWarnings("unchecked") public class V8ValueMap extends V8ValueObject implements IV8ValueMap { + protected static final String FUNCTION_ENTRIES = "entries"; protected static final String FUNCTION_KEYS = "keys"; protected static final String FUNCTION_VALUES = "values"; - protected static final String FUNCTION_ENTRIES = "entries"; V8ValueMap(long handle) { super(handle); diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java index 8a9345a39..fc235258d 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java @@ -46,8 +46,8 @@ public class V8ValueObject extends V8ValueReference implements IV8ValueObject { protected static final String FUNCTION_GET = "get"; protected static final String FUNCTION_HAS = "has"; protected static final String FUNCTION_SET = "set"; - protected static final String METHOD_PREFIX_IS = "is"; protected static final String METHOD_PREFIX_GET = "get"; + protected static final String METHOD_PREFIX_IS = "is"; protected static final String METHOD_PREFIX_SET = "set"; protected V8ValueObject(long handle) { @@ -292,18 +292,18 @@ public IV8ValueArray getOwnPropertyNames() throws JavetException { @Override @CheckReturnValue - public IV8ValueArray getPropertyNames() throws JavetException { + public T getProperty(Object key) throws JavetException { checkV8Runtime(); - return v8Runtime.getPropertyNames(this); + try (V8VirtualValue virtualKey = new V8VirtualValue(v8Runtime, key)) { + return v8Runtime.getProperty(this, virtualKey.get()); + } } @Override @CheckReturnValue - public T getProperty(Object key) throws JavetException { + public IV8ValueArray getPropertyNames() throws JavetException { checkV8Runtime(); - try (V8VirtualValue virtualKey = new V8VirtualValue(v8Runtime, key)) { - return v8Runtime.getProperty(this, virtualKey.get()); - } + return v8Runtime.getPropertyNames(this); } @Override @@ -395,22 +395,22 @@ public boolean strictEquals(V8Value v8Value) throws JavetException { } @Override - public String toProtoString() { + public String toJsonString() { try { checkV8Runtime(); - return v8Runtime.toProtoString(this); + try (V8ValueBuiltInJson v8ValueBuiltInJson = v8Runtime.getGlobalObject().getJson()) { + return v8ValueBuiltInJson.stringify(this); + } } catch (JavetException e) { return e.getMessage(); } } @Override - public String toJsonString() { + public String toProtoString() { try { checkV8Runtime(); - try (V8ValueBuiltInJson v8ValueBuiltInJson = v8Runtime.getGlobalObject().getJson()) { - return v8ValueBuiltInJson.stringify(this); - } + return v8Runtime.toProtoString(this); } catch (JavetException e) { return e.getMessage(); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java b/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java index 81dc79b45..98c5cd223 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java @@ -18,9 +18,9 @@ package com.caoccao.javet.values.reference; import com.caoccao.javet.annotations.CheckReturnValue; +import com.caoccao.javet.enums.V8ValueReferenceType; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.values.V8Value; -import com.caoccao.javet.enums.V8ValueReferenceType; public class V8ValuePromise extends V8ValueObject implements IV8ValuePromise { @@ -68,7 +68,7 @@ public void markAsHandled() throws JavetException { @Override @CheckReturnValue public V8ValuePromise then(IV8ValueFunction functionFulfilled, IV8ValueFunction functionRejected) - throws JavetException{ + throws JavetException { checkV8Runtime(); return v8Runtime.promiseThen(this, functionFulfilled, functionRejected); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java index f61b30648..d90ee6635 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java @@ -88,14 +88,14 @@ public boolean equals(V8Value v8Value) throws JavetException { return v8Runtime.equals(this, v8ValueReference); } - @Override - public abstract V8ValueReferenceType getType(); - @Override public long getHandle() { return handle; } + @Override + public abstract V8ValueReferenceType getType(); + @Override public boolean isWeak() { return weak; diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java index b18bdf348..d5c5fb600 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java @@ -89,11 +89,6 @@ public IV8ValueIterator getEntries() throws JavetException { return invoke(FUNCTION_ENTRIES); } - @Override - public V8ValueReferenceType getType() { - return V8ValueReferenceType.Set; - } - @Override @CheckReturnValue public IV8ValueIterator getKeys() throws JavetException { @@ -107,4 +102,9 @@ public int getSize() throws JavetException { return v8Runtime.getSize(this); } + @Override + public V8ValueReferenceType getType() { + return V8ValueReferenceType.Set; + } + } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java index ec7226960..d3afda98b 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java @@ -17,8 +17,8 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.enums.V8ValueReferenceType; +import com.caoccao.javet.exceptions.JavetException; import java.text.MessageFormat; @@ -31,15 +31,15 @@ public class V8ValueSymbol extends V8ValueObject { super(handle); } + public String getDescription() throws JavetException { + return getPropertyString(PROPERTY_DESCRIPTION); + } + @Override public V8ValueReferenceType getType() { return V8ValueReferenceType.Symbol; } - public String getDescription() throws JavetException { - return getPropertyString(PROPERTY_DESCRIPTION); - } - @Override public String toString() { try { diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueTypedArray.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueTypedArray.java index 533dd8cd4..86ebe4ff1 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueTypedArray.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueTypedArray.java @@ -18,8 +18,8 @@ package com.caoccao.javet.values.reference; import com.caoccao.javet.annotations.CheckReturnValue; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.enums.V8ValueReferenceType; +import com.caoccao.javet.exceptions.JavetException; import java.util.Objects; @@ -110,77 +110,6 @@ public class V8ValueTypedArray extends V8ValueObject implements IV8ValueTypedArr setType(V8ValueReferenceType.parse(type)); } - @Override - @CheckReturnValue - public V8ValueArrayBuffer getBuffer() throws JavetException { - return get(PROPERTY_BUFFER); - } - - @Override - public int getByteLength() throws JavetException { - return getInteger(PROPERTY_BYTE_LENGTH); - } - - @Override - public int getByteOffset() throws JavetException { - return getInteger(PROPERTY_BYTE_OFFSET); - } - - @Override - public int getLength() throws JavetException { - checkV8Runtime(); - return v8Runtime.getLength(this); - } - - @Override - public int getSizeInBytes() { - return sizeInBytes; - } - - @Override - public V8ValueReferenceType getType() { - return type; - } - - /** - * Sets type. - * - * @param type the type - */ - protected void setType(V8ValueReferenceType type) { - switch (type) { - case Int8Array: - case Uint8Array: - case Uint8ClampedArray: - sizeInBytes = ONE_BYTE_PER_VALUE; - break; - case Int16Array: - case Uint16Array: - sizeInBytes = TWO_BYTES_PER_VALUE; - break; - case Int32Array: - case Uint32Array: - case Float32Array: - sizeInBytes = FOUR_BYTES_PER_VALUE; - break; - case Float64Array: - case BigInt64Array: - case BigUint64Array: - sizeInBytes = EIGHT_BYTES_PER_VALUE; - break; - default: - type = V8ValueReferenceType.Invalid; - sizeInBytes = ZERO_BYTE_PER_VALUE; - break; - } - this.type = type; - } - - @Override - public boolean isValid() { - return type != V8ValueReferenceType.Invalid; - } - /** * From byte array. * @@ -288,6 +217,77 @@ public boolean fromShorts(short[] shorts) throws JavetException { return false; } + @Override + @CheckReturnValue + public V8ValueArrayBuffer getBuffer() throws JavetException { + return get(PROPERTY_BUFFER); + } + + @Override + public int getByteLength() throws JavetException { + return getInteger(PROPERTY_BYTE_LENGTH); + } + + @Override + public int getByteOffset() throws JavetException { + return getInteger(PROPERTY_BYTE_OFFSET); + } + + @Override + public int getLength() throws JavetException { + checkV8Runtime(); + return v8Runtime.getLength(this); + } + + @Override + public int getSizeInBytes() { + return sizeInBytes; + } + + @Override + public V8ValueReferenceType getType() { + return type; + } + + @Override + public boolean isValid() { + return type != V8ValueReferenceType.Invalid; + } + + /** + * Sets type. + * + * @param type the type + */ + protected void setType(V8ValueReferenceType type) { + switch (type) { + case Int8Array: + case Uint8Array: + case Uint8ClampedArray: + sizeInBytes = ONE_BYTE_PER_VALUE; + break; + case Int16Array: + case Uint16Array: + sizeInBytes = TWO_BYTES_PER_VALUE; + break; + case Int32Array: + case Uint32Array: + case Float32Array: + sizeInBytes = FOUR_BYTES_PER_VALUE; + break; + case Float64Array: + case BigInt64Array: + case BigUint64Array: + sizeInBytes = EIGHT_BYTES_PER_VALUE; + break; + default: + type = V8ValueReferenceType.Invalid; + sizeInBytes = ZERO_BYTE_PER_VALUE; + break; + } + this.type = type; + } + /** * To byte array. * @@ -306,30 +306,30 @@ public byte[] toBytes() throws JavetException { } /** - * To float array. + * To double array. * - * @return the float array + * @return the double array * @throws JavetException the javet exception */ - public float[] toFloats() throws JavetException { - if (getType() == V8ValueReferenceType.Float32Array) { + public double[] toDoubles() throws JavetException { + if (getType() == V8ValueReferenceType.Float64Array) { try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { - return v8ValueArrayBuffer.toFloats(); + return v8ValueArrayBuffer.toDoubles(); } } return null; } /** - * To double array. + * To float array. * - * @return the double array + * @return the float array * @throws JavetException the javet exception */ - public double[] toDoubles() throws JavetException { - if (getType() == V8ValueReferenceType.Float64Array) { + public float[] toFloats() throws JavetException { + if (getType() == V8ValueReferenceType.Float32Array) { try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { - return v8ValueArrayBuffer.toDoubles(); + return v8ValueArrayBuffer.toFloats(); } } return null; diff --git a/src/main/java/com/caoccao/javet/values/reference/builtin/V8ValueBuiltInPromise.java b/src/main/java/com/caoccao/javet/values/reference/builtin/V8ValueBuiltInPromise.java index 4074dcb3a..d9acafc13 100644 --- a/src/main/java/com/caoccao/javet/values/reference/builtin/V8ValueBuiltInPromise.java +++ b/src/main/java/com/caoccao/javet/values/reference/builtin/V8ValueBuiltInPromise.java @@ -45,11 +45,6 @@ public V8ValuePromise all(V8Value v8Value) throws JavetException { return invoke(FUNCTION_ALL, v8Value); } - public void allVoid(V8Value v8Value) throws JavetException { - Objects.requireNonNull(v8Value); - invokeVoid(FUNCTION_ALL, v8Value); - } - @CheckReturnValue public V8ValuePromise allSettled(V8Value v8Value) throws JavetException { Objects.requireNonNull(v8Value); @@ -61,6 +56,11 @@ public void allSettledVoid(V8Value v8Value) throws JavetException { invokeVoid(FUNCTION_ALL_SETTLED, v8Value); } + public void allVoid(V8Value v8Value) throws JavetException { + Objects.requireNonNull(v8Value); + invokeVoid(FUNCTION_ALL, v8Value); + } + @CheckReturnValue public V8ValuePromise any(V8Value v8Value) throws JavetException { Objects.requireNonNull(v8Value); @@ -83,20 +83,15 @@ public void raceVoid(V8Value v8Value) throws JavetException { invokeVoid(FUNCTION_RACE, v8Value); } - public void rejectVoid(V8Value v8Value) throws JavetException { - Objects.requireNonNull(v8Value); - invokeVoid(FUNCTION_REJECT, v8Value); - } - @CheckReturnValue public V8ValuePromise reject(V8Value v8Value) throws JavetException { Objects.requireNonNull(v8Value); return invoke(FUNCTION_REJECT, v8Value); } - public void resolveVoid(V8Value v8Value) throws JavetException { + public void rejectVoid(V8Value v8Value) throws JavetException { Objects.requireNonNull(v8Value); - invokeVoid(FUNCTION_RESOLVE, v8Value); + invokeVoid(FUNCTION_REJECT, v8Value); } @CheckReturnValue @@ -105,6 +100,11 @@ public V8ValuePromise resolve(V8Value v8Value) throws JavetException { return invoke(FUNCTION_RESOLVE, v8Value); } + public void resolveVoid(V8Value v8Value) throws JavetException { + Objects.requireNonNull(v8Value); + invokeVoid(FUNCTION_RESOLVE, v8Value); + } + @Override public V8ValueBuiltInPromise toClone() throws JavetException { return this; diff --git a/src/main/java/com/caoccao/javet/values/virtual/V8VirtualValueList.java b/src/main/java/com/caoccao/javet/values/virtual/V8VirtualValueList.java index 009cbc8e2..5a4ad84c5 100644 --- a/src/main/java/com/caoccao/javet/values/virtual/V8VirtualValueList.java +++ b/src/main/java/com/caoccao/javet/values/virtual/V8VirtualValueList.java @@ -20,7 +20,6 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.interfaces.IJavetClosable; import com.caoccao.javet.interop.V8Runtime; -import com.caoccao.javet.interop.converters.IJavetConverter; import com.caoccao.javet.utils.JavetResourceUtils; import com.caoccao.javet.values.V8Value; diff --git a/src/test/java/com/caoccao/javet/BaseTestJavet.java b/src/test/java/com/caoccao/javet/BaseTestJavet.java index b2c52ccd2..0fd2a2bd1 100644 --- a/src/test/java/com/caoccao/javet/BaseTestJavet.java +++ b/src/test/java/com/caoccao/javet/BaseTestJavet.java @@ -17,8 +17,8 @@ package com.caoccao.javet; -import com.caoccao.javet.interfaces.IJavetLogger; import com.caoccao.javet.enums.JSRuntimeType; +import com.caoccao.javet.interfaces.IJavetLogger; import com.caoccao.javet.interop.JavetLibLoader; import com.caoccao.javet.interop.V8Flags; import com.caoccao.javet.interop.V8Host; diff --git a/src/test/java/com/caoccao/javet/BaseTestJavetPool.java b/src/test/java/com/caoccao/javet/BaseTestJavetPool.java index aab5fa586..ef2ff8b0b 100644 --- a/src/test/java/com/caoccao/javet/BaseTestJavetPool.java +++ b/src/test/java/com/caoccao/javet/BaseTestJavetPool.java @@ -27,13 +27,6 @@ public abstract class BaseTestJavetPool extends BaseTestJavet { protected IJavetEnginePool javetEnginePool; - @BeforeEach - public void beforeEach() { - javetEnginePool = new JavetEnginePool(); - javetEnginePool.getConfig().setEngineGuardCheckIntervalMillis(1); - javetEnginePool.getConfig().setJSRuntimeType(v8Host.getJSRuntimeType()); - } - @AfterEach public void afterEach() throws JavetException { javetEnginePool.close(); @@ -41,4 +34,11 @@ public void afterEach() throws JavetException { V8Host.getV8Instance().clearInternalStatistic(); } + @BeforeEach + public void beforeEach() { + javetEnginePool = new JavetEnginePool(); + javetEnginePool.getConfig().setEngineGuardCheckIntervalMillis(1); + javetEnginePool.getConfig().setJSRuntimeType(v8Host.getJSRuntimeType()); + } + } diff --git a/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java b/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java index fb18a2173..0a87224f2 100644 --- a/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java +++ b/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java @@ -38,7 +38,7 @@ public void afterEach() throws JavetException { NodeRuntime nodeRuntime = (NodeRuntime) v8Runtime; assertEquals(nodeRuntime.getNodeModuleCount(), nodeRuntime.getReferenceCount(), "Reference count should be the node module count after test case is ended."); - }else { + } else { assertEquals(0, v8Runtime.getReferenceCount(), "Reference count should be 0 after test case is ended."); } diff --git a/src/test/java/com/caoccao/javet/interop/TestNodeRuntime.java b/src/test/java/com/caoccao/javet/interop/TestNodeRuntime.java index 8f7253842..d8d93ef80 100644 --- a/src/test/java/com/caoccao/javet/interop/TestNodeRuntime.java +++ b/src/test/java/com/caoccao/javet/interop/TestNodeRuntime.java @@ -34,8 +34,8 @@ import static org.junit.jupiter.api.Assertions.*; public class TestNodeRuntime extends BaseTestJavet { - protected NodeRuntime nodeRuntime; protected NodeModuleProcess nodeModuleProcess; + protected NodeRuntime nodeRuntime; public TestNodeRuntime() { super(JSRuntimeType.Node); @@ -103,6 +103,13 @@ public void testModuleProcess() throws JavetException { assertEquals(path1.toAbsolutePath().toString(), path4.toAbsolutePath().toString()); } + @Test + public void testModuleVM() throws JavetException { + internalTest( + "test-node-module-vm.js", + "[{\"a\":\"x\",\"b\":3},\"undefined\",\"undefined\"]"); + } + @Test public void testSqlite3() throws JavetException { File sqlite3File = getScriptFile("../node_modules/sqlite3/sqlite3.js"); @@ -123,11 +130,4 @@ public void testTimers() throws JavetException { "setTimeout() has been executed after await()."); } } - - @Test - public void testModuleVM() throws JavetException { - internalTest( - "test-node-module-vm.js", - "[{\"a\":\"x\",\"b\":3},\"undefined\",\"undefined\"]"); - } } diff --git a/src/test/java/com/caoccao/javet/interop/TestV8Host.java b/src/test/java/com/caoccao/javet/interop/TestV8Host.java index fcab97eee..ae5b7be80 100644 --- a/src/test/java/com/caoccao/javet/interop/TestV8Host.java +++ b/src/test/java/com/caoccao/javet/interop/TestV8Host.java @@ -39,16 +39,16 @@ public void testBothNodeAndV8() throws JavetException { } @Test - public void testCreateV8RuntimeWithoutGlobalName() throws JavetException { - try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { + public void testCreateV8RuntimeWithGlobalName() throws JavetException { + try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { assertNotNull(v8Runtime); assertTrue(v8Host.isIsolateCreated()); } } @Test - public void testCreateV8RuntimeWithGlobalName() throws JavetException { - try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { + public void testCreateV8RuntimeWithoutGlobalName() throws JavetException { + try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { assertNotNull(v8Runtime); assertTrue(v8Host.isIsolateCreated()); } diff --git a/src/test/java/com/caoccao/javet/interop/TestV8Inspector.java b/src/test/java/com/caoccao/javet/interop/TestV8Inspector.java index 209ea88ef..c5d3a9215 100644 --- a/src/test/java/com/caoccao/javet/interop/TestV8Inspector.java +++ b/src/test/java/com/caoccao/javet/interop/TestV8Inspector.java @@ -96,6 +96,7 @@ class MockV8InspectorListener implements IV8InspectorListener { private List notifications; private List requests; private List responses; + public MockV8InspectorListener() { contextGroupIds = new ArrayList<>(); notifications = new ArrayList<>(); diff --git a/src/test/java/com/caoccao/javet/interop/converters/TestJavetCustomConverter.java b/src/test/java/com/caoccao/javet/interop/converters/TestJavetCustomConverter.java index c48940d13..660efc6d3 100644 --- a/src/test/java/com/caoccao/javet/interop/converters/TestJavetCustomConverter.java +++ b/src/test/java/com/caoccao/javet/interop/converters/TestJavetCustomConverter.java @@ -118,14 +118,14 @@ public String getName() { return name; } - public void setName(String name) { - this.name = name; - } - public String getValue() { return value; } + public void setName(String name) { + this.name = name; + } + public void setValue(String value) { this.value = value; } diff --git a/src/test/java/com/caoccao/javet/interop/converters/TestJavetObjectConverter.java b/src/test/java/com/caoccao/javet/interop/converters/TestJavetObjectConverter.java index 4c12765df..1fa4f3527 100644 --- a/src/test/java/com/caoccao/javet/interop/converters/TestJavetObjectConverter.java +++ b/src/test/java/com/caoccao/javet/interop/converters/TestJavetObjectConverter.java @@ -193,24 +193,6 @@ public void testTypedArrayFloatArray() throws JavetException { } } - @Test - public void testTypedArrayLongArray() throws JavetException { - IJavetConverter converter = new JavetObjectConverter(); - long[] longs = new long[]{1L, 2L, 3L}; - try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, longs)) { - assertEquals(longs.length, v8ValueTypedArray.getLength()); - assertEquals(longs.length * v8ValueTypedArray.getSizeInBytes(), v8ValueTypedArray.getByteLength()); - assertEquals(0, v8ValueTypedArray.getByteOffset()); - assertArrayEquals(longs, v8ValueTypedArray.toLongs()); - } - try (V8ValueTypedArray v8ValueTypedArray = - v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.BigInt64Array, longs.length)) { - assertTrue(v8ValueTypedArray.fromLongs(longs)); - long[] newLongs = (long[]) converter.toObject(v8ValueTypedArray); - assertArrayEquals(longs, newLongs); - } - } - @Test public void testTypedArrayIntegerArray() throws JavetException { IJavetConverter converter = new JavetObjectConverter(); @@ -229,6 +211,24 @@ public void testTypedArrayIntegerArray() throws JavetException { } } + @Test + public void testTypedArrayLongArray() throws JavetException { + IJavetConverter converter = new JavetObjectConverter(); + long[] longs = new long[]{1L, 2L, 3L}; + try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, longs)) { + assertEquals(longs.length, v8ValueTypedArray.getLength()); + assertEquals(longs.length * v8ValueTypedArray.getSizeInBytes(), v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertArrayEquals(longs, v8ValueTypedArray.toLongs()); + } + try (V8ValueTypedArray v8ValueTypedArray = + v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.BigInt64Array, longs.length)) { + assertTrue(v8ValueTypedArray.fromLongs(longs)); + long[] newLongs = (long[]) converter.toObject(v8ValueTypedArray); + assertArrayEquals(longs, newLongs); + } + } + @Test public void testTypedArrayShortArray() throws JavetException { IJavetConverter converter = new JavetObjectConverter(); diff --git a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java index d6a99ca8f..43108326c 100644 --- a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java +++ b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java @@ -30,21 +30,6 @@ import static org.junit.jupiter.api.Assertions.*; public class TestJavetEngineGuard extends BaseTestJavetPool { - @Test - public void testWithoutTermination() throws JavetException { - final long timeoutMillis = 10000; - ZonedDateTime startZonedDateTime = JavetDateTimeUtils.getUTCNow(); - try (IJavetEngine iJavetEngine = javetEnginePool.getEngine()) { - try (IJavetEngineGuard iJavetEngineGuard = iJavetEngine.getGuard(timeoutMillis)) { - V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); - assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); - } - } - ZonedDateTime endZonedDateTime = JavetDateTimeUtils.getUTCNow(); - Duration duration = Duration.between(startZonedDateTime, endZonedDateTime); - assertTrue(duration.toMillis() < timeoutMillis); - } - @Test public void testTermination() throws JavetException { // Get an engine from the pool as usual. @@ -62,4 +47,19 @@ public void testTermination() throws JavetException { "The V8 runtime is not dead and still be able to execute code afterwards."); } } + + @Test + public void testWithoutTermination() throws JavetException { + final long timeoutMillis = 10000; + ZonedDateTime startZonedDateTime = JavetDateTimeUtils.getUTCNow(); + try (IJavetEngine iJavetEngine = javetEnginePool.getEngine()) { + try (IJavetEngineGuard iJavetEngineGuard = iJavetEngine.getGuard(timeoutMillis)) { + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + } + } + ZonedDateTime endZonedDateTime = JavetDateTimeUtils.getUTCNow(); + Duration duration = Duration.between(startZonedDateTime, endZonedDateTime); + assertTrue(duration.toMillis() < timeoutMillis); + } } diff --git a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java index bc89fe170..433cd4810 100644 --- a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java +++ b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java @@ -35,8 +35,8 @@ public class TestJavetEnginePool extends BaseTestJavet { public static final int TEST_POOL_DAEMON_CHECK_INTERVAL_MILLIS = 1; public static final int TEST_MAX_TIMEOUT = 1000; - private JavetEnginePool javetEnginePool; private JavetEngineConfig javetEngineConfig; + private JavetEnginePool javetEnginePool; @AfterEach private void afterEach() throws JavetException { @@ -54,27 +54,6 @@ private void beforeEach() { javetEngineConfig.setPoolDaemonCheckIntervalMillis(TEST_POOL_DAEMON_CHECK_INTERVAL_MILLIS); } - @Test - public void testSingleThreadedExecution() throws Exception { - assertEquals(0, javetEnginePool.getIdleEngineCount()); - assertEquals(0, javetEnginePool.getActiveEngineCount()); - try (IJavetEngine engine = javetEnginePool.getEngine()) { - assertEquals(0, javetEnginePool.getIdleEngineCount()); - assertEquals(1, javetEnginePool.getActiveEngineCount()); - V8Runtime v8Runtime = engine.getV8Runtime(); - assertTrue(v8Runtime.isPooled()); - assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); - v8Runtime.close(); // close() doesn't take effect because the V8 runtime is managed by pool - assertEquals(4, v8Runtime.getExecutor("2 + 2").executeInteger()); - assertThrows(JavetExecutionException.class, () -> { - v8Runtime.getExecutor("eval('1');").executeVoid(); - }, "By default, the engine pool should disallow eval()."); - } - runAndWait(TEST_MAX_TIMEOUT, () -> javetEnginePool.getIdleEngineCount() == 1); - assertEquals(1, javetEnginePool.getIdleEngineCount()); - assertEquals(0, javetEnginePool.getActiveEngineCount()); - } - @Test public void testMultiThreadedExecutionBelowMaxSize() throws Exception { assertEquals(0, javetEnginePool.getIdleEngineCount()); @@ -166,4 +145,25 @@ public void testMultiThreadedExecutionExceedsMaxSize() throws Exception { assertEquals(0, failureCount.get()); runAndWait(TEST_MAX_TIMEOUT, () -> 0 == javetEnginePool.getActiveEngineCount()); } + + @Test + public void testSingleThreadedExecution() throws Exception { + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + try (IJavetEngine engine = javetEnginePool.getEngine()) { + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(1, javetEnginePool.getActiveEngineCount()); + V8Runtime v8Runtime = engine.getV8Runtime(); + assertTrue(v8Runtime.isPooled()); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + v8Runtime.close(); // close() doesn't take effect because the V8 runtime is managed by pool + assertEquals(4, v8Runtime.getExecutor("2 + 2").executeInteger()); + assertThrows(JavetExecutionException.class, () -> { + v8Runtime.getExecutor("eval('1');").executeVoid(); + }, "By default, the engine pool should disallow eval()."); + } + runAndWait(TEST_MAX_TIMEOUT, () -> javetEnginePool.getIdleEngineCount() == 1); + assertEquals(1, javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + } } diff --git a/src/test/java/com/caoccao/javet/interop/engine/TestPerformance.java b/src/test/java/com/caoccao/javet/interop/engine/TestPerformance.java index 158d33e53..7b63746cc 100644 --- a/src/test/java/com/caoccao/javet/interop/engine/TestPerformance.java +++ b/src/test/java/com/caoccao/javet/interop/engine/TestPerformance.java @@ -73,47 +73,6 @@ public void testAdHocContextAnd1Thread() throws Exception { updateDoc(prefix + "Ad-hoc Context with 1 Thread", tps); } - @Test - @Tag("performance") - public void testAdHocIsolateAnd1Thread() throws Exception { - final int iterations = jsRuntimeType.isNode() ? 200 : 10000; - String codeString = "1 + 1"; - final long startTime = System.currentTimeMillis(); - try (IJavetEngine javetEngine = javetEnginePool.getEngine()) { - V8Runtime v8Runtime = javetEngine.getV8Runtime(); - IV8Executor v8Executor = v8Runtime.getExecutor(codeString); - for (int i = 0; i < iterations; i++) { - javetEngine.resetIsolate(); - assertEquals(2, v8Executor.executeInteger()); - } - } - final long stopTime = System.currentTimeMillis(); - final long tps = iterations * 1000 / (stopTime - startTime); - logger.logInfo(prefix + "Ad-hoc Isolate with 1 Thread: {0}", tps); - updateDoc(prefix + "Ad-hoc Isolate with 1 Thread", tps); - } - - @Test - @Tag("performance") - public void testSingleContextAnd1Thread() throws Exception { - final int iterations = 2000000; - String codeString = "1 + 1"; - final long startTime = System.currentTimeMillis(); - try (IJavetEngine javetEngine = javetEnginePool.getEngine()) { - V8Runtime v8Runtime = javetEngine.getV8Runtime(); - try (V8Locker v8Locker = v8Runtime.getV8Locker()) { - IV8Executor v8Executor = v8Runtime.getExecutor(codeString); - for (int i = 0; i < iterations; i++) { - assertEquals(2, v8Executor.executeInteger()); - } - } - } - final long stopTime = System.currentTimeMillis(); - final long tps = iterations * 1000 / (stopTime - startTime); - logger.logInfo(prefix + "Single Context with 1 Thread: {0}", tps); - updateDoc(prefix + "Single Context with 1 Thread", tps); - } - @Test @Tag("performance") public void testAdHocContextAnd8Threads() throws Exception { @@ -148,6 +107,26 @@ public void testAdHocContextAnd8Threads() throws Exception { updateDoc(prefix + "Ad-hoc Context with 8 Threads", tps); } + @Test + @Tag("performance") + public void testAdHocIsolateAnd1Thread() throws Exception { + final int iterations = jsRuntimeType.isNode() ? 200 : 10000; + String codeString = "1 + 1"; + final long startTime = System.currentTimeMillis(); + try (IJavetEngine javetEngine = javetEnginePool.getEngine()) { + V8Runtime v8Runtime = javetEngine.getV8Runtime(); + IV8Executor v8Executor = v8Runtime.getExecutor(codeString); + for (int i = 0; i < iterations; i++) { + javetEngine.resetIsolate(); + assertEquals(2, v8Executor.executeInteger()); + } + } + final long stopTime = System.currentTimeMillis(); + final long tps = iterations * 1000 / (stopTime - startTime); + logger.logInfo(prefix + "Ad-hoc Isolate with 1 Thread: {0}", tps); + updateDoc(prefix + "Ad-hoc Isolate with 1 Thread", tps); + } + @Test @Tag("performance") public void testAdHocIsolateAnd8Threads() throws Exception { @@ -182,6 +161,27 @@ public void testAdHocIsolateAnd8Threads() throws Exception { updateDoc(prefix + "Ad-hoc Isolate with 8 Threads", tps); } + @Test + @Tag("performance") + public void testSingleContextAnd1Thread() throws Exception { + final int iterations = 2000000; + String codeString = "1 + 1"; + final long startTime = System.currentTimeMillis(); + try (IJavetEngine javetEngine = javetEnginePool.getEngine()) { + V8Runtime v8Runtime = javetEngine.getV8Runtime(); + try (V8Locker v8Locker = v8Runtime.getV8Locker()) { + IV8Executor v8Executor = v8Runtime.getExecutor(codeString); + for (int i = 0; i < iterations; i++) { + assertEquals(2, v8Executor.executeInteger()); + } + } + } + final long stopTime = System.currentTimeMillis(); + final long tps = iterations * 1000 / (stopTime - startTime); + logger.logInfo(prefix + "Single Context with 1 Thread: {0}", tps); + updateDoc(prefix + "Single Context with 1 Thread", tps); + } + @Test @Tag("performance") public void testSingleContextAnd8Threads() throws Exception { diff --git a/src/test/java/com/caoccao/javet/mock/MockAnnotationBasedCallbackReceiver.java b/src/test/java/com/caoccao/javet/mock/MockAnnotationBasedCallbackReceiver.java index a37947e7a..944e43134 100644 --- a/src/test/java/com/caoccao/javet/mock/MockAnnotationBasedCallbackReceiver.java +++ b/src/test/java/com/caoccao/javet/mock/MockAnnotationBasedCallbackReceiver.java @@ -32,8 +32,8 @@ public class MockAnnotationBasedCallbackReceiver { private AtomicInteger count; - private V8Runtime v8Runtime; private String stringValue; + private V8Runtime v8Runtime; public MockAnnotationBasedCallbackReceiver() { count = new AtomicInteger(0); @@ -47,36 +47,6 @@ public static String staticEcho(String str) { return str; } - @V8Property - public Integer getIntegerValue() { - count.incrementAndGet(); - return 123; - } - - @V8Property - public String getStringValue() { - count.incrementAndGet(); - return stringValue; - } - - @V8Property(thisObjectRequired = true) - public String getStringValueWithThis(V8ValueObject thisObject) throws JavetException { - count.incrementAndGet(); - return thisObject.getString("stringValue"); - } - - @V8Property - public void setStringValue(String stringValue) { - count.incrementAndGet(); - this.stringValue = stringValue; - } - - @V8Property(thisObjectRequired = true) - public void setStringValueWithThis(V8ValueObject thisObject, String stringValue) throws JavetException { - count.incrementAndGet(); - thisObject.set("stringValue", stringValue); - } - @V8Function public Integer contextScope(V8ValueFunction v8ValueFunction) throws JavetException { assertTrue(v8ValueFunction.getJSFunctionType().isUserDefined()); @@ -96,13 +66,6 @@ public String echo(String str) { return str; } - // Instance method with different name and same signature. - @V8Function(name = "add") - public Integer mathAdd(Integer a, Integer b) { - count.incrementAndGet(); - return a + b; - } - // Instance method with converter for non-primitive objects. @V8Function(name = "generateArrayWithConverter") public Object[] generateArrayWithConverter() throws JavetException { @@ -121,6 +84,35 @@ public V8ValueArray generateArrayWithoutConverter() throws JavetException { return v8ValueArray; } + public int getCount() { + return count.get(); + } + + @V8Property + public Integer getIntegerValue() { + count.incrementAndGet(); + return 123; + } + + @V8Property + public String getStringValue() { + count.incrementAndGet(); + return stringValue; + } + + @V8Property(thisObjectRequired = true) + public String getStringValueWithThis(V8ValueObject thisObject) throws JavetException { + count.incrementAndGet(); + return thisObject.getString("stringValue"); + } + + // Instance method with different name and same signature. + @V8Function(name = "add") + public Integer mathAdd(Integer a, Integer b) { + count.incrementAndGet(); + return a + b; + } + // Instance method with primitive type byte @V8Function(name = "primitiveAddByte") public byte primitiveAddByte(byte a, byte b) { @@ -163,13 +155,6 @@ public short primitiveAddShort(short a, short b) { return Integer.valueOf(a + b).shortValue(); } - // Instance method with primitive type boolean - @V8Function(name = "primitiveRevertBoolean") - public boolean primitiveRevertBoolean(boolean b) { - count.incrementAndGet(); - return !b; - } - // Instance method with primitive type char @V8Function(name = "primitiveIncreaseChar") public char primitiveIncreaseChar(char c) { @@ -177,14 +162,29 @@ public char primitiveIncreaseChar(char c) { return (char) ((int) c + 1); } + // Instance method with primitive type boolean + @V8Function(name = "primitiveRevertBoolean") + public boolean primitiveRevertBoolean(boolean b) { + count.incrementAndGet(); + return !b; + } + @V8Function(thisObjectRequired = true) public V8ValueObject self(V8ValueObject thisObject) { count.incrementAndGet(); return thisObject; } - public int getCount() { - return count.get(); + @V8Property + public void setStringValue(String stringValue) { + count.incrementAndGet(); + this.stringValue = stringValue; + } + + @V8Property(thisObjectRequired = true) + public void setStringValueWithThis(V8ValueObject thisObject, String stringValue) throws JavetException { + count.incrementAndGet(); + thisObject.set("stringValue", stringValue); } // Declare the V8RuntimeSetter for dependency injection. diff --git a/src/test/java/com/caoccao/javet/mock/MockCallbackReceiver.java b/src/test/java/com/caoccao/javet/mock/MockCallbackReceiver.java index c0037e4f1..1b94269b0 100644 --- a/src/test/java/com/caoccao/javet/mock/MockCallbackReceiver.java +++ b/src/test/java/com/caoccao/javet/mock/MockCallbackReceiver.java @@ -40,14 +40,6 @@ public MockCallbackReceiver(V8Runtime v8Runtime) { value = null; } - public boolean isCalled() { - return called; - } - - public void setCalled(boolean called) { - this.called = called; - } - public void blank() { called = true; } @@ -58,9 +50,9 @@ public V8Value echo(V8Value arg) throws JavetException { return super.echo(arg); } - public V8Value echoThis(V8Value thisObject) throws JavetException { + public V8ValueArray echo(V8Value... args) throws JavetException { called = true; - return v8Runtime.createV8ValueString(((V8ValueObject) thisObject).toJsonString()); + return super.echo(args); } @Override @@ -81,9 +73,9 @@ public String echoString(V8Value... args) { return super.echoString(args); } - public V8ValueArray echo(V8Value... args) throws JavetException { + public V8Value echoThis(V8Value thisObject) throws JavetException { called = true; - return super.echo(args); + return v8Runtime.createV8ValueString(((V8ValueObject) thisObject).toJsonString()); } public V8ValueString echoThis(V8Value thisObject, V8Value arg) throws JavetException { @@ -134,9 +126,21 @@ public String getValue() { return value; } - public void setValue(String value) { + public boolean isCalled() { + return called; + } + + public String joinIntegerArrayWithThis( + V8ValueObject thisObject, + String s, Integer... integers) { called = true; - this.value = value; + List lines = new ArrayList<>(); + lines.add(thisObject.toJsonString()); + lines.add(s); + for (Integer integer : integers) { + lines.add(integer.toString()); + } + return String.join(",", lines); } public String joinWithThis( @@ -155,19 +159,6 @@ public String joinWithThis( return String.join(",", lines); } - public String joinIntegerArrayWithThis( - V8ValueObject thisObject, - String s, Integer... integers) { - called = true; - List lines = new ArrayList<>(); - lines.add(thisObject.toJsonString()); - lines.add(s); - for (Integer integer : integers) { - lines.add(integer.toString()); - } - return String.join(",", lines); - } - public String joinWithoutThis( Boolean b, Double d, Integer i, Long l, String s, ZonedDateTime z, V8ValueString v) { called = true; @@ -181,4 +172,13 @@ public String joinWithoutThis( lines.add(v.getValue()); return String.join(",", lines); } + + public void setCalled(boolean called) { + this.called = called; + } + + public void setValue(String value) { + called = true; + this.value = value; + } } diff --git a/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInNodeJSMode.java b/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInNodeJSMode.java index 338bc8e7f..e5f2eaf07 100644 --- a/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInNodeJSMode.java +++ b/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInNodeJSMode.java @@ -17,10 +17,10 @@ package com.caoccao.javet.tutorial; +import com.caoccao.javet.enums.JSRuntimeType; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.interfaces.IJavetClosable; import com.caoccao.javet.interfaces.IJavetLogger; -import com.caoccao.javet.enums.JSRuntimeType; import com.caoccao.javet.interop.NodeRuntime; import com.caoccao.javet.interop.engine.IJavetEngine; import com.caoccao.javet.interop.engine.IJavetEnginePool; @@ -32,8 +32,8 @@ import java.nio.file.Path; public class DecimalJavetInNodeJSMode implements IJavetClosable { - private IJavetEnginePool iJavetEnginePool; private IJavetEngine iJavetEngine; + private IJavetEnginePool iJavetEnginePool; public DecimalJavetInNodeJSMode() throws JavetException { iJavetEnginePool = new JavetEnginePool<>(); @@ -52,6 +52,20 @@ public static void main(String[] args) throws JavetException { } } + @Override + public void close() throws JavetException { + if (iJavetEngine != null) { + iJavetEngine.close(); + } + if (iJavetEnginePool != null) { + iJavetEnginePool.close(); + } + } + + public IJavetLogger getLogger() { + return iJavetEnginePool.getConfig().getJavetLogger(); + } + public void test() throws JavetException { NodeRuntime nodeRuntime = iJavetEngine.getV8Runtime(); Path workingDirectory = new File(JavetOSUtils.WORKING_DIRECTORY, "scripts/node/test-node").toPath(); @@ -63,18 +77,4 @@ public void test() throws JavetException { "const b = new Decimal(2.34);" + "a.add(b).toString();").executeString()); } - - public IJavetLogger getLogger() { - return iJavetEnginePool.getConfig().getJavetLogger(); - } - - @Override - public void close() throws JavetException { - if (iJavetEngine != null) { - iJavetEngine.close(); - } - if (iJavetEnginePool != null) { - iJavetEnginePool.close(); - } - } } diff --git a/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInV8Mode.java b/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInV8Mode.java index 4e3650a6f..e0c1f22d2 100644 --- a/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInV8Mode.java +++ b/src/test/java/com/caoccao/javet/tutorial/DecimalJavetInV8Mode.java @@ -33,8 +33,8 @@ import java.math.BigDecimal; public class DecimalJavetInV8Mode implements IJavetClosable { - private IJavetEnginePool iJavetEnginePool; private IJavetEngine iJavetEngine; + private IJavetEnginePool iJavetEnginePool; public DecimalJavetInV8Mode() throws JavetException { iJavetEnginePool = new JavetEnginePool<>(); @@ -54,6 +54,20 @@ public static void main(String[] args) throws JavetException { } } + @Override + public void close() throws JavetException { + if (iJavetEngine != null) { + iJavetEngine.close(); + } + if (iJavetEnginePool != null) { + iJavetEnginePool.close(); + } + } + + public IJavetLogger getLogger() { + return iJavetEnginePool.getConfig().getJavetLogger(); + } + public void loadJS() throws JavetException { File decimalJSFile = new File( JavetOSUtils.WORKING_DIRECTORY, @@ -89,18 +103,4 @@ public void test() throws JavetException { } } } - - public IJavetLogger getLogger() { - return iJavetEnginePool.getConfig().getJavetLogger(); - } - - @Override - public void close() throws JavetException { - if (iJavetEngine != null) { - iJavetEngine.close(); - } - if (iJavetEnginePool != null) { - iJavetEnginePool.close(); - } - } } diff --git a/src/test/java/com/caoccao/javet/tutorial/HelloJavet.java b/src/test/java/com/caoccao/javet/tutorial/HelloJavet.java index 333120a34..a3c767bca 100644 --- a/src/test/java/com/caoccao/javet/tutorial/HelloJavet.java +++ b/src/test/java/com/caoccao/javet/tutorial/HelloJavet.java @@ -35,24 +35,6 @@ public static void main(String[] args) throws JavetException { helloJavet.playWithPoolAndConsole(); } - public void printHelloJavet() throws JavetException { - // Step 1: Create a V8 runtime from V8 host in try-with-resource. - try (V8Runtime v8Runtime = V8Host.getV8Instance().createV8Runtime()) { - // Step 2: Execute a string as JavaScript code and print the result to console. - System.out.println(v8Runtime.getExecutor("'Hello Javet'").executeString()); // Hello Javet - // Step 3: Resource is recycled automatically at the end of the try-with-resource block. - } - } - - public void printOnePlusOne() throws JavetException { - // Step 1: Create a Node runtime from V8 host in try-with-resource. - try (NodeRuntime nodeRuntime = V8Host.getNodeInstance().createV8Runtime()) { - // Step 2: Execute a string as JavaScript code and print the result to console. - System.out.println("1 + 1 = " + nodeRuntime.getExecutor("1 + 1").executeInteger()); // 2 - // Step 3: Resource is recycled automatically at the end of the try-with-resource block. - } - } - public void playWithPoolAndConsole() throws JavetException { // Create a Javet engine pool. try (IJavetEnginePool javetEnginePool = new JavetEnginePool<>()) { @@ -75,4 +57,22 @@ public void playWithPoolAndConsole() throws JavetException { } } } + + public void printHelloJavet() throws JavetException { + // Step 1: Create a V8 runtime from V8 host in try-with-resource. + try (V8Runtime v8Runtime = V8Host.getV8Instance().createV8Runtime()) { + // Step 2: Execute a string as JavaScript code and print the result to console. + System.out.println(v8Runtime.getExecutor("'Hello Javet'").executeString()); // Hello Javet + // Step 3: Resource is recycled automatically at the end of the try-with-resource block. + } + } + + public void printOnePlusOne() throws JavetException { + // Step 1: Create a Node runtime from V8 host in try-with-resource. + try (NodeRuntime nodeRuntime = V8Host.getNodeInstance().createV8Runtime()) { + // Step 2: Execute a string as JavaScript code and print the result to console. + System.out.println("1 + 1 = " + nodeRuntime.getExecutor("1 + 1").executeInteger()); // 2 + // Step 3: Resource is recycled automatically at the end of the try-with-resource block. + } + } } diff --git a/src/test/java/com/caoccao/javet/tutorial/TestES6.java b/src/test/java/com/caoccao/javet/tutorial/TestES6.java index bd7a5115c..3d72de810 100644 --- a/src/test/java/com/caoccao/javet/tutorial/TestES6.java +++ b/src/test/java/com/caoccao/javet/tutorial/TestES6.java @@ -20,7 +20,6 @@ import com.caoccao.javet.BaseTestJavetRuntime; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.utils.JavetOSUtils; -import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.reference.V8ValueArray; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/caoccao/javet/tutorial/TestInterception.java b/src/test/java/com/caoccao/javet/tutorial/TestInterception.java index 897a1ca4b..08060df02 100644 --- a/src/test/java/com/caoccao/javet/tutorial/TestInterception.java +++ b/src/test/java/com/caoccao/javet/tutorial/TestInterception.java @@ -74,14 +74,15 @@ public static void main(String[] args) throws JavetException { } } - @V8Property - public String getName() { - return name; + @V8Function + public int add(int delta) { + value += delta; + return value; } @V8Property - public void setName(String name) { - this.name = name; + public String getName() { + return name; } @V8Property @@ -89,19 +90,18 @@ public int getValue() { return value; } - @V8Property - public void setValue(int value) { - this.value = value; - } - @V8Function public int increaseAndGet() { return ++value; } - @V8Function - public int add(int delta) { - value += delta; - return value; + @V8Property + public void setName(String name) { + this.name = name; + } + + @V8Property + public void setValue(int value) { + this.value = value; } } diff --git a/src/test/java/com/caoccao/javet/tutorial/cdt/CDTConfig.java b/src/test/java/com/caoccao/javet/tutorial/cdt/CDTConfig.java index 3a9095097..7220995ac 100644 --- a/src/test/java/com/caoccao/javet/tutorial/cdt/CDTConfig.java +++ b/src/test/java/com/caoccao/javet/tutorial/cdt/CDTConfig.java @@ -37,20 +37,20 @@ public static int getPort() { return port; } + public static V8Runtime getV8Runtime() { + return v8Runtime; + } + + public static String getWebSocketUrl() { + return webSocketUrl; + } + public static void setPort(int port) { CDTConfig.port = port; webSocketUrl = "ws://127.0.0.1:" + Integer.toString(port) + PATH_JAVET; } - public static V8Runtime getV8Runtime() { - return v8Runtime; - } - public static void setV8Runtime(V8Runtime v8Runtime) { CDTConfig.v8Runtime = v8Runtime; } - - public static String getWebSocketUrl() { - return webSocketUrl; - } } diff --git a/src/test/java/com/caoccao/javet/utils/TestSimpleFreeMarkerFormat.java b/src/test/java/com/caoccao/javet/utils/TestSimpleFreeMarkerFormat.java index d9c7e712e..cfb717cd8 100644 --- a/src/test/java/com/caoccao/javet/utils/TestSimpleFreeMarkerFormat.java +++ b/src/test/java/com/caoccao/javet/utils/TestSimpleFreeMarkerFormat.java @@ -26,47 +26,47 @@ public class TestSimpleFreeMarkerFormat { @Test public void testInvalid() { - assertEquals( "abc", + assertEquals("abc", SimpleFreeMarkerFormat.format("abc", null), "Parameters being null should pass."); - assertEquals( "abc", + assertEquals("abc", SimpleFreeMarkerFormat.format("abc", new HashMap<>()), "Parameters being empty should pass."); - assertEquals( "abc${", + assertEquals("abc${", SimpleFreeMarkerFormat.format("abc${", SimpleMap.of("d", "x")), "Open variable should pass."); - assertEquals( "abc${def", + assertEquals("abc${def", SimpleFreeMarkerFormat.format("abc${def", SimpleMap.of("d", "x")), "Open variable should pass."); - assertEquals( "abc$${d}", + assertEquals("abc$${d}", SimpleFreeMarkerFormat.format("abc$${d}", SimpleMap.of("d", "x")), "Double dollar should pass."); - assertEquals( "abcdef", + assertEquals("abcdef", SimpleFreeMarkerFormat.format("abc${e}def", SimpleMap.of("d", "x")), "Unknown variable should pass."); - assertEquals( "abcdef", + assertEquals("abcdef", SimpleFreeMarkerFormat.format("abc${}def", SimpleMap.of("d", "x")), "Empty variable should pass."); - assertEquals( "ab{def.$ghi}c", + assertEquals("ab{def.$ghi}c", SimpleFreeMarkerFormat.format("ab{def.$ghi}c", SimpleMap.of("ghi", "x")), "Dollar should pass."); } @Test public void testValid() { - assertEquals( "abcx", + assertEquals("abcx", SimpleFreeMarkerFormat.format("abc${d}", SimpleMap.of("d", "x")), "Variable at the end should pass."); - assertEquals( "xabc", + assertEquals("xabc", SimpleFreeMarkerFormat.format("${d}abc", SimpleMap.of("d", "x")), "Variable at the beginning should pass."); - assertEquals( "abxc", + assertEquals("abxc", SimpleFreeMarkerFormat.format("ab${d}c", SimpleMap.of("d", "x")), "Variable in the middle should pass."); - assertEquals( "abxc", + assertEquals("abxc", SimpleFreeMarkerFormat.format("ab${def.${ghi}c", SimpleMap.of("def.${ghi", "x")), "Variable with dollar should pass."); - assertEquals( "abxc", + assertEquals("abxc", SimpleFreeMarkerFormat.format("ab${{}c", SimpleMap.of("{", "x")), "Single open should pass."); } diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java index 34b36ea72..c75ababde 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertFalse; public class TestV8ValueDouble extends BaseTestJavetRuntime { public static final double DELTA = 0.001; diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java index 6f88ffffd..5a3153677 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertFalse; public class TestV8ValueInteger extends BaseTestJavetRuntime { @Test diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java index 66628908b..9ca0d8506 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertFalse; public class TestV8ValueString extends BaseTestJavetRuntime { @Test diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8Module.java b/src/test/java/com/caoccao/javet/values/reference/TestV8Module.java index 489d0c8e4..aa2826222 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8Module.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8Module.java @@ -50,45 +50,6 @@ public void testExecute() throws JavetException { } } - @Test - public void testImportValidModuleAndExecute() throws JavetException { - String codeString = "export function test() { return { a: 1 }; };"; - IV8Executor iV8Executor = v8Runtime.getExecutor(codeString).setResourceName("./test.js"); - try (V8Module v8Module = iV8Executor.compileV8Module()) { - if (v8Runtime.getJSRuntimeType().isNode()) { - v8Runtime.getExecutor("const process = require('process');\n" + - "var globalReason = null;\n" + - "process.on('unhandledRejection', (reason, promise) => {\n" + - " globalReason = reason;\n" + - "});").executeVoid(); - } - try (V8ValuePromise v8ValuePromise = v8Module.execute()) { - assertTrue(v8ValuePromise.isFulfilled()); - assertTrue(v8ValuePromise.getResult().isUndefined()); - } - // Import in module is supported. - codeString = "import { test } from './test.js'; test();"; - iV8Executor = v8Runtime.getExecutor(codeString).setResourceName("./a.js").setModule(true); - try (V8ValueObject v8ValueObject = iV8Executor.execute()) { - assertNotNull(v8ValueObject); - V8ValuePromise v8ValuePromise = (V8ValuePromise) v8ValueObject; - assertTrue(v8ValuePromise.isFulfilled()); - assertTrue(v8ValuePromise.getResult().isUndefined()); - if (v8Runtime.getJSRuntimeType().isV8()) { - assertFalse(v8Runtime.containsV8Module("./a.js")); - } - } - // V8: Dynamic import is not supported. - // Node: UnhandledPromiseRejectionWarning: TypeError: Invalid host defined options. - codeString = "const p = import('./test.js'); p;"; - iV8Executor = v8Runtime.getExecutor(codeString).setResourceName("./a.js").setModule(false); - try (V8ValuePromise v8ValuePromise = iV8Executor.execute()) { - assertNotNull(v8ValuePromise); - assertTrue(v8ValuePromise.isRejected()); - } - } - } - @Test public void testImportInvalidModuleInModule() throws JavetException { String codeString1 = "export function test1() {\n" + @@ -136,6 +97,45 @@ public void testImportInvalidModuleInModule() throws JavetException { } } + @Test + public void testImportValidModuleAndExecute() throws JavetException { + String codeString = "export function test() { return { a: 1 }; };"; + IV8Executor iV8Executor = v8Runtime.getExecutor(codeString).setResourceName("./test.js"); + try (V8Module v8Module = iV8Executor.compileV8Module()) { + if (v8Runtime.getJSRuntimeType().isNode()) { + v8Runtime.getExecutor("const process = require('process');\n" + + "var globalReason = null;\n" + + "process.on('unhandledRejection', (reason, promise) => {\n" + + " globalReason = reason;\n" + + "});").executeVoid(); + } + try (V8ValuePromise v8ValuePromise = v8Module.execute()) { + assertTrue(v8ValuePromise.isFulfilled()); + assertTrue(v8ValuePromise.getResult().isUndefined()); + } + // Import in module is supported. + codeString = "import { test } from './test.js'; test();"; + iV8Executor = v8Runtime.getExecutor(codeString).setResourceName("./a.js").setModule(true); + try (V8ValueObject v8ValueObject = iV8Executor.execute()) { + assertNotNull(v8ValueObject); + V8ValuePromise v8ValuePromise = (V8ValuePromise) v8ValueObject; + assertTrue(v8ValuePromise.isFulfilled()); + assertTrue(v8ValuePromise.getResult().isUndefined()); + if (v8Runtime.getJSRuntimeType().isV8()) { + assertFalse(v8Runtime.containsV8Module("./a.js")); + } + } + // V8: Dynamic import is not supported. + // Node: UnhandledPromiseRejectionWarning: TypeError: Invalid host defined options. + codeString = "const p = import('./test.js'); p;"; + iV8Executor = v8Runtime.getExecutor(codeString).setResourceName("./a.js").setModule(false); + try (V8ValuePromise v8ValuePromise = iV8Executor.execute()) { + assertNotNull(v8ValuePromise); + assertTrue(v8ValuePromise.isRejected()); + } + } + } + @Test public void testImportValidModuleInModule() throws JavetException { String codeString1 = "export function test1() {\n" + diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArguments.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArguments.java index 23e79b10d..5182bb476 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArguments.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArguments.java @@ -17,8 +17,8 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java index 2834b77db..1cb1f6ded 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java @@ -42,31 +42,6 @@ public void testForEach() throws JavetException { } } - @Test - public void testGetAndSet() throws JavetException { - try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("const a = new Array(); a;").execute()) { - v8ValueArray.set(0, "x"); - v8ValueArray.set(1, "y"); - v8ValueArray.set(2, "z"); - v8ValueArray.set("a", 1); - v8ValueArray.set("b", "2"); - assertEquals(3, v8ValueArray.getLength()); - assertEquals("x", v8ValueArray.getString(0)); - assertEquals("y", v8ValueArray.getString(1)); - assertEquals("z", v8ValueArray.getString(2)); - assertEquals(1, v8ValueArray.getInteger("a")); - assertEquals("2", v8ValueArray.getString("b")); - assertEquals("x,y,z", v8ValueArray.toString()); - assertEquals("[object Array]", v8ValueArray.toProtoString()); - assertEquals("[\"x\",\"y\",\"z\"]", v8ValueArray.toJsonString()); - List keys = v8ValueArray.getKeys(); - assertEquals(3, keys.size()); - assertEquals(0, keys.get(0)); - assertEquals(1, keys.get(1)); - assertEquals(2, keys.get(2)); - } - } - @Test public void testGet() throws JavetException { try (V8ValueArray v8ValueArray = v8Runtime.getExecutor( @@ -103,6 +78,31 @@ public void testGet() throws JavetException { } } + @Test + public void testGetAndSet() throws JavetException { + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("const a = new Array(); a;").execute()) { + v8ValueArray.set(0, "x"); + v8ValueArray.set(1, "y"); + v8ValueArray.set(2, "z"); + v8ValueArray.set("a", 1); + v8ValueArray.set("b", "2"); + assertEquals(3, v8ValueArray.getLength()); + assertEquals("x", v8ValueArray.getString(0)); + assertEquals("y", v8ValueArray.getString(1)); + assertEquals("z", v8ValueArray.getString(2)); + assertEquals(1, v8ValueArray.getInteger("a")); + assertEquals("2", v8ValueArray.getString("b")); + assertEquals("x,y,z", v8ValueArray.toString()); + assertEquals("[object Array]", v8ValueArray.toProtoString()); + assertEquals("[\"x\",\"y\",\"z\"]", v8ValueArray.toJsonString()); + List keys = v8ValueArray.getKeys(); + assertEquals(3, keys.size()); + assertEquals(0, keys.get(0)); + assertEquals(1, keys.get(1)); + assertEquals(2, keys.get(2)); + } + } + @Test public void testNestedArray() throws JavetException { try (V8ValueArray outerArray = v8Runtime.getExecutor("[1,2,3]").execute()) { diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueError.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueError.java index 47c4236f5..91c5bd7ef 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueError.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueError.java @@ -17,8 +17,8 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java index 397c8e179..e8e34bae7 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java @@ -178,20 +178,40 @@ public void testArrayPush() throws JavetException { } @Test - public void testCallbackBlankWithoutThis() throws JavetException, NoSuchMethodException { + public void testCallObject() throws JavetException { + String prefixString = "const x = 1; "; + String functionName = "function a"; + String suffixString = " const y = 2;"; + String codeString = "(b) { return [1,2,3].concat(b); }"; + v8Runtime.getExecutor(prefixString + functionName + codeString + suffixString).executeVoid(); + try (V8ValueFunction v8ValueFunction = v8Runtime.getGlobalObject().get("a")) { + assertTrue(v8ValueFunction.getJSFunctionType().isUserDefined()); + assertEquals(functionName + codeString, v8ValueFunction.toString()); + assertEquals(codeString, v8ValueFunction.getSourceCode()); + List result = v8ValueFunction.callObject(null, (Object) (new Integer[]{4, 5, 6})); + assertArrayEquals( + new Integer[]{1, 2, 3, 4, 5, 6}, + result.toArray(new Integer[0]), + "callObject() should work transparently without resource leak"); + } + } + + @Test + public void testCallbackBlankWithThis() throws JavetException, NoSuchMethodException { MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); JavetCallbackContext javetCallbackContext = new JavetCallbackContext( - mockCallbackReceiver, mockCallbackReceiver.getMethod("blank")); + mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThis", true), true); V8ValueObject globalObject = v8Runtime.getGlobalObject(); V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext); try (V8ValueObject a = v8Runtime.createV8ValueObject()) { globalObject.set("a", a); - a.set("blank", v8ValueFunction); + a.set("x", "1"); + a.set("echoThis", v8ValueFunction); assertFalse(mockCallbackReceiver.isCalled()); - v8Runtime.getExecutor("a.blank();").executeVoid(); + assertEquals("{\"x\":\"1\"}", v8Runtime.getExecutor("a.echoThis();").executeString()); assertTrue(mockCallbackReceiver.isCalled()); v8ValueFunction.setWeak(); - a.delete("blank"); + a.delete("echoThis"); globalObject.delete("a"); } assertEquals(1, v8Runtime.getReferenceCount()); @@ -205,21 +225,20 @@ public void testCallbackBlankWithoutThis() throws JavetException, NoSuchMethodEx } @Test - public void testCallbackBlankWithThis() throws JavetException, NoSuchMethodException { + public void testCallbackBlankWithoutThis() throws JavetException, NoSuchMethodException { MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); JavetCallbackContext javetCallbackContext = new JavetCallbackContext( - mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThis", true), true); + mockCallbackReceiver, mockCallbackReceiver.getMethod("blank")); V8ValueObject globalObject = v8Runtime.getGlobalObject(); V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext); try (V8ValueObject a = v8Runtime.createV8ValueObject()) { globalObject.set("a", a); - a.set("x", "1"); - a.set("echoThis", v8ValueFunction); + a.set("blank", v8ValueFunction); assertFalse(mockCallbackReceiver.isCalled()); - assertEquals("{\"x\":\"1\"}", v8Runtime.getExecutor("a.echoThis();").executeString()); + v8Runtime.getExecutor("a.blank();").executeVoid(); assertTrue(mockCallbackReceiver.isCalled()); v8ValueFunction.setWeak(); - a.delete("echoThis"); + a.delete("blank"); globalObject.delete("a"); } assertEquals(1, v8Runtime.getReferenceCount()); @@ -232,6 +251,35 @@ public void testCallbackBlankWithThis() throws JavetException, NoSuchMethodExcep } } + @Test + public void testCallbackEchoLILOWithThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + JavetCallbackContext javetCallbackContext = new JavetCallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethodVarargs("echoThis", true), true); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.set("x", "1"); + globalObject.set("echoThis", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("var a = echoThis(1, '2', 3n); a;").execute()) { + assertEquals(2, v8Runtime.getReferenceCount()); + assertEquals(4, v8ValueArray.getLength()); + try (V8ValueObject v8ValueObject = v8ValueArray.get(0)) { + assertEquals("1", v8ValueObject.getString("x")); + } + assertEquals(1, v8ValueArray.getInteger(1)); + assertEquals("2", v8ValueArray.getString(2)); + assertEquals(3L, v8ValueArray.getLong(3)); + } + assertTrue(globalObject.hasOwnProperty("a")); + assertTrue(mockCallbackReceiver.isCalled()); + globalObject.delete("echoThis"); + } + v8Runtime.requestGarbageCollectionForTesting(true); + } + @Test public void testCallbackEchoLILOWithoutThis() throws JavetException, NoSuchMethodException { assertEquals(0, v8Runtime.getReferenceCount()); @@ -257,31 +305,27 @@ public void testCallbackEchoLILOWithoutThis() throws JavetException, NoSuchMetho } @Test - public void testCallbackEchoLILOWithThis() throws JavetException, NoSuchMethodException { + public void testCallbackEchoStringWithThis() throws JavetException, NoSuchMethodException { assertEquals(0, v8Runtime.getReferenceCount()); MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); JavetCallbackContext javetCallbackContext = new JavetCallbackContext( - mockCallbackReceiver, mockCallbackReceiver.getMethodVarargs("echoThis", true), true); + mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThisString", V8Value.class, String.class), true); V8ValueObject globalObject = v8Runtime.getGlobalObject(); try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext)) { assertEquals(1, v8Runtime.getReferenceCount()); - globalObject.set("x", "1"); - globalObject.set("echoThis", v8ValueFunction); + try (V8ValueObject a = v8Runtime.createV8ValueObject()) { + a.set("x", "1"); + a.set("echoThisString", v8ValueFunction); + globalObject.set("a", a); + } assertFalse(mockCallbackReceiver.isCalled()); - try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("var a = echoThis(1, '2', 3n); a;").execute()) { - assertEquals(2, v8Runtime.getReferenceCount()); - assertEquals(4, v8ValueArray.getLength()); - try (V8ValueObject v8ValueObject = v8ValueArray.get(0)) { - assertEquals("1", v8ValueObject.getString("x")); - } - assertEquals(1, v8ValueArray.getInteger(1)); - assertEquals("2", v8ValueArray.getString(2)); - assertEquals(3L, v8ValueArray.getLong(3)); + try (V8ValueString v8ValueString = v8Runtime.getExecutor( + "const x = a.echoThisString('123'); x;").execute()) { + assertEquals("[{\"x\":\"1\"},\"123\"]", v8ValueString.getValue()); } - assertTrue(globalObject.hasOwnProperty("a")); - assertTrue(mockCallbackReceiver.isCalled()); - globalObject.delete("echoThis"); + globalObject.delete("a"); } + assertTrue(mockCallbackReceiver.isCalled()); v8Runtime.requestGarbageCollectionForTesting(true); } @@ -311,22 +355,21 @@ public void testCallbackEchoStringWithoutThis() throws JavetException, NoSuchMet } @Test - public void testCallbackEchoStringWithThis() throws JavetException, NoSuchMethodException { + public void testCallbackEchoVIVOWithThis() throws JavetException, NoSuchMethodException { assertEquals(0, v8Runtime.getReferenceCount()); MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); JavetCallbackContext javetCallbackContext = new JavetCallbackContext( - mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThisString", V8Value.class, String.class), true); + mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThis", V8Value.class, V8Value.class), true); V8ValueObject globalObject = v8Runtime.getGlobalObject(); try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext)) { assertEquals(1, v8Runtime.getReferenceCount()); try (V8ValueObject a = v8Runtime.createV8ValueObject()) { a.set("x", "1"); - a.set("echoThisString", v8ValueFunction); + a.set("echoThis", v8ValueFunction); globalObject.set("a", a); } assertFalse(mockCallbackReceiver.isCalled()); - try (V8ValueString v8ValueString = v8Runtime.getExecutor( - "const x = a.echoThisString('123'); x;").execute()) { + try (V8ValueString v8ValueString = v8Runtime.getExecutor("const x = a.echoThis('123'); x;").execute()) { assertEquals("[{\"x\":\"1\"},\"123\"]", v8ValueString.getValue()); } globalObject.delete("a"); @@ -356,30 +399,6 @@ public void testCallbackEchoVIVOWithoutThis() throws JavetException, NoSuchMetho v8Runtime.requestGarbageCollectionForTesting(true); } - @Test - public void testCallbackEchoVIVOWithThis() throws JavetException, NoSuchMethodException { - assertEquals(0, v8Runtime.getReferenceCount()); - MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); - JavetCallbackContext javetCallbackContext = new JavetCallbackContext( - mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThis", V8Value.class, V8Value.class), true); - V8ValueObject globalObject = v8Runtime.getGlobalObject(); - try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext)) { - assertEquals(1, v8Runtime.getReferenceCount()); - try (V8ValueObject a = v8Runtime.createV8ValueObject()) { - a.set("x", "1"); - a.set("echoThis", v8ValueFunction); - globalObject.set("a", a); - } - assertFalse(mockCallbackReceiver.isCalled()); - try (V8ValueString v8ValueString = v8Runtime.getExecutor("const x = a.echoThis('123'); x;").execute()) { - assertEquals("[{\"x\":\"1\"},\"123\"]", v8ValueString.getValue()); - } - globalObject.delete("a"); - } - assertTrue(mockCallbackReceiver.isCalled()); - v8Runtime.requestGarbageCollectionForTesting(true); - } - @Test public void testCallbackError() throws JavetException, NoSuchMethodException { MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); @@ -407,14 +426,13 @@ public void testCallbackError() throws JavetException, NoSuchMethodException { } @Test - public void testCallbackJoinWithThis() throws JavetException, NoSuchMethodException { + public void testCallbackJoinIntegerArrayWithThis() throws JavetException, NoSuchMethodException { assertEquals(0, v8Runtime.getReferenceCount()); MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); JavetCallbackContext javetCallbackContext = new JavetCallbackContext( mockCallbackReceiver, - mockCallbackReceiver.getMethod("joinWithThis", - V8ValueObject.class, Boolean.class, Double.class, Integer.class, Long.class, - String.class, ZonedDateTime.class, V8ValueString.class), + mockCallbackReceiver.getMethod( + "joinIntegerArrayWithThis", V8ValueObject.class, String.class, Integer[].class), true); V8ValueObject globalObject = v8Runtime.getGlobalObject(); try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext)) { @@ -422,12 +440,12 @@ public void testCallbackJoinWithThis() throws JavetException, NoSuchMethodExcept try (V8ValueObject x = v8Runtime.createV8ValueObject()) { globalObject.set("x", x); x.set("p", "q"); - x.set("joinWithThis", v8ValueFunction); + x.set("joinIntegerArrayWithThis", v8ValueFunction); } assertFalse(mockCallbackReceiver.isCalled()); String resultString = v8Runtime.getExecutor( - "const a = x.joinWithThis(true, 1.23, 2, 3n, '4', new Date(1611710223719), '6'); a;").executeString(); - assertEquals("{\"p\":\"q\"},true,1.23,2,3,4,2021-01-27T01:17:03.719Z[UTC],6", resultString); + "const a = x.joinIntegerArrayWithThis('x', 1, 2, 3); a;").executeString(); + assertEquals("{\"p\":\"q\"},x,1,2,3", resultString); globalObject.delete("x"); } assertTrue(mockCallbackReceiver.isCalled()); @@ -435,13 +453,14 @@ public void testCallbackJoinWithThis() throws JavetException, NoSuchMethodExcept } @Test - public void testCallbackJoinIntegerArrayWithThis() throws JavetException, NoSuchMethodException { + public void testCallbackJoinWithThis() throws JavetException, NoSuchMethodException { assertEquals(0, v8Runtime.getReferenceCount()); MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); JavetCallbackContext javetCallbackContext = new JavetCallbackContext( mockCallbackReceiver, - mockCallbackReceiver.getMethod( - "joinIntegerArrayWithThis", V8ValueObject.class, String.class, Integer[].class), + mockCallbackReceiver.getMethod("joinWithThis", + V8ValueObject.class, Boolean.class, Double.class, Integer.class, Long.class, + String.class, ZonedDateTime.class, V8ValueString.class), true); V8ValueObject globalObject = v8Runtime.getGlobalObject(); try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(javetCallbackContext)) { @@ -449,12 +468,12 @@ public void testCallbackJoinIntegerArrayWithThis() throws JavetException, NoSuch try (V8ValueObject x = v8Runtime.createV8ValueObject()) { globalObject.set("x", x); x.set("p", "q"); - x.set("joinIntegerArrayWithThis", v8ValueFunction); + x.set("joinWithThis", v8ValueFunction); } assertFalse(mockCallbackReceiver.isCalled()); String resultString = v8Runtime.getExecutor( - "const a = x.joinIntegerArrayWithThis('x', 1, 2, 3); a;").executeString(); - assertEquals("{\"p\":\"q\"},x,1,2,3", resultString); + "const a = x.joinWithThis(true, 1.23, 2, 3n, '4', new Date(1611710223719), '6'); a;").executeString(); + assertEquals("{\"p\":\"q\"},true,1.23,2,3,4,2021-01-27T01:17:03.719Z[UTC],6", resultString); globalObject.delete("x"); } assertTrue(mockCallbackReceiver.isCalled()); @@ -484,25 +503,6 @@ public void testCallbackJoinWithoutThis() throws JavetException, NoSuchMethodExc v8Runtime.requestGarbageCollectionForTesting(true); } - @Test - public void testCallObject() throws JavetException { - String prefixString = "const x = 1; "; - String functionName = "function a"; - String suffixString = " const y = 2;"; - String codeString = "(b) { return [1,2,3].concat(b); }"; - v8Runtime.getExecutor(prefixString + functionName + codeString + suffixString).executeVoid(); - try (V8ValueFunction v8ValueFunction = v8Runtime.getGlobalObject().get("a")) { - assertTrue(v8ValueFunction.getJSFunctionType().isUserDefined()); - assertEquals(functionName + codeString, v8ValueFunction.toString()); - assertEquals(codeString, v8ValueFunction.getSourceCode()); - List result = v8ValueFunction.callObject(null, (Object) (new Integer[]{4, 5, 6})); - assertArrayEquals( - new Integer[]{1, 2, 3, 4, 5, 6}, - result.toArray(new Integer[0]), - "callObject() should work transparently without resource leak"); - } - } - @Test public void testContextScope() throws JavetException { v8Runtime.getExecutor("var c = {};" + diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java index dd8ea772f..afa2889f1 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java @@ -90,29 +90,6 @@ public void testClearWeak() throws JavetException { } } - @Test - public void testForEach() throws JavetException { - try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( - "const a = {'A0': 0, 'A1': 1, 'A2': 2}; a;").execute()) { - AtomicInteger count = new AtomicInteger(0); - assertEquals(3, v8ValueObject.forEach((V8ValueString key) -> { - assertEquals("A" + Integer.toString(count.getAndIncrement()), key.getValue()); - })); - count.set(0); - assertEquals(3, v8ValueObject.forEach((V8ValueString key, V8ValueInteger value) -> { - assertEquals("A" + Integer.toString(count.get()), key.getValue()); - assertEquals(count.getAndIncrement(), value.getValue()); - })); - assertEquals(3, v8ValueObject.forEach((int index, V8ValueString key) -> { - assertEquals("A" + Integer.toString(index), key.getValue()); - })); - assertEquals(3, v8ValueObject.forEach((int index, V8ValueString key, V8ValueInteger value) -> { - assertEquals("A" + Integer.toString(index), key.getValue()); - assertEquals(index, value.getValue()); - })); - } - } - @Test public void testEquals() throws JavetException { try (V8ValueObject v8ValueObject1 = v8Runtime.getExecutor( @@ -136,29 +113,33 @@ public void testEquals() throws JavetException { } @Test - public void testGetOwnPropertyNames() throws JavetException { + public void testForEach() throws JavetException { try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( - "let x = {'a': 1, 'b': '2', 'c': 3n, d: 1, e: null, g: {h: 1}, '中文': '測試'}; x;").execute()) { - try (IV8ValueArray iV8ValueArray = v8ValueObject.getOwnPropertyNames()) { - assertNotNull(iV8ValueArray); - assertEquals(7, iV8ValueArray.getLength()); - // Order is preserved since ES2015. - assertEquals("a", iV8ValueArray.getPropertyString(0)); - assertEquals("b", iV8ValueArray.getPropertyString(1)); - assertEquals("c", iV8ValueArray.getPropertyString(2)); - assertEquals("d", iV8ValueArray.getPropertyString(3)); - assertEquals("e", iV8ValueArray.getPropertyString(4)); - assertEquals("g", iV8ValueArray.getPropertyString(5)); - assertEquals("中文", iV8ValueArray.getPropertyString(6)); - } + "const a = {'A0': 0, 'A1': 1, 'A2': 2}; a;").execute()) { + AtomicInteger count = new AtomicInteger(0); + assertEquals(3, v8ValueObject.forEach((V8ValueString key) -> { + assertEquals("A" + Integer.toString(count.getAndIncrement()), key.getValue()); + })); + count.set(0); + assertEquals(3, v8ValueObject.forEach((V8ValueString key, V8ValueInteger value) -> { + assertEquals("A" + Integer.toString(count.get()), key.getValue()); + assertEquals(count.getAndIncrement(), value.getValue()); + })); + assertEquals(3, v8ValueObject.forEach((int index, V8ValueString key) -> { + assertEquals("A" + Integer.toString(index), key.getValue()); + })); + assertEquals(3, v8ValueObject.forEach((int index, V8ValueString key, V8ValueInteger value) -> { + assertEquals("A" + Integer.toString(index), key.getValue()); + assertEquals(index, value.getValue()); + })); } } @Test - public void testGetPropertyNames() throws JavetException { + public void testGetOwnPropertyNames() throws JavetException { try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( "let x = {'a': 1, 'b': '2', 'c': 3n, d: 1, e: null, g: {h: 1}, '中文': '測試'}; x;").execute()) { - try (IV8ValueArray iV8ValueArray = v8ValueObject.getPropertyNames()) { + try (IV8ValueArray iV8ValueArray = v8ValueObject.getOwnPropertyNames()) { assertNotNull(iV8ValueArray); assertEquals(7, iV8ValueArray.getLength()); // Order is preserved since ES2015. @@ -173,24 +154,6 @@ public void testGetPropertyNames() throws JavetException { } } - @Test - public void testGetSetDelete() throws JavetException { - try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const a = {}; a;").execute()) { - assertTrue(v8ValueObject.set("a", 1)); - assertTrue(v8ValueObject.set("b", "2")); - assertTrue(v8ValueObject.set("c", new String[]{"x", "y"})); - assertEquals(1, v8ValueObject.getInteger("a")); - assertEquals("2", v8ValueObject.getString("b")); - assertArrayEquals( - new String[]{"x", "y"}, - ((List) v8Runtime.toObject(v8ValueObject.get("c"), true)).toArray(new String[0])); - assertTrue(v8ValueObject.delete("x")); - assertTrue(v8ValueObject.delete("b")); - V8Value v8Value = v8ValueObject.getUndefined("b"); - assertNotNull(v8Value); - } - } - @Test public void testGetProperty() throws JavetException { try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( @@ -230,6 +193,43 @@ public void testGetProperty() throws JavetException { } } + @Test + public void testGetPropertyNames() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( + "let x = {'a': 1, 'b': '2', 'c': 3n, d: 1, e: null, g: {h: 1}, '中文': '測試'}; x;").execute()) { + try (IV8ValueArray iV8ValueArray = v8ValueObject.getPropertyNames()) { + assertNotNull(iV8ValueArray); + assertEquals(7, iV8ValueArray.getLength()); + // Order is preserved since ES2015. + assertEquals("a", iV8ValueArray.getPropertyString(0)); + assertEquals("b", iV8ValueArray.getPropertyString(1)); + assertEquals("c", iV8ValueArray.getPropertyString(2)); + assertEquals("d", iV8ValueArray.getPropertyString(3)); + assertEquals("e", iV8ValueArray.getPropertyString(4)); + assertEquals("g", iV8ValueArray.getPropertyString(5)); + assertEquals("中文", iV8ValueArray.getPropertyString(6)); + } + } + } + + @Test + public void testGetSetDelete() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const a = {}; a;").execute()) { + assertTrue(v8ValueObject.set("a", 1)); + assertTrue(v8ValueObject.set("b", "2")); + assertTrue(v8ValueObject.set("c", new String[]{"x", "y"})); + assertEquals(1, v8ValueObject.getInteger("a")); + assertEquals("2", v8ValueObject.getString("b")); + assertArrayEquals( + new String[]{"x", "y"}, + ((List) v8Runtime.toObject(v8ValueObject.get("c"), true)).toArray(new String[0])); + assertTrue(v8ValueObject.delete("x")); + assertTrue(v8ValueObject.delete("b")); + V8Value v8Value = v8ValueObject.getUndefined("b"); + assertNotNull(v8Value); + } + } + @Test public void testHarmonyScoping() throws JavetException { v8Runtime.getExecutor("let a = 1;").executeVoid(); @@ -315,29 +315,6 @@ public void testSetProperty() throws JavetException { } } - @Test - public void testToClone() throws JavetException { - try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const x = {}; x;").execute()) { - v8ValueObject.setProperty("a", "1"); - assertEquals("{\"a\":\"1\"}", v8ValueObject.toJsonString()); - try (V8ValueObject clonedV8ValueObject = v8ValueObject.toClone()) { - assertEquals("{\"a\":\"1\"}", clonedV8ValueObject.toJsonString()); - assertNotEquals(v8ValueObject.getHandle(), clonedV8ValueObject.getHandle()); - assertEquals(v8Runtime, clonedV8ValueObject.getV8Runtime()); - } - } - } - - @Test - public void testToJsonString() throws JavetException { - try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const x = {}; x;").execute()) { - v8ValueObject.setProperty("a", "1"); - v8ValueObject.setProperty("b", 2); - v8ValueObject.setProperty("c", 1.23); - assertEquals("{\"a\":\"1\",\"b\":2,\"c\":1.23}", v8ValueObject.toJsonString()); - } - } - @Test public void testSetWeakDirectDescendant() throws JavetException { V8ValueObject a = v8Runtime.createV8ValueObject(); @@ -373,4 +350,27 @@ public void testSetWeakIndirectDescendant() throws JavetException { globalObject.delete("a"); v8Runtime.requestGarbageCollectionForTesting(true); } + + @Test + public void testToClone() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const x = {}; x;").execute()) { + v8ValueObject.setProperty("a", "1"); + assertEquals("{\"a\":\"1\"}", v8ValueObject.toJsonString()); + try (V8ValueObject clonedV8ValueObject = v8ValueObject.toClone()) { + assertEquals("{\"a\":\"1\"}", clonedV8ValueObject.toJsonString()); + assertNotEquals(v8ValueObject.getHandle(), clonedV8ValueObject.getHandle()); + assertEquals(v8Runtime, clonedV8ValueObject.getV8Runtime()); + } + } + } + + @Test + public void testToJsonString() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const x = {}; x;").execute()) { + v8ValueObject.setProperty("a", "1"); + v8ValueObject.setProperty("b", 2); + v8ValueObject.setProperty("c", 1.23); + assertEquals("{\"a\":\"1\",\"b\":2,\"c\":1.23}", v8ValueObject.toJsonString()); + } + } } diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueProxy.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueProxy.java index eca88a7dc..e61cc8470 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueProxy.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueProxy.java @@ -17,8 +17,8 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueRegExp.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueRegExp.java index 53b1ef2af..3a42ae212 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueRegExp.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueRegExp.java @@ -21,7 +21,6 @@ import com.caoccao.javet.exceptions.JavetException; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; public class TestV8ValueRegExp extends BaseTestJavetRuntime { diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSymbol.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSymbol.java index 0f02d9c02..679c8df72 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSymbol.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSymbol.java @@ -17,12 +17,12 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.BaseTestJavetRuntime; -import com.caoccao.javet.exceptions.JavetExecutionException; +import com.caoccao.javet.exceptions.JavetException; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class TestV8ValueSymbol extends BaseTestJavetRuntime { @Test diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakMap.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakMap.java index 13e7e4745..9fb4b657f 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakMap.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakMap.java @@ -34,7 +34,7 @@ public void testSetGetHasAndDelete() throws JavetException { v8ValueObject.set("x", "1"); v8ValueWeakMap.set(v8ValueObject, "2"); assertTrue(v8ValueWeakMap.has(v8ValueObject)); - assertEquals("2", ((V8ValueString)v8ValueWeakMap.get(v8ValueObject)).getValue()); + assertEquals("2", ((V8ValueString) v8ValueWeakMap.get(v8ValueObject)).getValue()); v8ValueWeakMap.delete(v8ValueObject); assertFalse(v8ValueWeakMap.has(v8ValueObject)); } diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakSet.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakSet.java index 2ccf38e58..27bb1dc3d 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakSet.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueWeakSet.java @@ -19,9 +19,6 @@ import com.caoccao.javet.BaseTestJavetRuntime; import com.caoccao.javet.exceptions.JavetException; -import com.caoccao.javet.values.V8Value; -import com.caoccao.javet.values.primitive.V8ValueInteger; -import com.caoccao.javet.values.primitive.V8ValueString; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; From 3e37b4fe6372c837ef814bde5aadd6883873c84b Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Fri, 11 Jun 2021 19:01:30 +0800 Subject: [PATCH 10/12] Enhanced pool with lazy resetContext() --- .../com/caoccao/javet/interop/V8Host.java | 2 +- .../javet/interop/engine/JavetEnginePool.java | 36 +++++++++--------- .../com/caoccao/javet/BaseTestJavetPool.java | 7 ++-- .../caoccao/javet/BaseTestJavetRuntime.java | 1 + .../TestJavetStandardConsoleInterceptor.java | 2 +- .../javet/interop/TestJavetLibLoader.java | 2 + .../interop/engine/TestJavetEngineGuard.java | 1 + .../interop/engine/TestJavetEnginePool.java | 2 + .../values/reference/TestV8ValueFunction.java | 38 +++++++++---------- .../values/reference/TestV8ValueObject.java | 6 +-- 10 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/caoccao/javet/interop/V8Host.java b/src/main/java/com/caoccao/javet/interop/V8Host.java index a7de1c236..10a9300af 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Host.java +++ b/src/main/java/com/caoccao/javet/interop/V8Host.java @@ -210,7 +210,7 @@ public void closeV8Runtime(V8Runtime v8Runtime) { if (v8Runtime != null) { final long handle = v8Runtime.getHandle(); if (handle > INVALID_HANDLE && v8RuntimeMap.containsKey(handle)) { - v8Native.closeV8Runtime(v8Runtime.getHandle()); + v8Native.closeV8Runtime(handle); v8RuntimeMap.remove(handle); } } diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java index 151bfaa5a..aa634790e 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java @@ -150,24 +150,7 @@ public void run() { if (engine.isActive()) { activeEngineList.add(engine); } else { - boolean autoSendGCNotification = config.isAutoSendGCNotification(); - if (config.getMaxEngineUsedCount() > 0) { - JavetEngineUsage usage = engine.getUsage(); - ZonedDateTime resetEngineZonedDateTime = usage.getLastActiveZonedDatetime() - .plus(config.getResetEngineTimeoutSeconds(), ChronoUnit.SECONDS); - if (usage.getEngineUsedCount() >= config.getMaxEngineUsedCount() || - resetEngineZonedDateTime.isBefore(getUTCNow())) { - try { - logger.debug("JavetEnginePool reset engine begins."); - engine.resetContext(); - autoSendGCNotification = false; - logger.debug("JavetEnginePool reset engine ends."); - } catch (Exception e) { - logger.logError(e, "Failed to reset idle engine."); - } - } - } - if (autoSendGCNotification) { + if (config.isAutoSendGCNotification()) { engine.sendGCNotification(); } idleEngineList.add(engine); @@ -175,6 +158,7 @@ public void run() { } final int idleEngineCount = getIdleEngineCount(); for (int i = config.getPoolMinSize(); i < idleEngineCount; ++i) { + final int immediateIdleEngineCount = getIdleEngineCount(); JavetEngine engine = idleEngineList.poll(); if (engine == null) { break; @@ -182,7 +166,7 @@ public void run() { JavetEngineUsage usage = engine.getUsage(); ZonedDateTime expirationZonedDateTime = usage.getLastActiveZonedDatetime() .plus(config.getPoolIdleTimeoutSeconds(), ChronoUnit.SECONDS); - if (getIdleEngineCount() > config.getPoolMaxSize() || expirationZonedDateTime.isBefore(getUTCNow())) { + if (immediateIdleEngineCount > config.getPoolMaxSize() || expirationZonedDateTime.isBefore(getUTCNow())) { try { engine.close(true); } catch (Throwable t) { @@ -190,6 +174,20 @@ public void run() { } } else { idleEngineList.add(engine); + if (config.getMaxEngineUsedCount() > 0) { + ZonedDateTime resetEngineZonedDateTime = usage.getLastActiveZonedDatetime() + .plus(config.getResetEngineTimeoutSeconds(), ChronoUnit.SECONDS); + if (usage.getEngineUsedCount() >= config.getMaxEngineUsedCount() || + resetEngineZonedDateTime.isBefore(getUTCNow())) { + try { + logger.debug("JavetEnginePool reset engine begins."); + engine.resetContext(); + logger.debug("JavetEnginePool reset engine ends."); + } catch (Exception e) { + logger.logError(e, "Failed to reset idle engine."); + } + } + } } } synchronized (externalLock) { diff --git a/src/test/java/com/caoccao/javet/BaseTestJavetPool.java b/src/test/java/com/caoccao/javet/BaseTestJavetPool.java index ef2ff8b0b..b04790592 100644 --- a/src/test/java/com/caoccao/javet/BaseTestJavetPool.java +++ b/src/test/java/com/caoccao/javet/BaseTestJavetPool.java @@ -18,20 +18,21 @@ package com.caoccao.javet; import com.caoccao.javet.exceptions.JavetException; -import com.caoccao.javet.interop.V8Host; import com.caoccao.javet.interop.engine.IJavetEnginePool; import com.caoccao.javet.interop.engine.JavetEnginePool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import static org.junit.jupiter.api.Assertions.assertEquals; + public abstract class BaseTestJavetPool extends BaseTestJavet { protected IJavetEnginePool javetEnginePool; @AfterEach public void afterEach() throws JavetException { javetEnginePool.close(); - V8Host.getNodeInstance().clearInternalStatistic(); - V8Host.getV8Instance().clearInternalStatistic(); + v8Host.clearInternalStatistic(); + assertEquals(0, v8Host.getNodeInstance().getV8RuntimeCount()); } @BeforeEach diff --git a/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java b/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java index 0a87224f2..0dfedf4a5 100644 --- a/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java +++ b/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java @@ -52,6 +52,7 @@ public void afterEach() throws JavetException { Arrays.copyOfRange(counters, 7, 13)); } v8Host.clearInternalStatistic(); + assertEquals(0, v8Host.getV8RuntimeCount()); } @BeforeEach diff --git a/src/test/java/com/caoccao/javet/interception/logging/TestJavetStandardConsoleInterceptor.java b/src/test/java/com/caoccao/javet/interception/logging/TestJavetStandardConsoleInterceptor.java index 0e51257e4..472609bc9 100644 --- a/src/test/java/com/caoccao/javet/interception/logging/TestJavetStandardConsoleInterceptor.java +++ b/src/test/java/com/caoccao/javet/interception/logging/TestJavetStandardConsoleInterceptor.java @@ -56,6 +56,6 @@ public void test() throws IOException, JavetException { byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()).trim()); } } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } } diff --git a/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java index ebed6f98b..1c7061f09 100644 --- a/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java +++ b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java @@ -70,6 +70,7 @@ protected void testLoadAndUnload(JSRuntimeType jsRuntimeType) throws JavetExcept try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); } + assertEquals(0, v8Host.getV8RuntimeCount()); assertTrue(v8Host.unloadLibrary()); assertFalse(v8Host.isLibraryLoaded()); try { @@ -82,5 +83,6 @@ protected void testLoadAndUnload(JSRuntimeType jsRuntimeType) throws JavetExcept try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); } + assertEquals(0, v8Host.getV8RuntimeCount()); } } diff --git a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java index 43108326c..235e9b9b2 100644 --- a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java +++ b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java @@ -37,6 +37,7 @@ public void testTermination() throws JavetException { V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); // Get a guard from the engine and apply try-with-resource pattern. try (IJavetEngineGuard iJavetEngineGuard = iJavetEngine.getGuard(1)) { + iJavetEngineGuard.enableInDebugMode(); v8Runtime.getExecutor("while (true) {}").executeVoid(); // That infinite loop will be terminated in 10 seconds by the guard. } catch (JavetTerminatedException e) { diff --git a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java index 433cd4810..4c86de0a3 100644 --- a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java +++ b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java @@ -44,6 +44,7 @@ private void afterEach() throws JavetException { assertEquals(0, javetEnginePool.getActiveEngineCount()); assertEquals(0, javetEnginePool.getIdleEngineCount()); assertFalse(javetEnginePool.isActive()); + assertEquals(0, v8Host.getV8RuntimeCount()); } @BeforeEach @@ -52,6 +53,7 @@ private void beforeEach() { assertTrue(javetEnginePool.isActive()); javetEngineConfig = javetEnginePool.getConfig(); javetEngineConfig.setPoolDaemonCheckIntervalMillis(TEST_POOL_DAEMON_CHECK_INTERVAL_MILLIS); + javetEngineConfig.setJSRuntimeType(v8Host.getJSRuntimeType()); } @Test diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java index e8e34bae7..a500b1fe5 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java @@ -112,7 +112,7 @@ public void testAnnotationBasedFunctions() throws JavetException { assertEquals(String.valueOf((char) 1), v8Runtime.getExecutor("a.primitiveIncreaseChar(undefined)").executeString()); v8Runtime.getGlobalObject().delete("a"); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -137,7 +137,7 @@ public void testAnnotationBasedFunctionsAndProperties() throws JavetException { assertEquals(4, mockAnnotationBasedCallbackReceiver.getCount()); v8Runtime.getGlobalObject().delete("a"); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -158,7 +158,7 @@ public void testAnonymousFunction() throws JavetException { assertEquals(2, v8Runtime.getExecutor("b(1);").executeInteger()); v8Runtime.getGlobalObject().delete("a"); v8Runtime.getGlobalObject().delete("b"); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -174,7 +174,7 @@ public void testArrayPush() throws JavetException { assertEquals(1, v8ValueArray.getLength()); assertEquals("x", v8ValueArray.toString()); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -215,7 +215,7 @@ public void testCallbackBlankWithThis() throws JavetException, NoSuchMethodExcep globalObject.delete("a"); } assertEquals(1, v8Runtime.getReferenceCount()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); assertEquals(0, v8Runtime.getReferenceCount()); try { v8ValueFunction.close(); @@ -242,7 +242,7 @@ public void testCallbackBlankWithoutThis() throws JavetException, NoSuchMethodEx globalObject.delete("a"); } assertEquals(1, v8Runtime.getReferenceCount()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); assertEquals(0, v8Runtime.getReferenceCount()); try { v8ValueFunction.close(); @@ -277,7 +277,7 @@ public void testCallbackEchoLILOWithThis() throws JavetException, NoSuchMethodEx assertTrue(mockCallbackReceiver.isCalled()); globalObject.delete("echoThis"); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -301,7 +301,7 @@ public void testCallbackEchoLILOWithoutThis() throws JavetException, NoSuchMetho assertTrue(mockCallbackReceiver.isCalled()); globalObject.delete("echo"); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -326,7 +326,7 @@ public void testCallbackEchoStringWithThis() throws JavetException, NoSuchMethod globalObject.delete("a"); } assertTrue(mockCallbackReceiver.isCalled()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -351,7 +351,7 @@ public void testCallbackEchoStringWithoutThis() throws JavetException, NoSuchMet assertEquals("abc,def", v8Runtime.getExecutor("echoString('abc', 'def')").executeString()); assertEquals("", v8Runtime.getExecutor("echoString()").executeString()); globalObject.delete("echoString"); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -375,7 +375,7 @@ public void testCallbackEchoVIVOWithThis() throws JavetException, NoSuchMethodEx globalObject.delete("a"); } assertTrue(mockCallbackReceiver.isCalled()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -396,7 +396,7 @@ public void testCallbackEchoVIVOWithoutThis() throws JavetException, NoSuchMetho } assertTrue(mockCallbackReceiver.isCalled()); assertTrue(globalObject.get("a").isUndefined()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -422,7 +422,7 @@ public void testCallbackError() throws JavetException, NoSuchMethodException { } globalObject.delete("testError"); mockCallbackReceiver.setCalled(false); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -449,7 +449,7 @@ public void testCallbackJoinIntegerArrayWithThis() throws JavetException, NoSuch globalObject.delete("x"); } assertTrue(mockCallbackReceiver.isCalled()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -477,7 +477,7 @@ public void testCallbackJoinWithThis() throws JavetException, NoSuchMethodExcept globalObject.delete("x"); } assertTrue(mockCallbackReceiver.isCalled()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -500,7 +500,7 @@ public void testCallbackJoinWithoutThis() throws JavetException, NoSuchMethodExc globalObject.delete("joinWithoutThis"); } assertTrue(mockCallbackReceiver.isCalled()); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -529,7 +529,7 @@ public void testContextScope() throws JavetException { e.printStackTrace(); fail(e.getScriptingError().toString()); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } /** @@ -611,7 +611,7 @@ public void testPropertyGetter() throws NoSuchMethodException, JavetException { assertTrue(mockCallbackReceiver.isCalled()); globalObject.delete("a"); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -646,6 +646,6 @@ public void testPropertyGetterAndSetter() throws NoSuchMethodException, JavetExc assertTrue(mockCallbackReceiver.isCalled()); globalObject.delete("a"); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } } diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java index afa2889f1..4cb0f991c 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java @@ -65,7 +65,7 @@ public void testAnnotationBasedProperties() throws JavetException { assertEquals(10, mockAnnotationBasedCallbackReceiver.getCount()); v8Runtime.getGlobalObject().delete("a"); } - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test @@ -331,7 +331,7 @@ public void testSetWeakDirectDescendant() throws JavetException { assertEquals(1, v8Runtime.getReferenceCount()); a.setWeak(); globalObject.delete("a"); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); assertEquals(0, v8Runtime.getReferenceCount()); assertEquals(0L, a.getHandle()); assertTrue(globalObject.get("a").isUndefined()); @@ -348,7 +348,7 @@ public void testSetWeakIndirectDescendant() throws JavetException { } assertEquals(1, v8Runtime.getReferenceCount()); globalObject.delete("a"); - v8Runtime.requestGarbageCollectionForTesting(true); + v8Runtime.lowMemoryNotification(); } @Test From 1b1eb036bc64e0d44f16239acc42557cf366a49d Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Fri, 11 Jun 2021 22:14:25 +0800 Subject: [PATCH 11/12] Reverted idle engine list processing logic --- .../java/com/caoccao/javet/interop/engine/JavetEnginePool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java index aa634790e..5d154fd47 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java @@ -173,7 +173,6 @@ public void run() { logger.logError(t, "Failed to release idle engine."); } } else { - idleEngineList.add(engine); if (config.getMaxEngineUsedCount() > 0) { ZonedDateTime resetEngineZonedDateTime = usage.getLastActiveZonedDatetime() .plus(config.getResetEngineTimeoutSeconds(), ChronoUnit.SECONDS); @@ -188,6 +187,7 @@ public void run() { } } } + idleEngineList.add(engine); } } synchronized (externalLock) { From 54aaf6ccb12e911e663c1a6ec84054a23ed14fae Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Sat, 12 Jun 2021 07:29:57 +0800 Subject: [PATCH 12/12] Removed maxEngineUsedCount from JavetEngineConfig --- docs/tutorial/spring_integration.rst | 2 -- .../javet/interop/engine/JavetEngineConfig.java | 12 ------------ .../javet/interop/engine/JavetEnginePool.java | 5 ++--- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/docs/tutorial/spring_integration.rst b/docs/tutorial/spring_integration.rst index a30e18f4c..341ce7848 100644 --- a/docs/tutorial/spring_integration.rst +++ b/docs/tutorial/spring_integration.rst @@ -24,7 +24,6 @@ Configuration javetEngineConfigNode.setAutoSendGCNotification(...); javetEngineConfigNode.setDefaultEngineGuardTimeoutMillis(...); javetEngineConfigNode.setEngineGuardCheckIntervalMillis(...); - javetEngineConfigNode.setMaxEngineUsedCount(...); javetEngineConfigNode.setPoolDaemonCheckIntervalMillis(...); javetEngineConfigNode.setPoolIdleTimeoutSeconds(...); javetEngineConfigNode.setPoolMinSize(...); @@ -43,7 +42,6 @@ Configuration javetEngineConfigV8.setAutoSendGCNotification(...); javetEngineConfigV8.setDefaultEngineGuardTimeoutMillis(...); javetEngineConfigV8.setEngineGuardCheckIntervalMillis(...); - javetEngineConfigV8.setMaxEngineUsedCount(...); javetEngineConfigV8.setPoolDaemonCheckIntervalMillis(...); javetEngineConfigV8.setPoolIdleTimeoutSeconds(...); javetEngineConfigV8.setPoolMinSize(...); diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java index 401eb293e..e2f394e4b 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java @@ -29,7 +29,6 @@ public final class JavetEngineConfig { public static final int DEFAULT_ENGINE_GUARD_TIMEOUT_MILLIS = 30000; public static final int DEFAULT_ENGINE_GUARD_CHECK_INTERVAL_MILLIS = 1000; public static final JSRuntimeType DEFAULT_JS_RUNTIME_TYPE = JSRuntimeType.V8; - public static final int DEFAULT_MAX_ENGINE_USED_COUNT = 100; public static final int DEFAULT_POOL_MIN_SIZE = 1; public static final int DEFAULT_POOL_IDLE_TIMEOUT_SECONDS = 60; public static final int DEFAULT_POOL_DAEMON_CHECK_INTERVAL_MILLIS = 1000; @@ -46,7 +45,6 @@ public final class JavetEngineConfig { private String globalName; private IJavetLogger javetLogger; private JSRuntimeType jsRuntimeType; - private int maxEngineUsedCount; private int poolDaemonCheckIntervalMillis; private int poolIdleTimeoutSeconds; private int poolMaxSize; @@ -82,10 +80,6 @@ public IJavetLogger getJavetLogger() { return javetLogger; } - public int getMaxEngineUsedCount() { - return maxEngineUsedCount; - } - public int getPoolDaemonCheckIntervalMillis() { return poolDaemonCheckIntervalMillis; } @@ -132,7 +126,6 @@ public JavetEngineConfig reset() { engineGuardCheckIntervalMillis = DEFAULT_ENGINE_GUARD_CHECK_INTERVAL_MILLIS; gcBeforeEngineClose = false; jsRuntimeType = DEFAULT_JS_RUNTIME_TYPE; - maxEngineUsedCount = DEFAULT_MAX_ENGINE_USED_COUNT; final int cpuCount = JavetOSUtils.getCPUCount(); poolMinSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount >> 1); poolMaxSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount); @@ -192,11 +185,6 @@ public JavetEngineConfig setJavetLogger(IJavetLogger javetLogger) { return this; } - public JavetEngineConfig setMaxEngineUsedCount(int maxEngineUsedCount) { - this.maxEngineUsedCount = maxEngineUsedCount; - return this; - } - @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setPoolDaemonCheckIntervalMillis(int poolDaemonCheckIntervalMillis) { this.poolDaemonCheckIntervalMillis = poolDaemonCheckIntervalMillis; diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java index 5d154fd47..07d6f8787 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java @@ -173,11 +173,10 @@ public void run() { logger.logError(t, "Failed to release idle engine."); } } else { - if (config.getMaxEngineUsedCount() > 0) { + if (config.getResetEngineTimeoutSeconds() > 0) { ZonedDateTime resetEngineZonedDateTime = usage.getLastActiveZonedDatetime() .plus(config.getResetEngineTimeoutSeconds(), ChronoUnit.SECONDS); - if (usage.getEngineUsedCount() >= config.getMaxEngineUsedCount() || - resetEngineZonedDateTime.isBefore(getUTCNow())) { + if (resetEngineZonedDateTime.isBefore(getUTCNow())) { try { logger.debug("JavetEnginePool reset engine begins."); engine.resetContext();