diff --git a/fine-j2v8/src/com/eclipsesource/v8/LibraryLoader.java b/fine-j2v8/src/com/eclipsesource/v8/LibraryLoader.java
index 84b9ba4aa..c3ef4351f 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/LibraryLoader.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/LibraryLoader.java
@@ -7,6 +7,7 @@
*
* Contributors:
* EclipseSource - initial API and implementation
+ * Wolfgang Steiner - code separation PlatformDetector/LibraryLoader
******************************************************************************/
package com.eclipsesource.v8;
@@ -27,49 +28,86 @@ class LibraryLoader {
SEPARATOR = System.getProperty("file.separator"); //$NON-NLS-1$
}
- private static String computeLibraryShortName() {
- String base = "j2v8";
- String osSuffix = getOS();
- String archSuffix = getArchSuffix();
- return base + "_" + osSuffix + "_" + archSuffix;
+ /**
+ * Returns the base-name for the native J2V8 library file.
+ * @param withLinuxVendor include/exclude the {vendor} part from the returned filename
+ *
NOTE: Vendors are only included for linux systems
+ * @return The filename string has the following structure:
+ *
{arch}-[vendor]-{operating_system}
+ */
+ public static String computeLibraryShortName(boolean withLinuxVendor) {
+ String prefix = "j2v8";
+ String vendor = withLinuxVendor && PlatformDetector.OS.isLinux() ? PlatformDetector.Vendor.getName() : null;
+ String os = PlatformDetector.OS.getName();
+ String arch = PlatformDetector.Arch.getName();
+
+ final String separator = "-";
+
+ return
+ prefix +
+ (vendor != null ? separator + vendor : "") +
+ separator + os +
+ separator + arch;
}
- private static String computeLibraryFullName() {
- return "lib" + computeLibraryShortName() + "." + getOSFileExtension();
+ public static String computeLibraryFullName(boolean withLinuxVendor) {
+ return "lib" + computeLibraryShortName(withLinuxVendor) + "." + PlatformDetector.OS.getLibFileExtension();
}
- static void loadLibrary(final String tempDirectory) {
- if ( isAndroid() ) {
- System.loadLibrary("j2v8");
- return;
- }
- StringBuffer message = new StringBuffer();
- String libShortName = computeLibraryShortName();
- String libFullName = computeLibraryFullName();
- String ideLocation = System.getProperty("user.dir") + SEPARATOR + "jni" + SEPARATOR + computeLibraryFullName();
- String path = null;
+ static boolean tryLoad(boolean withLinuxVendor, StringBuffer message) {
+ String libShortName = computeLibraryShortName(withLinuxVendor);
+ String libFullName = computeLibraryFullName(withLinuxVendor);
+ String ideLocation = System.getProperty("user.dir") + SEPARATOR + "jni" + SEPARATOR + libFullName;
/* Try loading library from java library path */
+ if (load(libFullName, message)) {
+ return true;
+ }
if (load(libShortName, message)) {
- return;
+ return true;
}
/* Try loading library from the IDE location */
if (new File(ideLocation).exists()) {
if (load(ideLocation, message)) {
- return;
+ return true;
}
}
+ return false;
+ }
+
+ static void loadLibrary(final String tempDirectory) {
+ if (PlatformDetector.OS.isAndroid()) {
+ System.loadLibrary("j2v8");
+ return;
+ }
+
+ StringBuffer message = new StringBuffer();
+
+ // try loading a vendor-specific library first
+ if (tryLoad(true, message))
+ return;
+
+ // if there is no vendor-specific library, just try to load the default OS library
+ if (tryLoad(false, message))
+ return;
+
+ String path = null;
+
if (tempDirectory != null) {
path = tempDirectory;
} else {
- path = System.getProperty("user.home"); //$NON-NLS-1$
+ path = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
}
- if (extract(path + SEPARATOR + libFullName, libFullName, message)) {
+ // try extracting a vendor-specific library first
+ if (extract(path, true, message))
+ return;
+
+ // if there is no vendor-specific library, just try to extract the default OS library
+ if (extract(path, false, message))
return;
- }
/* Failed to find the library */
throw new UnsatisfiedLinkError("Could not load J2V8 library. Reasons: " + message.toString()); //$NON-NLS-1$
@@ -94,10 +132,17 @@ class LibraryLoader {
return false;
}
+ static boolean extract(String libPath, boolean withLinuxVendor, StringBuffer message) {
+ String libFullName = computeLibraryFullName(withLinuxVendor);
+ return extract(libPath + SEPARATOR + libFullName, libFullName, message);
+ }
+
static boolean extract(final String fileName, final String mappedName, final StringBuffer message) {
FileOutputStream os = null;
InputStream is = null;
File file = new File(fileName);
+ //这部分自己修改过,主要是以为linux通过System.getProperty("java.io.tmpdir")获取到的是相对路径,但是System.load方法加载需要文件的绝对路径。
+ String absoluteName = file.getAbsolutePath();
boolean extracted = false;
try {
if (file.exists()) {
@@ -115,7 +160,7 @@ class LibraryLoader {
os.close();
is.close();
chmod("755", fileName);
- if (load(fileName, message)) {
+ if (load(absoluteName, message)) {
return true;
}
}
@@ -140,7 +185,7 @@ class LibraryLoader {
}
static void chmod(final String permision, final String path) {
- if (isWindows()) {
+ if (PlatformDetector.OS.isWindows()) {
return;
}
try {
@@ -148,69 +193,4 @@ class LibraryLoader {
} catch (Throwable e) {
}
}
-
- static String getOsName() {
- return System.getProperty("os.name") + System.getProperty("java.specification.vendor");
- }
-
- static boolean isWindows() {
- return getOsName().startsWith("Windows");
- }
-
- static boolean isMac() {
- return getOsName().startsWith("Mac");
- }
-
- static boolean isLinux() {
- return getOsName().startsWith("Linux");
- }
-
- static boolean isNativeClient() {
- return getOsName().startsWith("nacl");
- }
-
- static boolean isAndroid() {
- return getOsName().contains("Android");
- }
-
- static String getArchSuffix() {
- String arch = System.getProperty("os.arch");
- if (arch.equals("i686")) {
- return "x86";
- } else if (arch.equals("amd64")) {
- return "x86_64";
- } else if (arch.equals("nacl")) {
- return "armv7l";
- } else if (arch.equals("aarch64")) {
- return "armv7l";
- }
- return arch;
- }
-
- static String getOSFileExtension() {
- if (isWindows()) {
- return "dll";
- } else if (isMac()) {
- return "dylib";
- } else if (isLinux()) {
- return "so";
- } else if (isNativeClient()) {
- return "so";
- }
- throw new UnsatisfiedLinkError("Unsupported platform: " + getOsName());
- }
-
- static String getOS() {
- if (isWindows()) {
- return "win32";
- } else if (isMac()) {
- return "macosx";
- } else if (isLinux() && !isAndroid()) {
- return "linux";
- } else if (isAndroid()) {
- return "android";
- }
- throw new UnsatisfiedLinkError("Unsupported platform: " + getOsName());
- }
-
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/NodeJS.java b/fine-j2v8/src/com/eclipsesource/v8/NodeJS.java
index 0877ff026..30d148364 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/NodeJS.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/NodeJS.java
@@ -29,6 +29,9 @@ public class NodeJS {
private static final String STARTUP_CALLBACK = "__run";
private static final String STARTUP_SCRIPT = "global." + STARTUP_CALLBACK + "(require, exports, module, __filename, __dirname);";
private static final String STARTUP_SCRIPT_NAME = "startup";
+ private static final String VERSIONS = "versions";
+ private static final String NODE = "node";
+ private String nodeVersion = null;
private V8 v8;
private V8Function require;
@@ -45,6 +48,29 @@ public class NodeJS {
return createNodeJS(null);
}
+ /**
+ * Returns the version of Node.js that is runtime is built against.
+ * This uses process.versions.node to get the version.
+ *
+ * @return The version of Node.js.
+ */
+ public String getNodeVersion() {
+ if (nodeVersion != null) {
+ return nodeVersion;
+ }
+ V8Object process = null;
+ V8Object versions = null;
+ try {
+ process = v8.getObject(PROCESS);
+ versions = process.getObject(VERSIONS);
+ nodeVersion = versions.getString(NODE);
+ } finally {
+ safeRelease(process);
+ safeRelease(versions);
+ }
+ return nodeVersion;
+ }
+
/**
* Creates a NodeJS runtime and executes a JS Script
*
@@ -65,7 +91,7 @@ public class NodeJS {
try {
node.init(require.twin());
} finally {
- require.release();
+ require.close();
}
}
}, STARTUP_CALLBACK);
@@ -111,10 +137,10 @@ public class NodeJS {
public void release() {
v8.checkThread();
if (!require.isReleased()) {
- require.release();
+ require.close();
}
if (!v8.isReleased()) {
- v8.release();
+ v8.close();
}
}
@@ -142,7 +168,7 @@ public class NodeJS {
requireParams.push(file.getAbsolutePath());
return (V8Object) require.call(null, requireParams);
} finally {
- requireParams.release();
+ requireParams.close();
}
}
@@ -179,7 +205,7 @@ public class NodeJS {
requireParams.push(file.getAbsolutePath());
return require.call(null, requireParams);
} finally {
- requireParams.release();
+ requireParams.close();
}
}
});
diff --git a/fine-j2v8/src/com/eclipsesource/v8/Platform.java b/fine-j2v8/src/com/eclipsesource/v8/Platform.java
new file mode 100644
index 000000000..8168935bc
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/Platform.java
@@ -0,0 +1,12 @@
+package com.eclipsesource.v8;
+
+public class Platform {
+ public static final String ANDROID = "android";
+ public static final String LINUX = "linux";
+ public static final String MACOSX = "macosx";
+ public static final String WINDOWS = "windows";
+
+ public static final String NATIVE_CLIENT = "nacl";
+
+ public static final String UNKNOWN = "unknown";
+}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/PlatformDetector.java b/fine-j2v8/src/com/eclipsesource/v8/PlatformDetector.java
new file mode 100644
index 000000000..510bee075
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/PlatformDetector.java
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Trustin Lee - original OS/Arch/Vendor detection code (see: https://github.com/trustin/os-maven-plugin)
+ * Wolfgang Steiner - initial API and implementation
+ *
+ * Copyright 2014 Trustin Heuiseung Lee.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.eclipsesource.v8;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Locale;
+
+public class PlatformDetector {
+ public static class Arch {
+ public static String getName() {
+ final String archProperty = System.getProperty("os.arch");
+ final String archName = normalizeArch(archProperty);
+
+ if (archName.equals(Platform.UNKNOWN)) {
+ throw new UnsatisfiedLinkError("Unsupported arch: " + archProperty);
+ }
+
+ return archName;
+ }
+ }
+
+ public static class OS {
+ public static String getName() {
+ final String osProperty = System.getProperty("os.name");
+ final String osName = normalizeOs(osProperty);
+
+ final String vendorProperty = System.getProperty("java.specification.vendor");
+ final String vendorName = normalize(vendorProperty);
+
+ // special handling for android
+ if (vendorName.contains("android") || osName.contains("android")) {
+ return Platform.ANDROID;
+ }
+
+ if (osName.equals(Platform.UNKNOWN)) {
+ throw new UnsatisfiedLinkError("Unsupported platform/vendor: " + osProperty + " / " + vendorProperty);
+ }
+
+ return osName;
+ }
+
+ public static boolean isWindows() {
+ return getName().equals(Platform.WINDOWS);
+ }
+
+ public static boolean isMac() {
+ return getName().equals(Platform.MACOSX);
+ }
+
+ public static boolean isLinux() {
+ return getName().equals(Platform.LINUX);
+ }
+
+ public static boolean isNativeClient() {
+ return getName().equals(Platform.NATIVE_CLIENT);
+ }
+
+ public static boolean isAndroid() {
+ return getName().equals(Platform.ANDROID);
+ }
+
+ public static String getLibFileExtension() {
+ if (isWindows()) {
+ return "dll";
+ }
+
+ if (isMac()) {
+ return "dylib";
+ }
+
+ if (isLinux()
+ || isAndroid()
+ || isNativeClient()) {
+ return "so";
+ }
+
+ throw new UnsatisfiedLinkError("Unsupported platform library-extension for: " + getName());
+ }
+ }
+
+ public static class Vendor {
+ private static final String[] LINUX_OS_RELEASE_FILES = {"/etc/os-release", "/usr/lib/os-release"};
+ private static final String REDHAT_RELEASE_FILE = "/etc/redhat-release";
+ private static final String LINUX_ID_PREFIX = "ID=";
+
+ public static String getName() {
+ if (OS.isWindows()) {
+ return "microsoft";
+ }
+ if (OS.isMac()) {
+ return "apple";
+ }
+ if (OS.isLinux()) {
+ return getLinuxOsReleaseId();
+ }
+ if (OS.isAndroid()) {
+ return "google";
+ }
+
+ throw new UnsatisfiedLinkError("Unsupported vendor: " + getName());
+ }
+
+ private static String getLinuxOsReleaseId() {
+ // First, look for the os-release file.
+ for (String osReleaseFileName : LINUX_OS_RELEASE_FILES) {
+ File file = new File(osReleaseFileName);
+ if (file.exists()) {
+ return parseLinuxOsReleaseFile(file);
+ }
+ }
+
+ // Older versions of redhat don't have /etc/os-release. In this case, try
+ // parsing this file.
+ File file = new File(REDHAT_RELEASE_FILE);
+ if (file.exists()) {
+ return parseLinuxRedhatReleaseFile(file);
+ }
+
+ throw new UnsatisfiedLinkError("Unsupported linux vendor: " + getName());
+ }
+
+ private static String parseLinuxOsReleaseFile(final File file) {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
+
+ String id = null;
+ String line;
+ while((line = reader.readLine()) != null) {
+ // Parse the ID line.
+ if (line.startsWith(LINUX_ID_PREFIX)) {
+ // Set the ID for this version.
+ id = normalizeOsReleaseValue(line.substring(LINUX_ID_PREFIX.length()));
+ break;
+ }
+ }
+
+ return id;
+ } catch (IOException ignored) {
+ // Just absorb. Don't treat failure to read /etc/os-release as an error.
+ } finally {
+ closeQuietly(reader);
+ }
+ return null;
+ }
+
+ private static String parseLinuxRedhatReleaseFile(final File file) {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
+
+ // There is only a single line in this file.
+ String line = reader.readLine();
+ if (line != null) {
+ line = line.toLowerCase(Locale.US);
+
+ String id;
+ if (line.contains("centos")) {
+ id = "centos";
+ } else if (line.contains("fedora")) {
+ id = "fedora";
+ } else if (line.contains("red hat enterprise linux")) {
+ id = "rhel";
+ } else {
+ // Other variants are not currently supported.
+ return null;
+ }
+
+ return id;
+ }
+ } catch (IOException ignored) {
+ // Just absorb. Don't treat failure to read /etc/os-release as an error.
+ } finally {
+ closeQuietly(reader);
+ }
+ return null;
+ }
+
+ private static void closeQuietly(final Closeable obj) {
+ try {
+ if (obj != null) {
+ obj.close();
+ }
+ } catch (IOException ignored) {
+ // Ignore.
+ }
+ }
+ }
+
+ private static String normalizeOsReleaseValue(final String value) {
+ // Remove any quotes from the string.
+ return value.trim().replace("\"", "");
+ }
+
+ private static String normalizeOs(String value) {
+ value = normalize(value);
+ if (value.startsWith("aix")) {
+ return "aix";
+ }
+ if (value.startsWith("hpux")) {
+ return "hpux";
+ }
+ if (value.startsWith("os400")) {
+ // Avoid the names such as os4000
+ if ((value.length() <= 5) || !Character.isDigit(value.charAt(5))) {
+ return "os400";
+ }
+ }
+ if (value.startsWith("android")) {
+ return Platform.ANDROID;
+ }
+ if (value.startsWith("linux")) {
+ return Platform.LINUX;
+ }
+ if (value.startsWith("nacl")) {
+ return Platform.NATIVE_CLIENT;
+ }
+ if (value.startsWith("macosx") || value.startsWith("osx")) {
+ return Platform.MACOSX;
+ }
+ if (value.startsWith("freebsd")) {
+ return "freebsd";
+ }
+ if (value.startsWith("openbsd")) {
+ return "openbsd";
+ }
+ if (value.startsWith("netbsd")) {
+ return "netbsd";
+ }
+ if (value.startsWith("solaris") || value.startsWith("sunos")) {
+ return "sunos";
+ }
+ if (value.startsWith("windows")) {
+ return Platform.WINDOWS;
+ }
+
+ return Platform.UNKNOWN;
+ }
+
+ private static String normalizeArch(String value) {
+ value = normalize(value);
+ if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) {
+ return "x86_64";
+ }
+ if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) {
+ return "x86_32";
+ }
+ if (value.matches("^(ia64|itanium64)$")) {
+ return "itanium_64";
+ }
+ if (value.matches("^(sparc|sparc32)$")) {
+ return "sparc_32";
+ }
+ if (value.matches("^(sparcv9|sparc64)$")) {
+ return "sparc_64";
+ }
+ if (value.matches("^(arm|arm32)$")) {
+ return "arm_32";
+ }
+ if ("aarch64".equals(value)) {
+ return "aarch_64";
+ }
+ if (value.matches("^(ppc|ppc32)$")) {
+ return "ppc_32";
+ }
+ if ("ppc64".equals(value)) {
+ return "ppc_64";
+ }
+ if ("ppc64le".equals(value)) {
+ return "ppcle_64";
+ }
+ if ("s390".equals(value)) {
+ return "s390_32";
+ }
+ if ("s390x".equals(value)) {
+ return "s390_64";
+ }
+
+ return Platform.UNKNOWN;
+ }
+
+ private static String normalize(final String value) {
+ if (value == null) {
+ return "";
+ }
+ return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
+ }
+}
\ No newline at end of file
diff --git a/fine-j2v8/src/com/eclipsesource/v8/Releasable.java b/fine-j2v8/src/com/eclipsesource/v8/Releasable.java
index 0017787da..3edee1e61 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/Releasable.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/Releasable.java
@@ -10,15 +10,21 @@
******************************************************************************/
package com.eclipsesource.v8;
+import java.io.Closeable;
+
/**
* An interface used to denote all V8 Classes which can be released.
*/
-public interface Releasable {
+public interface Releasable extends Closeable {
/**
* Release the underlying resources. Once an object is released
* it typically cannot be used again.
*/
- void release();
+ void close();
+ /**
+ * Synonym for {@link #close()}.
+ */
+ void release();
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8.java b/fine-j2v8/src/com/eclipsesource/v8/V8.java
index 9d8d86849..077ff6de1 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8.java
@@ -22,6 +22,7 @@ import java.util.Set;
import com.eclipsesource.v8.utils.V8Executor;
import com.eclipsesource.v8.utils.V8Map;
+import com.eclipsesource.v8.utils.V8Runnable;
/**
* An isolated V8Runtime. All JavaScript execution must exist
@@ -38,11 +39,13 @@ import com.eclipsesource.v8.utils.V8Map;
*/
public class V8 extends V8Object {
- private static Object lock = new Object();
- private volatile static int runtimeCounter = 0;
- private static String v8Flags = null;
- private static boolean initialized = false;
+ private static Object lock = new Object();
+ private volatile static int runtimeCounter = 0;
+ private static String v8Flags = null;
+ private static boolean initialized = false;
+ protected Map v8WeakReferences = new HashMap();
+ private Map data = null;
private final V8Locker locker;
private long objectReferences = 0;
private long v8RuntimePtr = 0;
@@ -51,12 +54,13 @@ public class V8 extends V8Object {
private boolean forceTerminateExecutors = false;
private Map functionRegistry = new HashMap();
private LinkedList referenceHandlers = new LinkedList();
+ private LinkedList releaseHandlers = new LinkedList();
- private static boolean nativeLibraryLoaded = false;
- private static Error nativeLoadError = null;
- private static Exception nativeLoadException = null;
- private static V8Value undefined = new Undefined();
- private static Object invalid = new Object();
+ private static boolean nativeLibraryLoaded = false;
+ private static Error nativeLoadError = null;
+ private static Exception nativeLoadException = null;
+ private static V8Value undefined = new V8Object.Undefined();
+ private static Object invalid = new Object();
private class MethodDescriptor {
Object object;
@@ -167,6 +171,16 @@ public class V8 extends V8Object {
referenceHandlers.add(0, handler);
}
+ /**
+ * Adds a handler that will be called when the runtime is being released.
+ * The runtime will still be available when the handler is executed.
+ *
+ * @param handler The handler to invoke when the runtime, is being released
+ */
+ public void addReleaseHandler(final V8Runnable handler) {
+ releaseHandlers.add(handler);
+ }
+
/**
* Removes an existing ReferenceHandler from the collection of reference handlers.
* If the ReferenceHandler does not exist in the collection, it is ignored.
@@ -177,6 +191,50 @@ public class V8 extends V8Object {
referenceHandlers.remove(handler);
}
+ /**
+ * Removes an existing release handler from the collection of release handlers.
+ * If the release handler does not exist in the collection, it is ignored.
+ *
+ * @param handler The handler to remove
+ */
+ public void removeReleaseHandler(final V8Runnable handler) {
+ releaseHandlers.remove(handler);
+ }
+
+ /**
+ * Associates an arbitrary object with this runtime.
+ *
+ * @param key The key used to reference this object
+ * @param value The object to associate with this runtime
+ */
+ public synchronized void setData(final String key, final Object value) {
+ if (data == null) {
+ data = new HashMap();
+ }
+ data.put(key, value);
+ }
+
+ /**
+ * Returns the data object associated with this runtime, null if no object
+ * has been associated.
+ *
+ * @param key The key used to reference this object
+ *
+ * @return The data object associated with this runtime, or null.
+ */
+ public Object getData(final String key) {
+ if (data == null) {
+ return null;
+ }
+ return data.get(key);
+ }
+
+ private void notifyReleaseHandlers(final V8 runtime) {
+ for (V8Runnable handler : releaseHandlers) {
+ handler.run(runtime);
+ }
+ }
+
private void notifyReferenceCreated(final V8Value object) {
for (ReferenceHandler referenceHandler : referenceHandlers) {
referenceHandler.v8HandleCreated(object);
@@ -191,12 +249,16 @@ public class V8 extends V8Object {
private static void checkNativeLibraryLoaded() {
if (!nativeLibraryLoaded) {
+ String vendorName = LibraryLoader.computeLibraryShortName(true);
+ String baseName = LibraryLoader.computeLibraryShortName(false);
+ String message = "J2V8 native library not loaded (" + baseName + "/" + vendorName + ")";
+
if (nativeLoadError != null) {
- throw new IllegalStateException("J2V8 native library not loaded", nativeLoadError);
+ throw new IllegalStateException(message, nativeLoadError);
} else if (nativeLoadException != null) {
- throw new IllegalStateException("J2V8 native library not loaded", nativeLoadException);
+ throw new IllegalStateException(message, nativeLoadException);
} else {
- throw new IllegalStateException("J2V8 native library not loaded");
+ throw new IllegalStateException(message);
}
}
}
@@ -208,9 +270,9 @@ public class V8 extends V8Object {
protected V8(final String globalAlias) {
super(null);
released = false;
- locker = new V8Locker();
- checkThread();
v8RuntimePtr = _createIsolate(globalAlias);
+ locker = new V8Locker(this);
+ checkThread();
objectHandle = _getGlobalObject(v8RuntimePtr);
}
@@ -238,7 +300,7 @@ public class V8 extends V8Object {
* @return The number of Object References on this runtime.
*/
public long getObjectReferenceCount() {
- return objectReferences;
+ return objectReferences - v8WeakReferences.size();
}
protected long getV8RuntimePtr() {
@@ -254,11 +316,32 @@ public class V8 extends V8Object {
return _getVersion();
}
+ /**
+ * Returns the revision ID of this version as specified
+ * by the source code management system. Currently we use
+ * Git, so this will return the commit ID for this revision.
+ *
+ * @return The revision ID of this version of J2V8
+ */
+ public static String getSCMRevision() {
+ return "Unknown revision ID";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.V8Value#close()
+ */
+ @Override
+ public void close() {
+ release(true);
+ }
+
/*
* (non-Javadoc)
* @see com.eclipsesource.v8.V8Value#release()
*/
@Override
+ @Deprecated
public void release() {
release(true);
}
@@ -286,20 +369,24 @@ public class V8 extends V8Object {
return;
}
checkThread();
- releaseResources();
- shutdownExecutors(forceTerminateExecutors);
- if (executors != null) {
- executors.clear();
- }
- releaseNativeMethodDescriptors();
- synchronized (lock) {
- runtimeCounter--;
- }
- _releaseRuntime(v8RuntimePtr);
- v8RuntimePtr = 0L;
- released = true;
- if (reportMemoryLeaks && (objectReferences > 0)) {
- throw new IllegalStateException(objectReferences + " Object(s) still exist in runtime");
+ try {
+ notifyReleaseHandlers(this);
+ } finally {
+ releaseResources();
+ shutdownExecutors(forceTerminateExecutors);
+ if (executors != null) {
+ executors.clear();
+ }
+ releaseNativeMethodDescriptors();
+ synchronized (lock) {
+ runtimeCounter--;
+ }
+ _releaseRuntime(v8RuntimePtr);
+ v8RuntimePtr = 0L;
+ released = true;
+ if (reportMemoryLeaks && (getObjectReferenceCount() > 0)) {
+ throw new IllegalStateException(getObjectReferenceCount() + " Object(s) still exist in runtime");
+ }
}
}
@@ -672,6 +759,28 @@ public class V8 extends V8Object {
return _getBuildID();
}
+ /**
+ * Indicates to V8 that the system is low on memory.
+ * V8 may use this to attempt to recover space by running
+ * the garbage collector.
+ */
+ public void lowMemoryNotification() {
+ checkThread();
+ lowMemoryNotification(getV8RuntimePtr());
+ }
+
+ void checkRuntime(final V8Value value) {
+ if ((value == null) || value.isUndefined()) {
+ return;
+ }
+ V8 runtime = value.getRuntime();
+ if ((runtime == null) ||
+ runtime.isReleased() ||
+ (runtime != this)) {
+ throw new Error("Invalid target runtime");
+ }
+ }
+
void checkThread() {
locker.checkThread();
if (isReleased()) {
@@ -722,7 +831,7 @@ public class V8 extends V8Object {
private Object getDefaultValue(final Class> type) {
if (type.equals(V8Object.class)) {
- return new Undefined();
+ return new V8Object.Undefined();
} else if (type.equals(V8Array.class)) {
return new V8Array.Undefined();
}
@@ -733,6 +842,20 @@ public class V8 extends V8Object {
functionRegistry.remove(methodID);
}
+ protected void weakReferenceReleased(final long objectID) {
+ V8Value v8Value = v8WeakReferences.get(objectID);
+ if (v8Value != null) {
+ v8WeakReferences.remove(objectID);
+ try {
+ v8Value.close();
+ } catch (Exception e) {
+ // Swallow these exceptions. The V8 GC is running, and
+ // if we return to V8 with Java exception on our stack,
+ // we will be in a world of hurt.
+ }
+ }
+ }
+
protected Object callObjectJavaMethod(final long methodID, final V8Object receiver, final V8Array parameters) throws Throwable {
MethodDescriptor methodDescriptor = functionRegistry.get(methodID);
if (methodDescriptor.callback != null) {
@@ -810,13 +933,13 @@ public class V8 extends V8Object {
Object[] varArgs = (Object[]) args[args.length - 1];
for (Object object : varArgs) {
if (object instanceof V8Value) {
- ((V8Value) object).release();
+ ((V8Value) object).close();
}
}
}
for (Object arg : args) {
if (arg instanceof V8Value) {
- ((V8Value) arg).release();
+ ((V8Value) arg).close();
}
}
}
@@ -916,6 +1039,22 @@ public class V8 extends V8Object {
return _initNewV8Object(v8RuntimePtr);
}
+ protected long initEmptyContainer(final long v8RuntimePtr) {
+ return _initEmptyContainer(v8RuntimePtr);
+ }
+
+ protected void acquireLock(final long v8RuntimePtr) {
+ _acquireLock(v8RuntimePtr);
+ }
+
+ protected void releaseLock(final long v8RuntimePtr) {
+ _releaseLock(v8RuntimePtr);
+ }
+
+ protected void lowMemoryNotification(final long v8RuntimePtr) {
+ _lowMemoryNotification(v8RuntimePtr);
+ }
+
protected void createTwin(final long v8RuntimePtr, final long objectHandle, final long twinHandle) {
_createTwin(v8RuntimePtr, objectHandle, twinHandle);
}
@@ -944,6 +1083,18 @@ public class V8 extends V8Object {
_executeVoidScript(v8RuntimePtr, script, scriptName, lineNumber);
}
+ protected void setWeak(final long v8RuntimePtr, final long objectHandle) {
+ _setWeak(v8RuntimePtr, objectHandle);
+ }
+
+ protected void clearWeak(final long v8RuntimePtr, final long objectHandle) {
+ _clearWeak(v8RuntimePtr, objectHandle);
+ }
+
+ protected boolean isWeak(final long v8RuntimePtr, final long objectHandle) {
+ return _isWeak(v8RuntimePtr, objectHandle);
+ }
+
protected void release(final long v8RuntimePtr, final long objectHandle) {
_release(v8RuntimePtr, objectHandle);
}
@@ -1170,6 +1321,10 @@ public class V8 extends V8Object {
_addArrayNullItem(v8RuntimePtr, arrayHandle);
}
+ protected int getType(final long v8RuntimePtr, final long objectHandle) {
+ return _getType(v8RuntimePtr, objectHandle);
+ }
+
protected int getType(final long v8RuntimePtr, final long objectHandle, final String key) {
return _getType(v8RuntimePtr, objectHandle, key);
}
@@ -1240,6 +1395,14 @@ public class V8 extends V8Object {
private native long _initNewV8Object(long v8RuntimePtr);
+ private native long _initEmptyContainer(long v8RuntimePtr);
+
+ private native void _acquireLock(long v8RuntimePtr);
+
+ private native void _releaseLock(long v8RuntimePtr);
+
+ private native void _lowMemoryNotification(long v8RuntimePtr);
+
private native void _createTwin(long v8RuntimePtr, long objectHandle, long twinHandle);
private native void _releaseRuntime(long v8RuntimePtr);
@@ -1356,6 +1519,8 @@ public class V8 extends V8Object {
private native void _setPrototype(long v8RuntimePtr, long objectHandle, long prototypeHandle);
+ private native int _getType(long v8RuntimePtr, long objectHandle);
+
private native int _getType(long v8RuntimePtr, long objectHandle, final int index, final int length);
private native double[] _arrayGetDoubles(final long v8RuntimePtr, final long objectHandle, final int index, final int length);
@@ -1400,6 +1565,12 @@ public class V8 extends V8Object {
private native long _initNewV8UInt8ClampedArray(long runtimePtr, long bufferHandle, int offset, int size);
+ private native void _setWeak(long runtimePtr, long objectHandle);
+
+ private native void _clearWeak(long runtimePtr, long objectHandle);
+
+ private native boolean _isWeak(long runtimePtr, long objectHandle);
+
private native ByteBuffer _createV8ArrayBufferBackingStore(final long v8RuntimePtr, final long objectHandle, final int capacity);
private native static String _getVersion();
@@ -1418,6 +1589,19 @@ public class V8 extends V8Object {
private native static boolean _isRunning(final long v8RuntimePtr);
+ private native static boolean _isNodeCompatible();
+
+ public static boolean isNodeCompatible() {
+ if (!nativeLibraryLoaded) {
+ synchronized (lock) {
+ if (!nativeLibraryLoaded) {
+ load(null);
+ }
+ }
+ }
+ return _isNodeCompatible();
+ }
+
void addObjRef(final V8Value reference) {
objectReferences++;
if (!referenceHandlers.isEmpty()) {
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8Array.java b/fine-j2v8/src/com/eclipsesource/v8/V8Array.java
index 548302cf5..87775179e 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8Array.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8Array.java
@@ -51,6 +51,18 @@ public class V8Array extends V8Object {
return (V8Array) super.twin();
}
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.V8Object#toString()
+ */
+ @Override
+ public String toString() {
+ if (released || v8.isReleased()) {
+ return "[Array released]";
+ }
+ return super.toString();
+ }
+
@Override
protected void initialize(final long runtimePtr, final Object data) {
long handle = v8.initNewV8Array(runtimePtr);
@@ -521,6 +533,7 @@ public class V8Array extends V8Object {
public V8Array push(final V8Value value) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(value);
if (value == null) {
v8.addArrayNullItem(v8.getV8RuntimePtr(), getHandle());
} else if (value.equals(V8.getUndefined())) {
@@ -531,6 +544,46 @@ public class V8Array extends V8Object {
return this;
}
+ /**
+ * Pushes a Object to the next available spot in the Array. In
+ * particular, this[length] = value;
+ *
+ * @param value The value to push to the array.
+ *
+ * @return The receiver.
+ */
+ public V8Array push(final Object value) {
+ v8.checkThread();
+ checkReleased();
+ if (value instanceof V8Value) {
+ v8.checkRuntime((V8Value) value);
+ }
+ if (value == null) {
+ v8.addArrayNullItem(v8.getV8RuntimePtr(), getHandle());
+ } else if (value.equals(V8.getUndefined())) {
+ v8.addArrayUndefinedItem(v8.getV8RuntimePtr(), getHandle());
+ } else {
+ if (value instanceof Double) {
+ v8.addArrayDoubleItem(v8.getV8RuntimePtr(), getHandle(), (Double) value);
+ } else if (value instanceof Integer) {
+ v8.addArrayIntItem(v8.getV8RuntimePtr(), getHandle(), (Integer) value);
+ } else if (value instanceof Float) {
+ v8.addArrayDoubleItem(v8.getV8RuntimePtr(), getHandle(), ((Float) value).doubleValue());
+ } else if (value instanceof Number) {
+ v8.addArrayDoubleItem(v8.getV8RuntimePtr(), getHandle(), ((Number) value).doubleValue());
+ } else if (value instanceof Boolean) {
+ v8.addArrayBooleanItem(v8.getV8RuntimePtr(), getHandle(), (Boolean) value);
+ } else if (value instanceof String) {
+ v8.addArrayStringItem(v8.getV8RuntimePtr(), getHandle(), (String) value);
+ } else if (value instanceof V8Value) {
+ v8.addArrayObjectItem(v8.getV8RuntimePtr(), getHandle(), ((V8Value) value).getHandle());
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+ return this;
+ }
+
/**
* Pushes null to the next available spot in the Array. In
* particular, this[length] = null;
@@ -580,11 +633,20 @@ public class V8Array extends V8Object {
return false;
}
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.V8Value#close()
+ */
+ @Override
+ public void close() {
+ }
+
/*
* (non-Javadoc)
* @see com.eclipsesource.v8.V8Value#release()
*/
@Override
+ @Deprecated
public void release() {
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8ArrayBuffer.java b/fine-j2v8/src/com/eclipsesource/v8/V8ArrayBuffer.java
index 46df2dc7b..4ecd48ebe 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8ArrayBuffer.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8ArrayBuffer.java
@@ -25,7 +25,7 @@ import java.nio.ByteOrder;
*/
public class V8ArrayBuffer extends V8Value {
- private ByteBuffer byteBuffer;
+ ByteBuffer byteBuffer;
/**
* Creates a new V8ArrayBuffer on a given V8Runtime with a
@@ -41,17 +41,11 @@ public class V8ArrayBuffer extends V8Value {
byteBuffer.order(ByteOrder.nativeOrder());
}
- /**
- * Creates a new V8ArrayBuffer with the provided ByteBuffer as the backing store.
- * The ByteBuffer must be allocated as a DirectBuffer. If the ByteBuffer is not
- * a DirectBuffer an IllegalArgumentException will be thrown.
- *
- * @param v8 The runtime on which to create the ArrayBuffer
- * @param byteBuffer The ByteBuffer to use as the backing store. The ByteBuffer must
- * be allocated as a DirectBuffer.
- */
- public V8ArrayBuffer(final V8 v8, final ByteBuffer byteBuffer) {
+ public V8ArrayBuffer(final V8 v8, ByteBuffer byteBuffer) {
super(v8);
+ if (byteBuffer == null) {
+ byteBuffer = ByteBuffer.allocateDirect(0);
+ }
if (!byteBuffer.isDirect()) {
throw new IllegalArgumentException("ByteBuffer must be a allocated as a direct ByteBuffer");
}
@@ -86,18 +80,393 @@ public class V8ArrayBuffer extends V8Value {
*/
@Override
public V8ArrayBuffer twin() {
+ v8.checkThread();
+ checkReleased();
return (V8ArrayBuffer) super.twin();
}
/**
- * Returns the backing store used for this ArrayBuffer.
+ * Returns the buffers limit
+ *
+ * @return the buffers limit
+ */
+ public int limit() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.limit();
+ }
+
+ /**
+ * Returns the buffers capacity
*
- * @return The backing store used for this ArrayBuffer.
+ * @return the buffers capacity
*/
- public ByteBuffer getBackingStore() {
- v8.checkReleased();
+ public final int capacity() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.capacity();
+ }
+
+ /**
+ *
+ * @return
+ */
+ public final int position() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.position();
+ }
+
+ public final V8ArrayBuffer position(final int newPosition) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.position(newPosition);
+ return this;
+ }
+
+ public final V8ArrayBuffer limit(final int newLimit) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.limit(newLimit);
+ return this;
+ }
+
+ public final V8ArrayBuffer mark() {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.mark();
+ return this;
+ }
+
+ public final V8ArrayBuffer reset() {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.reset();
+ return this;
+ }
+
+ public final V8ArrayBuffer clear() {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.clear();
+ return this;
+ }
+
+ public final V8ArrayBuffer flip() {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.flip();
+ return this;
+ }
+
+ public final V8ArrayBuffer rewind() {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.rewind();
+ return this;
+ }
+
+ public final int remaining() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.remaining();
+ }
+
+ public final boolean hasRemaining() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.hasRemaining();
+ }
+
+ public boolean isReadOnly() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.isReadOnly();
+ }
+
+ public byte get() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.get();
+ }
+
+ public V8ArrayBuffer put(final byte b) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.put(b);
+ return this;
+ }
+
+ public byte get(final int index) {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.get(index);
+ }
+
+ public V8ArrayBuffer put(final int index, final byte b) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.put(index, b);
+ return this;
+ }
+
+ public V8ArrayBuffer get(final byte[] dst, final int offset, final int length) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.get(dst, offset, length);
+ return this;
+ }
+
+ public V8ArrayBuffer get(final byte[] dst) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.get(dst);
+ return this;
+ }
+
+ public V8ArrayBuffer put(final ByteBuffer src) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.put(src);
+ return this;
+ }
+
+ public V8ArrayBuffer put(final byte[] src, final int offset, final int length) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.put(src, offset, length);
+ return this;
+ }
+
+ public final V8ArrayBuffer put(final byte[] src) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.put(src);
+ return this;
+ }
+
+ public final boolean hasArray() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.hasArray();
+ }
+
+ public final byte[] array() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.array();
+ }
+
+ public final int arrayOffset() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.arrayOffset();
+ }
+
+ public V8ArrayBuffer compact() {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.compact();
+ return this;
+ }
+
+ public boolean isDirect() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.isDirect();
+ }
+
+ public final ByteOrder order() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.order();
+ }
+
+ public final V8ArrayBuffer order(final ByteOrder bo) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.order(bo);
+ return this;
+ }
+
+ public char getChar() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getChar();
+ }
+
+ public V8ArrayBuffer putChar(final char value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putChar(value);
+ return this;
+ }
+
+ public char getChar(final int index) {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getChar(index);
+ }
+
+ public V8ArrayBuffer putChar(final int index, final char value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putChar(index, value);
+ return this;
+ }
+
+ public short getShort() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getShort();
+ }
+
+ public V8ArrayBuffer putShort(final short value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putShort(value);
+ return this;
+ }
+
+ public short getShort(final int index) {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getShort(index);
+ }
+
+ public V8ArrayBuffer putShort(final int index, final short value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putShort(index, value);
+ return this;
+ }
+
+ public int getInt() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getInt();
+ }
+
+ public V8ArrayBuffer putInt(final int value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putInt(value);
+ return this;
+ }
+
+ public int getInt(final int index) {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getInt(index);
+ }
+
+ public V8ArrayBuffer putInt(final int index, final int value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.asIntBuffer().put(index, value);
+ return this;
+ }
+
+ public long getLong() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getLong();
+ }
+
+ public V8ArrayBuffer putLong(final long value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putLong(value);
+ return this;
+ }
+
+ public long getLong(final int index) {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getLong(index);
+ }
+
+ public V8ArrayBuffer putLong(final int index, final long value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putLong(index, value);
+ return this;
+ }
+
+ public float getFloat() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getFloat();
+ }
+
+ public V8ArrayBuffer putFloat(final float value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putFloat(value);
+ return this;
+ }
+
+ public float getFloat(final int index) {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getFloat(index);
+ }
+
+ public V8ArrayBuffer putFloat(final int index, final float value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putFloat(index, value);
+ return this;
+ }
+
+ public double getDouble() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getDouble();
+ }
+
+ public V8ArrayBuffer putDouble(final double value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putDouble(value);
+ return this;
+ }
+
+ public double getDouble(final int index) {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.getDouble(index);
+ }
+
+ public V8ArrayBuffer putDouble(final int index, final double value) {
+ v8.checkThread();
+ checkReleased();
+ byteBuffer.putDouble(index, value);
+ return this;
+ }
+
+ public int floatLimit() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.asFloatBuffer().limit();
+ }
+
+ public int intLimit() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.asIntBuffer().limit();
+ }
+
+ public int shortLimit() {
+ v8.checkThread();
+ checkReleased();
+ return byteBuffer.asShortBuffer().limit();
+ }
+
+ public int doubleLimit() {
v8.checkThread();
- return byteBuffer;
+ checkReleased();
+ return byteBuffer.asDoubleBuffer().limit();
}
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8Function.java b/fine-j2v8/src/com/eclipsesource/v8/V8Function.java
index 0f3dc965f..c9fba3c60 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8Function.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8Function.java
@@ -38,6 +38,18 @@ public class V8Function extends V8Object {
return new V8Function(v8);
}
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.V8Object#toString()
+ */
+ @Override
+ public String toString() {
+ if (released || v8.isReleased()) {
+ return "[Function released]";
+ }
+ return super.toString();
+ }
+
@Override
protected void initialize(final long runtimePtr, final Object data) {
if (data == null) {
@@ -71,9 +83,12 @@ public class V8Function extends V8Object {
*
* @return The result of JavaScript function.
*/
+ @SuppressWarnings("resource")
public Object call(V8Object receiver, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(receiver);
+ v8.checkRuntime(parameters);
receiver = receiver != null ? receiver : v8;
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
long receiverHandle = receiver.isUndefined() ? v8.getHandle() : receiver.getHandle();
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8Locker.java b/fine-j2v8/src/com/eclipsesource/v8/V8Locker.java
index f0ab0749f..8084e64c4 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8Locker.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8Locker.java
@@ -18,32 +18,72 @@ package com.eclipsesource.v8;
*/
public class V8Locker {
- private Thread thread = null;
+ private Thread thread = null;
+ private boolean released = false;
+ private V8 runtime;
- V8Locker() {
+ V8Locker(final V8 runtime) {
+ this.runtime = runtime;
acquire();
}
+ /**
+ * Returns the current thread associated with locker.
+ *
+ * @return The currently locked thread.
+ */
+ public Thread getThread() {
+ return thread;
+ }
+
/**
* Acquire the lock if it's currently not acquired by another
- * thread. If it's current held by another thread, an
+ * thread. If it's currently held by another thread, an
* Error will be thrown.
*/
public synchronized void acquire() {
if ((thread != null) && (thread != Thread.currentThread())) {
- throw new Error("Invalid V8 thread access");
+ throw new Error("Invalid V8 thread access: current thread is " + Thread.currentThread() + " while the locker has thread " + thread);
+ } else if ((thread == Thread.currentThread())) {
+ return;
+ }
+ runtime.acquireLock(runtime.getV8RuntimePtr());
+ thread = Thread.currentThread();
+ released = false;
+ }
+
+ /**
+ * Acquire the lock if it's currently not acquired by another
+ * thread. If it's currently held by another thread, tryAcquire
+ * will return false, otherwise true is returned.
+ *
+ * @return Returns true if the lock was acquired, false otherwise.
+ */
+ public synchronized boolean tryAcquire() {
+ if ((thread != null) && (thread != Thread.currentThread())) {
+ return false;
+ } else if (thread == Thread.currentThread()) {
+ return true;
}
+ runtime.acquireLock(runtime.getV8RuntimePtr());
thread = Thread.currentThread();
+ released = false;
+ return true;
}
/**
* Release the lock if it's currently held by the calling thread.
* If the current thread does not hold the lock, and error will be
- * thrown.
+ * thrown. If no thread holds the lock then nothing will happen.
*/
public synchronized void release() {
+ if ((released && (thread == null)) || runtime.isReleased()) {
+ return;
+ }
checkThread();
+ runtime.releaseLock(runtime.getV8RuntimePtr());
thread = null;
+ released = true;
}
/**
@@ -52,8 +92,11 @@ public class V8Locker {
* is thrown.
*/
public void checkThread() {
+ if(released && (thread == null)){
+ throw new Error("Invalid V8 thread access: the locker has been released!");
+ }
if ((thread != Thread.currentThread())) {
- throw new Error("Invalid V8 thread access");
+ throw new Error("Invalid V8 thread access: current thread is " + Thread.currentThread() + " while the locker has thread " + thread);
}
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8Object.java b/fine-j2v8/src/com/eclipsesource/v8/V8Object.java
index b043be5c4..640e2a1f4 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8Object.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8Object.java
@@ -73,6 +73,7 @@ public class V8Object extends V8Value {
public boolean contains(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
return v8.contains(v8.getV8RuntimePtr(), objectHandle, key);
}
@@ -100,6 +101,7 @@ public class V8Object extends V8Value {
public int getType(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
return v8.getType(v8.getV8RuntimePtr(), objectHandle, key);
}
@@ -115,6 +117,7 @@ public class V8Object extends V8Value {
public Object get(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
return v8.get(v8.getV8RuntimePtr(), V8_OBJECT, objectHandle, key);
}
@@ -131,6 +134,7 @@ public class V8Object extends V8Value {
public int getInteger(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
return v8.getInteger(v8.getV8RuntimePtr(), objectHandle, key);
}
@@ -147,6 +151,7 @@ public class V8Object extends V8Value {
public boolean getBoolean(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
return v8.getBoolean(v8.getV8RuntimePtr(), objectHandle, key);
}
@@ -163,6 +168,7 @@ public class V8Object extends V8Value {
public double getDouble(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
return v8.getDouble(v8.getV8RuntimePtr(), objectHandle, key);
}
@@ -179,6 +185,7 @@ public class V8Object extends V8Value {
public String getString(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
return v8.getString(v8.getV8RuntimePtr(), objectHandle, key);
}
@@ -195,6 +202,7 @@ public class V8Object extends V8Value {
public V8Array getArray(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
Object result = v8.get(v8.getV8RuntimePtr(), V8_ARRAY, objectHandle, key);
if ((result == null) || (result instanceof V8Array)) {
return (V8Array) result;
@@ -215,6 +223,7 @@ public class V8Object extends V8Value {
public V8Object getObject(final String key) {
v8.checkThread();
checkReleased();
+ checkKey(key);
Object result = v8.get(v8.getV8RuntimePtr(), V8_OBJECT, objectHandle, key);
if ((result == null) || (result instanceof V8Object)) {
return (V8Object) result;
@@ -236,6 +245,7 @@ public class V8Object extends V8Value {
public int executeIntegerFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
return v8.executeIntegerFunction(v8.getV8RuntimePtr(), getHandle(), name, parametersHandle);
}
@@ -254,6 +264,7 @@ public class V8Object extends V8Value {
public double executeDoubleFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
return v8.executeDoubleFunction(v8.getV8RuntimePtr(), getHandle(), name, parametersHandle);
}
@@ -272,6 +283,7 @@ public class V8Object extends V8Value {
public String executeStringFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
return v8.executeStringFunction(v8.getV8RuntimePtr(), getHandle(), name, parametersHandle);
}
@@ -290,6 +302,7 @@ public class V8Object extends V8Value {
public boolean executeBooleanFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
return v8.executeBooleanFunction(v8.getV8RuntimePtr(), getHandle(), name, parametersHandle);
}
@@ -308,6 +321,7 @@ public class V8Object extends V8Value {
public V8Array executeArrayFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
Object result = v8.executeFunction(v8.getV8RuntimePtr(), V8_ARRAY, objectHandle, name, parametersHandle);
if (result instanceof V8Array) {
@@ -330,6 +344,7 @@ public class V8Object extends V8Value {
public V8Object executeObjectFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
Object result = v8.executeFunction(v8.getV8RuntimePtr(), V8_OBJECT, objectHandle, name, parametersHandle);
if (result instanceof V8Object) {
@@ -350,6 +365,7 @@ public class V8Object extends V8Value {
public Object executeFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
return v8.executeFunction(v8.getV8RuntimePtr(), UNKNOWN, objectHandle, name, parametersHandle);
}
@@ -383,15 +399,15 @@ public class V8Object extends V8Value {
} else if (object instanceof V8Value) {
parameterArray.push((V8Value) object);
} else if (object instanceof Integer) {
- parameterArray.push((Integer) object);
+ parameterArray.push(object);
} else if (object instanceof Double) {
- parameterArray.push((Double) object);
+ parameterArray.push(object);
} else if (object instanceof Long) {
parameterArray.push(((Long) object).doubleValue());
} else if (object instanceof Float) {
parameterArray.push(((Float) object).floatValue());
} else if (object instanceof Boolean) {
- parameterArray.push((Boolean) object);
+ parameterArray.push(object);
} else if (object instanceof String) {
parameterArray.push((String) object);
} else {
@@ -400,7 +416,7 @@ public class V8Object extends V8Value {
}
return executeFunction(name, parameterArray);
} finally {
- parameterArray.release();
+ parameterArray.close();
}
}
@@ -414,6 +430,7 @@ public class V8Object extends V8Value {
public void executeVoidFunction(final String name, final V8Array parameters) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(parameters);
long parametersHandle = parameters == null ? 0 : parameters.getHandle();
v8.executeVoidFunction(v8.getV8RuntimePtr(), objectHandle, name, parametersHandle);
}
@@ -495,6 +512,7 @@ public class V8Object extends V8Value {
public V8Object add(final String key, final V8Value value) {
v8.checkThread();
checkReleased();
+ v8.checkRuntime(value);
if (value == null) {
v8.addNull(v8.getV8RuntimePtr(), objectHandle, key);
} else if (value.equals(V8.getUndefined())) {
@@ -629,11 +647,19 @@ public class V8Object extends V8Value {
*/
@Override
public String toString() {
+ if (isReleased() || v8.isReleased()) {
+ return "[Object released]";
+ }
v8.checkThread();
- checkReleased();
return v8.toString(v8.getV8RuntimePtr(), getHandle());
}
+ private void checkKey(final String key) {
+ if (key == null) {
+ throw new IllegalArgumentException("Key cannot be null");
+ }
+ }
+
static class Undefined extends V8Object {
public Undefined() {
@@ -657,10 +683,19 @@ public class V8Object extends V8Value {
return false;
}
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.V8Value#close()
+ */
+ @Override
+ public void close() {
+ }
+
/*
* (non-Javadoc)
* @see com.eclipsesource.v8.V8Value#release()
*/
+ @Deprecated
@Override
public void release() {
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8TypedArray.java b/fine-j2v8/src/com/eclipsesource/v8/V8TypedArray.java
index 342439e10..423f9a71a 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8TypedArray.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8TypedArray.java
@@ -10,8 +10,6 @@
******************************************************************************/
package com.eclipsesource.v8;
-import java.nio.ByteBuffer;
-
/**
* A representation of a JS TypedArray in Java. The typed array is simply a 'view' onto
* a back buffer.
@@ -37,6 +35,41 @@ public class V8TypedArray extends V8Array {
super(v8);
}
+ @Override
+ protected V8Value createTwin() {
+ v8.checkThread();
+ checkReleased();
+ return new V8TypedArray(v8);
+ }
+
+ @Override
+ public Object get(final int index) {
+ v8.checkThread();
+ checkReleased();
+ int type = getType();
+ switch (type) {
+ case FLOAT_32_ARRAY:
+ return ((Number) super.get(index)).floatValue();
+ case FLOAT_64_ARRAY:
+ return super.get(index);
+ case INT_32_ARRAY:
+ return super.get(index);
+ case INT_16_ARRAY:
+ return ((Number) super.get(index)).shortValue();
+ case INT_8_ARRAY:
+ return ((Number) super.get(index)).byteValue();
+ case UNSIGNED_INT_16_ARRAY:
+ return 0xFFFF & (Integer) super.get(index);
+ case UNSIGNED_INT_32_ARRAY:
+ return 0x00000000FFFFFFFF & ((Number) super.get(index)).longValue();
+ case UNSIGNED_INT_8_CLAMPED_ARRAY:
+ return (short) (0x00FF & ((Number) super.get(index)).byteValue());
+ case UNSIGNED_INT_8_ARRAY:
+ return (short) (0x00FF & ((Number) super.get(index)).shortValue());
+ }
+ return null;
+ }
+
/**
* Provide access to the underlying ByteBuffer used for this TypedArray.
* The V8ArrayBuffer must be released.
@@ -47,20 +80,6 @@ public class V8TypedArray extends V8Array {
return (V8ArrayBuffer) get("buffer");
}
- /**
- * Returns the underlying ByteBuffer used to back this TypedArray.
- *
- * @return The ByteBuffer used as the backing store for this TypedArray
- */
- public ByteBuffer getByteBuffer() {
- V8ArrayBuffer buffer = getBuffer();
- try {
- return buffer.getBackingStore();
- } finally {
- buffer.release();
- }
- }
-
@Override
protected void initialize(final long runtimePtr, final Object data) {
v8.checkThread();
@@ -96,7 +115,7 @@ public class V8TypedArray extends V8Array {
case V8Value.UNSIGNED_INT_8_CLAMPED_ARRAY:
return v8.initNewV8UInt8ClampedArray(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size);
default:
- throw new IllegalArgumentException("Cannot create a typed array of type " + V8Value.getStringRepresentaion(arrayData.type));
+ throw new IllegalArgumentException("Cannot create a typed array of type " + V8Value.getStringRepresentation(arrayData.type));
}
}
@@ -122,7 +141,7 @@ public class V8TypedArray extends V8Array {
case V8Value.UNSIGNED_INT_8_CLAMPED_ARRAY:
return 1;
default:
- throw new IllegalArgumentException("Cannot create a typed array of type " + V8Value.getStringRepresentaion(type));
+ throw new IllegalArgumentException("Cannot create a typed array of type " + V8Value.getStringRepresentation(type));
}
}
@@ -136,7 +155,7 @@ public class V8TypedArray extends V8Array {
throw new IllegalStateException("RangeError: Invalid typed array length");
}
int limit = (arrayData.size * getStructureSize(arrayData.type)) + arrayData.offset;
- if (limit > arrayData.buffer.getBackingStore().limit()) {
+ if (limit > arrayData.buffer.limit()) {
throw new IllegalStateException("RangeError: Invalid typed array length");
}
}
@@ -147,11 +166,6 @@ public class V8TypedArray extends V8Array {
}
}
- @Override
- protected V8Value createTwin() {
- return new V8TypedArray(v8);
- }
-
private static class V8ArrayData {
private V8ArrayBuffer buffer;
private int offset;
diff --git a/fine-j2v8/src/com/eclipsesource/v8/V8Value.java b/fine-j2v8/src/com/eclipsesource/v8/V8Value.java
index 3952716d2..d22042580 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/V8Value.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/V8Value.java
@@ -12,16 +12,16 @@ package com.eclipsesource.v8;
/**
* A base class for all V8 resources. V8 resources must
- * be released. The rules for releasing resources is as
+ * be closed/released. The rules for releasing resources is as
* follows:
*
- * 1. If you created it, you must release it, with one exception;
+ * 1. If you created it, you must close it, with one exception;
* if the object is being passed pack via a return statement,
* the system will release it for you.
*
* 2. If the system created it, you don’t need to worry about it,
* with one caveat; if the object was returned to you as a
- * result of a method call, you must release it.
+ * result of a method call, you must close it.
*/
abstract public class V8Value implements Releasable {
@@ -83,12 +83,24 @@ abstract public class V8Value implements Releasable {
}
}
+
/**
* Returns a string representation of the V8 Type.
* @param type Type to return as a string. See constants in V8Value.
* @return The V8Value type as a string.
+ * @deprecated Use
*/
+ @Deprecated
public static String getStringRepresentaion(final int type) {
+ return getStringRepresentation(type);
+ }
+
+ /**
+ * Returns a string representation of the V8 Type.
+ * @param type Type to return as a string. See constants in V8Value.
+ * @return The V8Value type as a string.
+ */
+ public static String getStringRepresentation(final int type) {
switch (type) {
case NULL:
return "Null";
@@ -149,6 +161,22 @@ abstract public class V8Value implements Releasable {
return v8;
}
+ /**
+ * Returns the 'type' of this V8Value. The available types are defined
+ * as constants in {@link V8Value}. Only types that inherit from
+ * {@link V8Value} can be returned here.
+ *
+ * @return The 'type of this V8Value.
+ */
+ public int getV8Type() {
+ if (isUndefined()) {
+ return UNDEFINED;
+ }
+ v8.checkThread();
+ v8.checkReleased();
+ return v8.getType(v8.getV8RuntimePtr(), objectHandle);
+ }
+
/**
* Creates a new Java object pointing at the same V8 Value
* as this. If the value is mutated (by adding new members or
@@ -156,7 +184,7 @@ abstract public class V8Value implements Releasable {
* will be updated. Twins are .equal and .strict equals, but
* not == in Java.
*
- * Twins must be released separately since they have their own
+ * Twins must be closed separately since they have their own
* native resources.
*
* @return A new Java object pointing at the same V8 Value
@@ -174,10 +202,62 @@ abstract public class V8Value implements Releasable {
}
/**
- * Releases the native resources associated with this V8Value.
+ * Sets the V8Value as weak reference. A weak reference will eventually
+ * be closed when no more references exist to this object. Once setWeak
+ * is called, you should check if {@link V8Value#isReleased()} is true
+ * before invoking any methods on this object.
+ *
+ * If any other references exist to this object, the object will not be
+ * reclaimed. Even if no reference exist, V8 does not give any guarantee
+ * the object will be closed, so this should only be used if there is no
+ * other way to track object usage.
+ *
+ * @return The receiver.
+ */
+ public V8Value setWeak() {
+ v8.checkThread();
+ v8.checkReleased();
+ v8.v8WeakReferences.put(getHandle(), this);
+ v8.setWeak(v8.getV8RuntimePtr(), getHandle());
+ return this;
+ }
+
+ /**
+ * Clears any weak reference set on this V8Value and makes this a strong
+ * reference. Strong references will not be garbage collected and this
+ * Object must be explicitly released.
+ *
+ * Calling clearWeak does nothing if the object is not currently set
+ * to weak.
+ *
+ * @return The receiver.
+ */
+ public V8Value clearWeak() {
+ v8.checkThread();
+ v8.checkReleased();
+ v8.v8WeakReferences.remove(getHandle());
+ v8.clearWeak(v8.getV8RuntimePtr(), getHandle());
+ return this;
+ }
+
+ /**
+ * If {@link V8Value#setWeak()} has been called on this Object, this method
+ * will return true. Otherwise it will return false.
+ *
+ * @return Returns true if this object has been set 'Weak', return false otherwise.
+ */
+ public boolean isWeak() {
+ v8.checkThread();
+ v8.checkReleased();
+ return v8.isWeak(v8.getV8RuntimePtr(), getHandle());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.io.Closeable#close()
*/
@Override
- public void release() {
+ public void close() {
v8.checkThread();
if (!released) {
try {
@@ -189,6 +269,17 @@ abstract public class V8Value implements Releasable {
}
}
+ /**
+ * Releases the native resources associated with this V8Value.
+ *
+ * @deprecated use close() instead.
+ */
+ @Override
+ @Deprecated
+ public void release() {
+ close();
+ }
+
/**
* Determine if the native resources have been released. Once released
* a V8 Value can no longer be used.
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/DebugHandler.java b/fine-j2v8/src/com/eclipsesource/v8/debug/DebugHandler.java
index 6346d40df..ffb680d00 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/DebugHandler.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/DebugHandler.java
@@ -105,7 +105,7 @@ public class DebugHandler implements Releasable {
try {
return debugObject.executeIntegerFunction(SET_BREAK_POINT, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -124,7 +124,7 @@ public class DebugHandler implements Releasable {
try {
return debugObject.executeIntegerFunction(SET_SCRIPT_BREAK_POINT_BY_NAME, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -139,7 +139,7 @@ public class DebugHandler implements Releasable {
try {
debugObject.executeVoidFunction(ENABLE_SCRIPT_BREAK_POINT, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -154,7 +154,7 @@ public class DebugHandler implements Releasable {
try {
debugObject.executeVoidFunction(DISABLE_SCRIPT_BREAK_POINT, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -169,7 +169,7 @@ public class DebugHandler implements Releasable {
try {
debugObject.executeVoidFunction(CLEAR_BREAK_POINT, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -190,7 +190,7 @@ public class DebugHandler implements Releasable {
try {
return breakPoints.length();
} finally {
- breakPoints.release();
+ breakPoints.close();
}
}
@@ -208,12 +208,12 @@ public class DebugHandler implements Releasable {
try {
result[i] = breakPoint.executeIntegerFunction(NUMBER, null);
} finally {
- breakPoint.release();
+ breakPoint.close();
}
}
return result;
} finally {
- breakPoints.release();
+ breakPoints.close();
}
}
@@ -232,9 +232,9 @@ public class DebugHandler implements Releasable {
scriptBreakPoint = debugObject.executeObjectFunction(FIND_SCRIPT_BREAK_POINT, parameters);
return new ScriptBreakPoint(scriptBreakPoint);
} finally {
- parameters.release();
+ parameters.close();
if (scriptBreakPoint != null) {
- scriptBreakPoint.release();
+ scriptBreakPoint.close();
}
}
}
@@ -252,13 +252,23 @@ public class DebugHandler implements Releasable {
try {
debugObject.executeVoidFunction(CHANGE_BREAK_POINT_CONDITION, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
+ /*
+ * (non-Javadoc)
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ debugObject.close();
+ }
+
@Override
+ @Deprecated
public void release() {
- debugObject.release();
+ close();
}
private void setupDebugObject(final V8 runtime) {
@@ -266,7 +276,7 @@ public class DebugHandler implements Releasable {
try {
debugObject = outerDebug.getObject(V8_DEBUG_OBJECT);
} finally {
- outerDebug.release();
+ outerDebug.close();
}
}
@@ -277,14 +287,15 @@ public class DebugHandler implements Releasable {
V8Array parameters = null;
try {
debugHandler = (V8Function) debugObject.getObject(DEBUG_BREAK_HANDLER);
- parameters = new V8Array(runtime).push(debugHandler);
+ parameters = new V8Array(runtime);
+ parameters.push(debugHandler);
debugObject.executeFunction(SET_LISTENER, parameters);
} finally {
if ((debugHandler != null) && !debugHandler.isReleased()) {
- debugHandler.release();
+ debugHandler.close();
}
if ((parameters != null) && !parameters.isReleased()) {
- parameters.release();
+ parameters.close();
}
}
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/EventData.java b/fine-j2v8/src/com/eclipsesource/v8/debug/EventData.java
index e31342ebc..8b58a9cda 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/EventData.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/EventData.java
@@ -25,10 +25,16 @@ public class EventData implements Releasable {
}
@Override
- public void release() {
+ public void close() {
if (!v8Object.isReleased()) {
- v8Object.release();
+ v8Object.close();
}
}
+ @Override
+ @Deprecated
+ public void release() {
+ close();
+ }
+
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/ExecutionState.java b/fine-j2v8/src/com/eclipsesource/v8/debug/ExecutionState.java
index 77dad6fa1..43adb1b09 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/ExecutionState.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/ExecutionState.java
@@ -58,7 +58,7 @@ public class ExecutionState implements Releasable {
try {
v8Object.executeVoidFunction(PREPARE_STEP, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -76,19 +76,25 @@ public class ExecutionState implements Releasable {
frame = v8Object.executeObjectFunction(FRAME, parameters);
return new Frame(frame);
} finally {
- parameters.release();
+ parameters.close();
if (frame != null) {
- frame.release();
+ frame.close();
}
}
}
@Override
- public void release() {
+ public void close() {
if ((v8Object != null) && !v8Object.isReleased()) {
- v8Object.release();
+ v8Object.close();
v8Object = null;
}
}
+ @Override
+ @Deprecated
+ public void release() {
+ close();
+ }
+
}
\ No newline at end of file
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/ScriptBreakPoint.java b/fine-j2v8/src/com/eclipsesource/v8/debug/ScriptBreakPoint.java
index 850c14bdb..d91ccc97f 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/ScriptBreakPoint.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/ScriptBreakPoint.java
@@ -61,7 +61,7 @@ public class ScriptBreakPoint implements Releasable {
try {
v8Object.executeVoidFunction(SET_CONDITION, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -80,10 +80,15 @@ public class ScriptBreakPoint implements Releasable {
}
@Override
- public void release() {
+ public void close() {
if ((v8Object != null) && !v8Object.isReleased()) {
- v8Object.release();
+ v8Object.close();
v8Object = null;
}
}
+
+ @Override
+ public void release() {
+ close();
+ }
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/V8DebugServer.java b/fine-j2v8/src/com/eclipsesource/v8/debug/V8DebugServer.java
new file mode 100644
index 000000000..f0e957dc9
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/V8DebugServer.java
@@ -0,0 +1,693 @@
+/**
+ * Copyright 2016, Genuitec, LLC
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Genuitec LLC - initial API and implementation
+ ******************************************************************************/
+package com.eclipsesource.v8.debug;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.Charset;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.eclipsesource.v8.JavaVoidCallback;
+import com.eclipsesource.v8.Releasable;
+import com.eclipsesource.v8.V8;
+import com.eclipsesource.v8.V8Array;
+import com.eclipsesource.v8.V8Function;
+import com.eclipsesource.v8.V8Object;
+
+/**
+ *
V8DebugServer enables debuggers to connect to J2V8 via V8 server sockets debug protocol.
+ * Server has to be created in the same thread as the provided V8 runtime has been created (the V8 thread).
+ * You can specify port and whether the {@link #start()} method should
+ * block until a client connects. {@link #setTraceCommunication(boolean)} allows to output
+ * communication details for debugging purposes. Before creating V8 runtime you need to set V8 flag to expose
+ * debug object. If you do not intend to set other flags, than you can use {@link #configureV8ForDebugging()}
+ * method, otherwise set {@code -expose-debug-as=__j2v8_Debug} flag through {@link V8#setFlags(String)}.
+ *
+ *
Client connection is handled in a separate thread, however, commands are processed in the V8 thread.
+ * Therefore it is vital to provide an opportunity to process requests by calling
+ * {@link #processRequests(long)} method from the V8 thread. This will for instance
+ * allow to install breakpoints before the JavaScript code starts to execute. It is also good to call that
+ * method when V8 thread is idle to promptly provide responses to the debugger to avoid timeouts.
+ *
+ *
Example code:
+ *
+ *
+ * //configure for debugging before creating runtime
+ * V8DebugServer.configureV8ForDebugging();
+ *
+ * //create V8 runtime
+ * V8 runtime = V8.createV8Runtime();
+ *
+ * //create and start debug server
+ * int port = 0;
+ * boolean waitForConnection = true;
+ * server = new V8DebugServer(runtime, port, waitForConnection);
+ * System.out.println("V8 Debug Server listening on port "
+ * + server.getPort());
+ * server.start();
+ *
+ * //execute script and provide name for it
+ * runtime.executeVoidScript("var i = 15", "myscript.js", 0);
+ *
+ *
+ *
+ * @author piotr@genuitec.com
+ */
+@SuppressWarnings("nls")
+public class V8DebugServer {
+
+ /**
+ * Name under which internal V8 debug object is going to be exposed in the runtime.
+ * You can change the name if you are passing a different one through {@code -expose-debug-as}
+ * flag.
+ */
+ public static String DEBUG_OBJECT_NAME = "__j2v8_Debug";
+
+ private static final String DEBUG_BREAK_HANDLER = "__j2v8_debug_handler";
+ private static final String MAKE_BREAK_EVENT = "__j2v8_MakeBreakEvent";
+ private static final String MAKE_COMPILE_EVENT = "__j2v8_MakeCompileEvent";
+ private static final String SET_LISTENER = "setListener";
+ private static final String V8_DEBUG_OBJECT = "Debug";
+
+ //Headers
+ private static final String HEADER_TYPE = "Type: ";
+ private static final String HEADER_V8_VERSION = "V8-Version: ";
+ private static final String HEADER_PROTOCOL_VERSION = "Protocol-Version: ";
+ private static final String HEADER_EMBEDDING_HOST = "Embedding-Host: ";
+
+ private static final String V8_VERSION = "4.10.253";
+ private static final String J2V8_VERSION = "4.0.0";
+ private static final String PROTOCOL_VERSION = "1";
+
+ //Protocol consts
+ private static final Charset PROTOCOL_CHARSET = Charset.forName("UTF-8");
+ private static final String PROTOCOL_EOL = "\r\n";
+ private static final byte[] PROTOCOL_EOL_BYTES = PROTOCOL_EOL.getBytes(PROTOCOL_CHARSET);
+ private static final String PROTOCOL_CONTENT_LENGTH_HEADER = "Content-Length:";
+ private static final byte[] PROTOCOL_CONTENT_LENGTH_BYTES = PROTOCOL_CONTENT_LENGTH_HEADER.getBytes(PROTOCOL_CHARSET);
+ private static final int PROTOCOL_BUFFER_SIZE = 4096;
+
+ /**
+ * Utility method for simplification of configuring V8 for debugging support.
+ */
+ public static void configureV8ForDebugging() {
+ try {
+ V8.setFlags("-expose-debug-as=" + DEBUG_OBJECT_NAME);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ private ServerSocket server;
+ private Socket client;
+ private Object clientLock = new Object();
+
+ private V8 runtime;
+ private V8Object debugObject;
+ private V8Object runningStateDcp;
+ private V8Object stoppedStateDcp;
+ private boolean waitForConnection;
+ private boolean traceCommunication = false;
+
+ private List requests = new LinkedList();
+
+ /**
+ * Creates V8DebugServer.
+ *
+ * @param runtime
+ * @param port
+ * @param waitForConnection
+ */
+ public V8DebugServer(final V8 runtime, final int port, final boolean waitForConnection) {
+ this.runtime = runtime;
+ this.waitForConnection = waitForConnection;
+
+ V8Object debugScope = runtime.getObject(DEBUG_OBJECT_NAME);
+ if (debugScope == null) {
+ System.err.println("Cannot initialize debugger server - global debug object not found.");
+ return;
+ }
+ try {
+ debugObject = debugScope.getObject(V8_DEBUG_OBJECT);
+ } finally {
+ debugScope.close();
+ }
+
+ runtime.executeVoidScript("(function() {\n"
+ + " " + DEBUG_OBJECT_NAME + ".Debug. " + MAKE_BREAK_EVENT + " = function (break_id,breakpoints_hit) {\n"
+ + " return new " + DEBUG_OBJECT_NAME + ".BreakEvent(break_id,breakpoints_hit);\n"
+ + " }\n"
+ + " " + DEBUG_OBJECT_NAME + ".Debug. " + MAKE_COMPILE_EVENT + " = function(script,type) {\n"
+ + " var scripts = " + DEBUG_OBJECT_NAME + ".Debug.scripts()\n"
+ + " for (var i in scripts) {\n"
+ + " if (scripts[i].id == script.id()) {\n"
+ + " return new " + DEBUG_OBJECT_NAME + ".CompileEvent(scripts[i], type);\n"
+ + " }\n"
+ + " }\n"
+ + " return {toJSONProtocol: function() {return ''}}\n"
+ + " }\n"
+ + "})()");
+ try {
+ server = new ServerSocket(port);
+ } catch (Exception e) {
+ logError(e);
+ }
+ }
+
+ /**
+ * Returns port on which server is listening or -1 if server failed to bound to a port.
+ * @return port or -1 if server failed to bound to a port
+ */
+ public int getPort() {
+ return (server != null) && server.isBound() ? server.getLocalPort() : -1;
+ }
+
+ /**
+ * Output all communication to the console. For purpose of debugging V8DebugServer itself.
+ * @param value
+ */
+ public void setTraceCommunication(final boolean value) {
+ traceCommunication = value;
+ }
+
+ /**
+ * Starts accepting client connections and blocks until a client connects
+ * if {@code waitForConnection} has been passed to V8DebugServer constructor.
+ */
+ public void start() {
+ if (server == null) {
+ return;
+ }
+ boolean waitForConnection = this.waitForConnection;
+ Thread clientThread = new Thread(new ClientLoop(), "J2V8 Debugger Server");
+ clientThread.setDaemon(true);
+ clientThread.start();
+
+ setupEventHandler();
+
+ runningStateDcp = runtime.executeObjectScript("(function() {return new " + DEBUG_OBJECT_NAME + ".DebugCommandProcessor(null, true)})()");
+
+ if (waitForConnection) {
+ synchronized (clientLock) {
+ while (this.waitForConnection) {
+ try {
+ clientLock.wait();
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ }
+ }
+
+ //Process initial requests
+ //Give 100ms for initial debugger connection setup
+ try {
+ processRequests(100);
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ }
+
+ }
+
+ public void stop() {
+ try {
+ server.close();
+
+ synchronized (clientLock) {
+ if (client != null) {
+ client.close();
+ client = null;
+ }
+ }
+ } catch (IOException e) {
+ logError(e);
+ }
+
+ //release resources
+ if (runningStateDcp != null) {
+ runningStateDcp.close();
+ runningStateDcp = null;
+ }
+ if (debugObject != null) {
+ debugObject.close();
+ debugObject = null;
+ }
+ if (stoppedStateDcp != null) {
+ stoppedStateDcp.close();
+ stoppedStateDcp = null;
+ }
+ };
+
+ private void sendJson(String json) throws IOException {
+ json = json.replace("\\/", "/"); // Unescape slashes.
+ sendMessage("", json);
+ }
+
+ protected void logError(final Throwable t) {
+ t.printStackTrace();
+ }
+
+ private void sendMessage(final String header, final String contents) throws IOException {
+ synchronized (clientLock) {
+ if (!isConnected()) {
+ throw new IOException("There is no connected client.");
+ }
+
+ byte[] contentBytes = contents.getBytes(PROTOCOL_CHARSET);
+
+ StringBuilder sb = new StringBuilder();
+
+ //append custom headers
+ sb.append(header);
+
+ //append content length header
+ sb.append(PROTOCOL_CONTENT_LENGTH_HEADER);
+ sb.append(Integer.toString(contentBytes.length));
+ sb.append(PROTOCOL_EOL);
+
+ //skip tools info
+ sb.append(PROTOCOL_EOL);
+
+ //send headers to the client
+ client.getOutputStream().write(sb.toString().getBytes(
+ PROTOCOL_CHARSET));
+
+ //send contents to the client
+ if (contentBytes.length > 0) {
+ client.getOutputStream().write(contentBytes);
+ }
+ }
+ }
+
+ private boolean isConnected() {
+ synchronized (clientLock) {
+ return (server != null) && (client != null) && client.isConnected();
+ }
+ }
+
+ public void processRequests(final long timeout) throws InterruptedException {
+ if (server == null) {
+ return;
+ }
+ long start = System.currentTimeMillis();
+ do {
+ String[] reqs;
+ do {
+ synchronized (requests) {
+ reqs = requests.toArray(new String[requests.size()]);
+ requests.clear();
+ }
+ for (String req : reqs) {
+ try {
+ processRequest(req);
+ } catch (Exception e) {
+ logError(e);
+ }
+ }
+ } while (reqs.length > 0);
+ if (timeout > 0) {
+ Thread.sleep(10);
+ }
+ } while ((timeout > 0) && ((start + timeout) > System.currentTimeMillis()));
+ }
+
+ private void processRequest(final String message) throws IOException {
+ if (traceCommunication) {
+ System.out.println("Got message: \n" + message.substring(0, Math.min(message.length(), 1000)));
+ }
+ V8Array params = new V8Array(runtime);
+ params.push(message);
+
+ @SuppressWarnings("resource")
+ V8Object dcp = stoppedStateDcp != null ? stoppedStateDcp : runningStateDcp;
+ Object result = dcp.executeFunction("processDebugJSONRequest", params);
+
+ String json = result.toString();
+
+ if ((stoppedStateDcp == null) && json.contains("\"running\":false")) {
+ //XXX Need to implement functionality by adding to V8 class
+ // breakpoints before initial script or function execution
+ json = json.replace("\"running\":false", "\"running\":true")
+ .replace("\"success\":true", "\"success\":false")
+ .replace("{\"", "{\"message\":\"Client requested suspension is not supported on J2V8.\",\"");
+ dcp.add("running_", true);
+ }
+
+ if (traceCommunication) {
+ System.out.println("Returning response: \n" + json.substring(0, Math.min(json.length(), 1000)));
+ }
+ sendJson(json);
+ }
+
+ private void setupEventHandler() {
+ EventHandler handler = new EventHandler();
+ debugObject.registerJavaMethod(handler, DEBUG_BREAK_HANDLER);
+ V8Function debugHandler = null;
+ V8Array parameters = null;
+ try {
+ debugHandler = (V8Function) debugObject.getObject(DEBUG_BREAK_HANDLER);
+ parameters = new V8Array(runtime);
+ parameters.push(debugHandler);
+ debugObject.executeFunction(SET_LISTENER, parameters);
+ } finally {
+ if ((debugHandler != null) && !debugHandler.isReleased()) {
+ debugHandler.close();
+ }
+ if ((parameters != null) && !parameters.isReleased()) {
+ parameters.close();
+ }
+ }
+ }
+
+ private void enterBreakLoop(final V8Object execState, final V8Object eventData) throws IOException {
+ try {
+ V8Array params = new V8Array(runtime);
+ try {
+ params.push(false);
+ stoppedStateDcp = execState.executeObjectFunction("debugCommandProcessor", params);
+ } finally {
+ params.close();
+ }
+
+ //send event to debugger
+ int breakId = execState.getInteger("break_id");
+ V8Array breakpointsHit = eventData.getArray("break_points_hit_");
+ V8Object event = null;
+
+ params = new V8Array(runtime);
+ try {
+ params.push(breakId);
+ params.push(breakpointsHit);
+ event = debugObject.executeObjectFunction(MAKE_BREAK_EVENT, params);
+ String json = event.executeStringFunction("toJSONProtocol", null);
+ if (traceCommunication) {
+ System.out.println("Sending event (Break):\n" + json);
+ }
+ sendJson(json);
+ } finally {
+ params.close();
+ breakpointsHit.close();
+ if (event != null) {
+ event.close();
+ }
+ }
+
+ //process requests until one of the resumes execution
+ while (isConnected() && !stoppedStateDcp.executeBooleanFunction("isRunning", null)) {
+ try {
+ processRequests(10);
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ }
+ } finally {
+ stoppedStateDcp.close();
+ stoppedStateDcp = null;
+ }
+ }
+
+ private void sendCompileEvent(final V8Object eventData) throws IOException {
+ if (!isConnected()) {
+ return;
+ }
+ //send event to debugger
+ int type = eventData.getInteger("type_");
+ V8Object script = eventData.getObject("script_");
+ V8Object event = null;
+
+ V8Array params = new V8Array(runtime);
+ try {
+ params.push(script);
+ params.push(type);
+ event = debugObject.executeObjectFunction(MAKE_COMPILE_EVENT, params);
+ String json = event.executeStringFunction("toJSONProtocol", null);
+ if (traceCommunication) {
+ System.out.println("Sending event (CompileEvent):\n" + json.substring(0, Math.min(json.length(), 1000)));
+ }
+ if (json.length() > 0) {
+ sendJson(json);
+ }
+ } finally {
+ params.close();
+ script.close();
+ if (event != null) {
+ event.close();
+ }
+ }
+ }
+
+ private class EventHandler implements JavaVoidCallback {
+
+ @Override
+ public void invoke(final V8Object receiver, final V8Array parameters) {
+ if ((parameters == null) || parameters.isUndefined()) {
+ return;
+ }
+ V8Object execState = null;
+ V8Object eventData = null;
+ try {
+ int event = parameters.getInteger(0);
+ execState = parameters.getObject(1);
+ eventData = parameters.getObject(2);
+
+ if (traceCommunication) {
+ String type = "unknown";
+ switch (event) {
+ case 1:
+ type = "Break";
+ break;
+ case 2:
+ type = "Exception";
+ break;
+ case 3:
+ type = "NewFunction";
+ break;
+ case 4:
+ type = "BeforeCompile";
+ break;
+ case 5:
+ type = "AfterCompile";
+ break;
+ case 6:
+ type = "CompileError";
+ break;
+ case 7:
+ type = "PromiseEvent";
+ break;
+ case 8:
+ type = "AsyncTaskEvent";
+ break;
+ }
+ System.out.println("V8 has emmitted an event of type " + type);
+ }
+
+ if (!isConnected()) {
+ return;
+ }
+
+ switch (event) {
+ case 1: //Break
+ enterBreakLoop(execState, eventData);
+ break;
+ case 5: //afterCompile
+ case 6: //compileError
+ sendCompileEvent(eventData);
+ break;
+ case 2: //exception
+ default:
+ }
+ } catch (Exception e) {
+ logError(e);
+ } finally {
+ safeRelease(execState);
+ safeRelease(eventData);
+ }
+ }
+
+ private void safeRelease(final Releasable object) {
+ if ((object != null)) {
+ object.release();
+ }
+ }
+
+ }
+
+ private class ClientLoop implements Runnable {
+
+ private int from;
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ Socket socket = server.accept();
+ socket.setTcpNoDelay(true);
+ synchronized (clientLock) {
+ client = socket;
+ waitForConnection = false;
+ clientLock.notifyAll();
+ }
+ startHandshake();
+ processClientRequests();
+ } catch (Exception e) {
+ synchronized (clientLock) {
+ if (client != null) {
+ try {
+ client.close();
+ } catch (IOException ex) {
+ //ignore
+ }
+ client = null;
+ }
+ }
+ logError(e);
+ }
+ }
+ }
+
+ private void startHandshake() throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(HEADER_V8_VERSION);
+ sb.append(V8_VERSION);
+ sb.append(PROTOCOL_EOL);
+
+ sb.append(HEADER_PROTOCOL_VERSION);
+ sb.append(PROTOCOL_VERSION);
+ sb.append(PROTOCOL_EOL);
+
+ sb.append(HEADER_EMBEDDING_HOST);
+ sb.append("j2v8 ");
+ sb.append(J2V8_VERSION);
+ sb.append(PROTOCOL_EOL);
+
+ sb.append(HEADER_TYPE);
+ sb.append("connect");
+ sb.append(PROTOCOL_EOL);
+
+ sendMessage(sb.toString(), "");
+ }
+
+ private void processClientRequests() throws IOException {
+ final byte[] EMPTY_ARR = new byte[] {};
+
+ byte[] buf = new byte[PROTOCOL_BUFFER_SIZE];
+ int bytesRead;
+ int offset = 0;
+
+ //Message data
+ boolean toolInfoSkipped = false;
+ byte[] messageBytes = EMPTY_ARR;
+ int contentLength = -1;
+
+ InputStream cIn;
+ synchronized (clientLock) {
+ cIn = client.getInputStream();
+ }
+
+ while ((bytesRead = cIn.read(buf, offset, PROTOCOL_BUFFER_SIZE - offset)) > 0) {
+ bytesRead += offset;
+ from = 0;
+ do {
+ if (contentLength < 0) {
+ contentLength = readContentLength(buf, bytesRead);
+ if (contentLength < 0) {
+ break;
+ }
+ }
+ if (!toolInfoSkipped) {
+ toolInfoSkipped = skipToolInfo(buf, bytesRead);
+ if (!toolInfoSkipped) {
+ break;
+ }
+ }
+ int length = Math.min(contentLength - messageBytes.length, bytesRead - from);
+ messageBytes = join(messageBytes, buf, from, length);
+ from += length;
+ if (messageBytes.length == contentLength) {
+ String message = new String(messageBytes, PROTOCOL_CHARSET);
+ synchronized (requests) {
+ requests.add(message);
+ }
+ contentLength = -1;
+ toolInfoSkipped = false;
+ messageBytes = EMPTY_ARR;
+ }
+ } while (from < bytesRead);
+ if (from < bytesRead) {
+ System.arraycopy(buf, from, buf, 0, bytesRead - from);
+ offset = bytesRead - from;
+ } else {
+ offset = 0;
+ }
+ }
+ }
+
+ private int readContentLength(final byte[] bytes, final int to) throws IOException {
+ int pos = indexOf(PROTOCOL_CONTENT_LENGTH_BYTES, bytes, from, to);
+ if (pos < 0) {
+ return -1;
+ }
+ pos += PROTOCOL_CONTENT_LENGTH_BYTES.length;
+ int end = indexOf(PROTOCOL_EOL_BYTES, bytes, pos, to);
+ if (end < 0) {
+ return -1;
+ }
+ String str = new String(bytes, pos, end - pos, PROTOCOL_CHARSET);
+ int contentLength;
+ try {
+ contentLength = Integer.parseInt(str.trim());
+ } catch (Exception ex) {
+ throw new IOException("Invalid content length header: '" + str + "' in message" +
+ new String(bytes, PROTOCOL_CHARSET));
+ }
+ from = end + PROTOCOL_EOL_BYTES.length;
+ return contentLength;
+ }
+
+ private boolean skipToolInfo(final byte[] bytes, final int n) {
+ int end = indexOf(PROTOCOL_EOL_BYTES, bytes, from, n);
+ if (end < 0) {
+ return false;
+ }
+ from = end + PROTOCOL_EOL_BYTES.length;
+ return true;
+ }
+
+ private int indexOf(final byte[] pattern, final byte[] array, final int start, final int end) {
+ int len = pattern.length;
+ for (int i = start; i < end; i++) {
+ for (int j = 0; j <= len; j++) {
+ if (j == len) {
+ //pattern matches at i
+ return i;
+ }
+ if (((i + j) >= end) || (array[i + j] != pattern[j])) {
+ //pattern does not match at i
+ break;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private byte[] join(final byte[] arr1, final byte[] arr2, final int startPos, final int length) {
+ byte[] res = new byte[arr1.length + length];
+ System.arraycopy(arr1, 0, res, 0, arr1.length);
+ System.arraycopy(arr2, startPos, res, arr1.length, length);
+ return res;
+ }
+
+ };
+}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Frame.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Frame.java
index ad1f14371..f06c838d2 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Frame.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Frame.java
@@ -19,8 +19,8 @@ import com.eclipsesource.v8.V8Object;
*/
public class Frame extends Mirror {
- private static final String END = "end";
- private static final String START = "start";
+ private static final String SOURCE_TEXT = "sourceText";
+ private static final String FUNC = "func";
private static final String COLUMN = "column";
private static final String LINE = "line";
private static final String POSITION = "position";
@@ -56,18 +56,28 @@ public class Frame extends Mirror {
*/
public SourceLocation getSourceLocation() {
V8Object sourceLocation = v8Object.executeObjectFunction(SOURCE_LOCATION, null);
+ FunctionMirror function = getFunction();
+ String functionScriptName = function.getScriptName();
try {
+ String scriptName = null;
V8Object scriptObject = (V8Object) sourceLocation.get(SCRIPT);
- String scriptName = scriptObject.getString(NAME);
- scriptObject.release();
+ if (scriptObject != null) {
+ scriptName = scriptObject.getString(NAME);
+ scriptObject.close();
+ }
+ if ((scriptName == null) && (functionScriptName != null)) {
+ scriptName = functionScriptName;
+ } else {
+ scriptName = "undefined";
+ }
return new SourceLocation(scriptName,
- sourceLocation.getInteger(POSITION),
- sourceLocation.getInteger(LINE),
- sourceLocation.getInteger(COLUMN),
- sourceLocation.getInteger(START),
- sourceLocation.getInteger(END));
+ sourceLocation.getInteger(POSITION),
+ sourceLocation.getInteger(LINE),
+ sourceLocation.getInteger(COLUMN),
+ sourceLocation.getString(SOURCE_TEXT));
} finally {
- sourceLocation.release();
+ function.close();
+ sourceLocation.close();
}
}
@@ -92,7 +102,7 @@ public class Frame extends Mirror {
try {
return v8Object.executeStringFunction(ARGUMENT_NAME, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -113,9 +123,9 @@ public class Frame extends Mirror {
}
return new ValueMirror(result);
} finally {
- parameters.release();
+ parameters.close();
if (result != null) {
- result.release();
+ result.close();
}
}
}
@@ -137,9 +147,9 @@ public class Frame extends Mirror {
}
return createMirror(result);
} finally {
- parameters.release();
+ parameters.close();
if (result != null) {
- result.release();
+ result.close();
}
}
}
@@ -165,7 +175,7 @@ public class Frame extends Mirror {
try {
return v8Object.executeStringFunction(LOCAL_NAME, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -183,9 +193,9 @@ public class Frame extends Mirror {
scope = v8Object.executeObjectFunction(SCOPE, parameters);
return new Scope(scope);
} finally {
- parameters.release();
+ parameters.close();
if (scope != null) {
- scope.release();
+ scope.close();
}
}
}
@@ -198,11 +208,11 @@ public class Frame extends Mirror {
public FunctionMirror getFunction() {
V8Object function = null;
try {
- function = v8Object.executeObjectFunction("func", null);
+ function = v8Object.executeObjectFunction(FUNC, null);
return new FunctionMirror(function);
} finally {
if (function != null) {
- function.release();
+ function.close();
}
}
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/FunctionMirror.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/FunctionMirror.java
index f340f82c8..fb5fea4ea 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/FunctionMirror.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/FunctionMirror.java
@@ -17,6 +17,9 @@ import com.eclipsesource.v8.V8Object;
*/
public class FunctionMirror extends ObjectMirror {
+ private static final String NAME = "name";
+ private static final String SCRIPT = "script";
+
FunctionMirror(final V8Object v8Object) {
super(v8Object);
}
@@ -27,7 +30,22 @@ public class FunctionMirror extends ObjectMirror {
* @return The name of this function
*/
public String getName() {
- return v8Object.executeStringFunction("name", null);
+ return v8Object.executeStringFunction(NAME, null);
+ }
+
+ /**
+ * Returns the name of the Script associated with
+ * this FunctionMirror.
+ *
+ * @return The name of the script.
+ */
+ public String getScriptName() {
+ V8Object script = v8Object.executeObjectFunction(SCRIPT, null);
+ try {
+ return script.executeStringFunction(NAME, null);
+ } finally {
+ script.close();
+ }
}
@Override
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Mirror.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Mirror.java
index 9b8eaa380..3dc5e0259 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Mirror.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Mirror.java
@@ -156,14 +156,28 @@ public class Mirror implements Releasable {
return false;
}
+ /*
+ * (non-Javadoc)
+ * @see java.io.Closeable#close()
+ */
@Override
- public void release() {
+ public void close() {
if ((v8Object != null) && !v8Object.isReleased()) {
- v8Object.release();
+ v8Object.close();
v8Object = null;
}
}
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.Releasable#release()
+ */
+ @Override
+ @Deprecated
+ public void release() {
+ close();
+ }
+
protected static boolean isValue(final V8Object mirror) {
try {
return mirror.executeBooleanFunction(IS_VALUE, null);
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/ObjectMirror.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/ObjectMirror.java
index b9a5e8bf1..11131d6a5 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/ObjectMirror.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/ObjectMirror.java
@@ -64,9 +64,9 @@ public class ObjectMirror extends ValueMirror {
}
return result;
} finally {
- parameters.release();
+ parameters.close();
if (propertyNames != null) {
- propertyNames.release();
+ propertyNames.close();
}
}
}
@@ -87,9 +87,9 @@ public class ObjectMirror extends ValueMirror {
result = v8Object.executeArrayFunction(PROPERTIES, parameters);
return new PropertiesArray(result);
} finally {
- parameters.release();
+ parameters.close();
if ((result != null) && !result.isReleased()) {
- result.release();
+ result.close();
result = null;
}
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertiesArray.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertiesArray.java
index c247e9e25..e9cea1e36 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertiesArray.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertiesArray.java
@@ -36,17 +36,31 @@ public class PropertiesArray implements Releasable {
try {
return new PropertyMirror(result);
} finally {
- result.release();
+ result.close();
}
}
+ /*
+ * (non-Javadoc)
+ * @see java.io.Closeable#close()
+ */
@Override
- public void release() {
+ public void close() {
if (!v8Array.isReleased()) {
- v8Array.release();
+ v8Array.close();
}
}
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.Releasable#release()
+ */
+ @Override
+ @Deprecated
+ public void release() {
+ close();
+ }
+
/**
* Returns the number of properties contained in this array.
*
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertyMirror.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertyMirror.java
index be94c7669..235f657a3 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertyMirror.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/PropertyMirror.java
@@ -40,7 +40,7 @@ public class PropertyMirror extends Mirror {
try {
return createMirror(mirror);
} finally {
- mirror.release();
+ mirror.close();
}
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Scope.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Scope.java
index 820320600..9c3b0d258 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Scope.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/Scope.java
@@ -63,7 +63,7 @@ public class Scope extends Mirror {
try {
v8Object.executeVoidFunction(SET_VARIABLE_VALUE, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -80,7 +80,7 @@ public class Scope extends Mirror {
try {
v8Object.executeVoidFunction(SET_VARIABLE_VALUE, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -97,7 +97,7 @@ public class Scope extends Mirror {
try {
v8Object.executeVoidFunction(SET_VARIABLE_VALUE, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -114,7 +114,7 @@ public class Scope extends Mirror {
try {
v8Object.executeVoidFunction(SET_VARIABLE_VALUE, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -131,7 +131,7 @@ public class Scope extends Mirror {
try {
v8Object.executeVoidFunction(SET_VARIABLE_VALUE, parameters);
} finally {
- parameters.release();
+ parameters.close();
}
}
@@ -147,7 +147,7 @@ public class Scope extends Mirror {
return (ObjectMirror) createMirror(mirror);
} finally {
if ( mirror != null ) {
- mirror.release();
+ mirror.close();
}
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/SourceLocation.java b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/SourceLocation.java
index 71bb31b4a..a68148c8c 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/SourceLocation.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/debug/mirror/SourceLocation.java
@@ -19,8 +19,7 @@ public class SourceLocation {
private final int position;
private final int line;
private final int column;
- private final int start;
- private final int end;
+ private String sourceText;
/**
* Represents a JS Script Source Location
@@ -28,21 +27,19 @@ public class SourceLocation {
* @param position The position in the script
* @param line The line number
* @param column The column number
- * @param start The start of this location
- * @param end The end of this location
+ * @param sourceText The sourceCode at this location
*/
- public SourceLocation(final String scriptName, final int position, final int line, final int column, final int start, final int end) {
+ public SourceLocation(final String scriptName, final int position, final int line, final int column, final String sourceText) {
this.scriptName = scriptName;
this.position = position;
this.line = line;
this.column = column;
- this.start = start;
- this.end = end;
+ this.sourceText = sourceText;
}
@Override
public String toString() {
- return scriptName + " : " + position + " : " + line + " : " + column + " : " + start + " : " + end;
+ return scriptName + " : " + position + " : " + line + " : " + column + " : " + sourceText;
}
/**
@@ -78,19 +75,10 @@ public class SourceLocation {
}
/**
- * Returns the start of this SourceLocation.
- * @return The start of this SourceLocation.
+ * Returns the source text for this SourceLocation.
+ * @return The source text for this SourceLocation.
*/
- public int getStart() {
- return start;
+ public String getSourceText() {
+ return sourceText;
}
-
- /**
- * Returns the end of this SourceLocation.
- * @return The end of this SourceLocation.
- */
- public int getEnd() {
- return end;
- }
-
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/ArrayBuffer.java b/fine-j2v8/src/com/eclipsesource/v8/utils/ArrayBuffer.java
new file mode 100644
index 000000000..d1a39b8ec
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/ArrayBuffer.java
@@ -0,0 +1,69 @@
+package com.eclipsesource.v8.utils;
+/*******************************************************************************
+ * Copyright (c) 2019 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * EclipseSource - initial API and implementation
+ ******************************************************************************/
+
+import java.nio.ByteBuffer;
+
+import com.eclipsesource.v8.V8;
+import com.eclipsesource.v8.V8ArrayBuffer;
+
+/**
+ * A lightweight handle to a V8TypedArray. This handle provides
+ * access to a V8TypedArray. This handle does not need to be
+ * closed, but if the type array is accessed using getV8TypedArray
+ * then the result must be closed.
+ *
+ * The underlying V8TypedArray may be reclaimed by the JavaScript
+ * garbage collector. To check if it's still available, use
+ * isAvailable.
+ */
+public class ArrayBuffer {
+
+ private V8ArrayBuffer arrayBuffer;
+
+ ArrayBuffer(final V8ArrayBuffer arrayBuffer) {
+ this.arrayBuffer = (V8ArrayBuffer) arrayBuffer.twin().setWeak();
+ }
+
+ /**
+ * Create a new ArrayBuffer from a java.nio.ByteBuffer
+ *
+ * @param v8 the Runtime on which to create the ArrayBuffer
+ * @param byteBuffer the ByteBuffer to use to back the ArrayBuffer
+ */
+ public ArrayBuffer(final V8 v8, final ByteBuffer byteBuffer) {
+ V8ArrayBuffer v8ArrayBuffer = new V8ArrayBuffer(v8, byteBuffer);
+ try {
+ arrayBuffer = (V8ArrayBuffer) v8ArrayBuffer.twin().setWeak();
+ } finally {
+ v8ArrayBuffer.close();
+ }
+ }
+
+ /**
+ * Determine if the underlying V8ArrayBuffer is still available, or if it's been cleaned up by the JavaScript
+ * garbage collector.
+ *
+ * @return true if the underlying V8ArrayBuffer is still available, false otherwise.
+ */
+ public boolean isAvailable() {
+ return !arrayBuffer.isReleased();
+ }
+
+ /**
+ * Returns the underlying V8ArrayBuffer.
+ * @return the underlying V8ArrayBuffer.
+ */
+ public V8ArrayBuffer getV8ArrayBuffer() {
+ return arrayBuffer.twin();
+ }
+
+}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/ConcurrentV8.java b/fine-j2v8/src/com/eclipsesource/v8/utils/ConcurrentV8.java
new file mode 100644
index 000000000..a1b9d602b
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/ConcurrentV8.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Brandon Sanders
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Brandon Sanders - initial API and implementation and/or initial documentation
+ ******************************************************************************/
+package com.eclipsesource.v8.utils;
+
+import com.eclipsesource.v8.V8;
+
+/**
+ * Wrapper class for an {@link com.eclipsesource.v8.V8} instance that allows
+ * a V8 instance to be invoked from across threads without explicitly acquiring
+ * or releasing locks.
+ *
+ * This class does not guarantee the safety of any objects stored in or accessed
+ * from the wrapped V8 instance; it only enables callers to interact with a V8
+ * instance from any thread. The V8 instance represented by this class should
+ * still be treated with thread safety in mind
+ *
+ * @author Brandon Sanders [brandon@alicorn.io]
+ * @author R. Ian Bull - Additional API
+ */
+public final class ConcurrentV8 {
+ private V8 v8 = null;
+
+ /**
+ * Create a new ConcurrentV8. A ConcurrentV8 allows multiple
+ * threads to work with the same V8 engine by releasing
+ * the locks between calls.
+ */
+ public ConcurrentV8() {
+ v8 = V8.createV8Runtime();
+ v8.getLocker().release();
+ }
+
+ /**
+ * Returns the V8 runtime backing by this ConcurrentV8
+ *
+ * @return The V8 runtime backing this ConcurrentV8
+ */
+ public V8 getV8() {
+ return v8;
+ }
+
+ /**
+ * Runs an {@link V8Runnable} on the V8 thread.
+ *
+ * Note: This method executes synchronously, not asynchronously;
+ * it will not return until the passed {@link V8Runnable} is done
+ * executing. The method is also synchronized, so it will block until it
+ * gets a chance to run.
+ *
+ * @param runnable {@link V8Runnable} to run.
+ */
+ public synchronized void run(final V8Runnable runnable) {
+ try {
+ v8.getLocker().acquire();
+ runnable.run(v8);
+ } finally {
+ if ((v8 != null) && (v8.getLocker() != null) && v8.getLocker().hasLock()) {
+ v8.getLocker().release();
+ }
+ }
+ }
+
+ /**
+ * Releases the underlying {@link V8} instance.
+ *
+ * This method should be invoked once you're done using this object,
+ * otherwise a large amount of garbage could be left on the JVM due to
+ * native resources.
+ *
+ * Note: If this method has already been called once, it
+ * will do nothing.
+ */
+ public void release() {
+ if ((v8 != null) && !v8.isReleased()) {
+ // Release the V8 instance from the V8 thread context.
+ run(new V8Runnable() {
+ @Override
+ public void run(final V8 v8) {
+ if ((v8 != null) && !v8.isReleased()) {
+ v8.close();
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/MemoryManager.java b/fine-j2v8/src/com/eclipsesource/v8/utils/MemoryManager.java
index ddb4f88df..7f236ae56 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/utils/MemoryManager.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/MemoryManager.java
@@ -98,7 +98,7 @@ public class MemoryManager {
releasing = true;
try {
for (V8Value reference : references) {
- reference.release();
+ reference.close();
}
v8.removeReferenceHandler(memoryManagerReferenceHandler);
references.clear();
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/SingleTypeAdapter.java b/fine-j2v8/src/com/eclipsesource/v8/utils/SingleTypeAdapter.java
new file mode 100644
index 000000000..d8efca58d
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/SingleTypeAdapter.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * EclipseSource - initial API and implementation
+ ******************************************************************************/
+package com.eclipsesource.v8.utils;
+
+/**
+ * Adapt all instances of a single type from JavaScript to Java.
+ * The TypeAdapter can be used with the V8ObjectUtils to allow users to customize
+ * the conversion.
+ */
+public abstract class SingleTypeAdapter implements TypeAdapter {
+
+ private int typeToAdapt;
+
+ /**
+ * Create a SingleTypeAdapter
+ *
+ * @param typeToAdapt The V8 Type this TypeAdapter should be applied to.
+ */
+ public SingleTypeAdapter(final int typeToAdapt) {
+ this.typeToAdapt = typeToAdapt;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.eclipsesource.v8.utils.TypeAdapter#adapt(int, java.lang.Object)
+ */
+ @Override
+ public Object adapt(final int type, final Object value) {
+ if (type == typeToAdapt) {
+ return adapt(value);
+ }
+ return TypeAdapter.DEFAULT;
+ }
+
+ /**
+ * Adapt an object from V8 to Java.
+ *
+ * If the value is a V8Value (V8Object) then it will be released after
+ * this method is called. If you wish to retain the object, call
+ * ((V8Value)value).twin();
+ *
+ * @param value The V8 Object to be converted.
+ * @return The adapted Java Object or {@link TypeAdapter#DEFAULT} for the default conversion.
+ */
+ public abstract Object adapt(final Object value);
+
+}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/TypeAdapter.java b/fine-j2v8/src/com/eclipsesource/v8/utils/TypeAdapter.java
new file mode 100644
index 000000000..7bae5eb2f
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/TypeAdapter.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2017 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * EclipseSource - initial API and implementation
+ ******************************************************************************/
+package com.eclipsesource.v8.utils;
+
+/**
+ * An interface which allows a plug-able conversion from V8Value types to Java objects.
+ * The TypeAdapter can be used with the V8ObjectUtils to allow users to customize
+ * the conversion.
+ */
+public interface TypeAdapter {
+
+ /**
+ * A default adapter that if returned in {@link TypeAdapter#adapt(int, Object)}, will result
+ * in the default type adaption.
+ */
+ public static final Object DEFAULT = new Object();
+
+ /**
+ * Adapt an object from V8 to Java.
+ *
+ * If the value is a V8Value (V8Object) then it will be released after
+ * this method is called. If you wish to retain the object, call
+ * ((V8Value)value).twin();
+ *
+ * @param type The Type of the object to be adapted.
+ * @param value The V8 Object to be converted.
+ * @return The adapted Java Object or {@link TypeAdapter#DEFAULT} for the default conversion.
+ */
+ public Object adapt(int type, Object value);
+
+}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/TypedArray.java b/fine-j2v8/src/com/eclipsesource/v8/utils/TypedArray.java
new file mode 100644
index 000000000..1c1616ab7
--- /dev/null
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/TypedArray.java
@@ -0,0 +1,73 @@
+package com.eclipsesource.v8.utils;
+/*******************************************************************************
+ * Copyright (c) 2019 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * EclipseSource - initial API and implementation
+ ******************************************************************************/
+
+import com.eclipsesource.v8.V8;
+import com.eclipsesource.v8.V8ArrayBuffer;
+import com.eclipsesource.v8.V8TypedArray;
+
+/**
+ * A lightweight handle to a V8TypedArray. This handle provides
+ * access to a V8TypedArray. This handle does not need to be
+ * closed, but if the type array is accessed using getV8TypedArray
+ * then the result must be closed.
+ *
+ * The underlying V8TypedArray may be reclaimed by the JavaScript
+ * garbage collector. To check if it's still available, use
+ * isAvailable.
+ */
+public class TypedArray {
+
+ private V8TypedArray typedArray;
+
+ TypedArray(final V8TypedArray typedArray) {
+ this.typedArray = (V8TypedArray) typedArray.twin().setWeak();
+ }
+
+ /**
+ * Create a new TypedArray from an ArrayBuffer.
+ *
+ * @param v8 the V8Runtime on which to create the TypedArray
+ * @param buffer the ArrayBuffer to use to back the TypedArray
+ * @param type the Type of Array to create
+ * @param offset the Offset into the ArrayBuffer in which to map the TyepdArray
+ * @param size the Size of the TypedArray
+ */
+ public TypedArray(final V8 v8, final ArrayBuffer buffer, final int type, final int offset, final int size) {
+ V8ArrayBuffer v8ArrayBuffer = buffer.getV8ArrayBuffer();
+ V8TypedArray v8typedArray = new V8TypedArray(v8, v8ArrayBuffer, type, offset, size);
+ try {
+ typedArray = (V8TypedArray) v8typedArray.twin().setWeak();
+ } finally {
+ v8ArrayBuffer.close();
+ v8typedArray.close();
+ }
+ }
+
+ /**
+ * Determine if the underlying V8TypedArray is still available, or if it's been cleaned up by the JavaScript
+ * garbage collector.
+ *
+ * @return true if the underlying V8TypedArray is still available, false otherwise.
+ */
+ public boolean isAvailable() {
+ return !typedArray.isReleased();
+ }
+
+ /**
+ * Returns the underlying V8TypedArray.
+ * @return the underlying V8TypedArray.
+ */
+ public V8TypedArray getV8TypedArray() {
+ return (V8TypedArray) typedArray.twin();
+ }
+
+}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/V8Executor.java b/fine-j2v8/src/com/eclipsesource/v8/utils/V8Executor.java
index 50f8c3490..b7e2b6c27 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/utils/V8Executor.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/V8Executor.java
@@ -149,8 +149,8 @@ public class V8Executor extends Thread {
parameters.push(strings);
runtime.executeVoidFunction(messageHandler, parameters);
} finally {
- strings.release();
- parameters.release();
+ strings.close();
+ parameters.close();
}
}
}
@@ -159,7 +159,7 @@ public class V8Executor extends Thread {
} finally {
synchronized (this) {
if (runtime.getLocker().hasLock()) {
- runtime.release();
+ runtime.close();
runtime = null;
}
terminated = true;
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/V8Map.java b/fine-j2v8/src/com/eclipsesource/v8/utils/V8Map.java
index 08343fb67..fa8d58ca1 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/utils/V8Map.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/V8Map.java
@@ -40,14 +40,24 @@ public class V8Map implements Map, Releasable {
twinMap = new HashMap();
}
+ /*
+ * (non-Javadoc)
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ this.clear();
+ }
+
/**
* Releases all the resources associated with this map. A
* map can be used again once it's released, although
* if it's used again it should be released again.
*/
@Override
+ @Deprecated
public void release() {
- this.clear();
+ close();
}
/*
@@ -116,7 +126,7 @@ public class V8Map implements Map, Releasable {
V result = map.remove(key);
V8Value twin = twinMap.remove(key);
if (twin != null) {
- twin.release();
+ twin.close();
}
return result;
}
@@ -127,7 +137,7 @@ public class V8Map implements Map, Releasable {
*/
@Override
public void putAll(final Map extends V8Value, ? extends V> m) {
- for (Entry extends V8Value, ? extends V> entry : m.entrySet()) {
+ for (java.util.Map.Entry extends V8Value, ? extends V> entry : m.entrySet()) {
this.put(entry.getKey(), entry.getValue());
}
}
@@ -140,7 +150,7 @@ public class V8Map implements Map, Releasable {
public void clear() {
map.clear();
for (V8Value V8Value : twinMap.keySet()) {
- V8Value.release();
+ V8Value.close();
}
twinMap.clear();
}
@@ -168,7 +178,7 @@ public class V8Map implements Map, Releasable {
* @see java.util.Map#entrySet()
*/
@Override
- public Set> entrySet() {
+ public Set> entrySet() {
return map.entrySet();
}
diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/V8ObjectUtils.java b/fine-j2v8/src/com/eclipsesource/v8/utils/V8ObjectUtils.java
index 7f8c888fb..b43356585 100644
--- a/fine-j2v8/src/com/eclipsesource/v8/utils/V8ObjectUtils.java
+++ b/fine-j2v8/src/com/eclipsesource/v8/utils/V8ObjectUtils.java
@@ -10,7 +10,6 @@
******************************************************************************/
package com.eclipsesource.v8.utils;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
@@ -18,23 +17,13 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import com.eclipsesource.v8.Releasable;
import com.eclipsesource.v8.V8;
import com.eclipsesource.v8.V8Array;
import com.eclipsesource.v8.V8ArrayBuffer;
import com.eclipsesource.v8.V8Object;
import com.eclipsesource.v8.V8TypedArray;
import com.eclipsesource.v8.V8Value;
-import com.eclipsesource.v8.utils.typedarrays.ArrayBuffer;
-import com.eclipsesource.v8.utils.typedarrays.Float32Array;
-import com.eclipsesource.v8.utils.typedarrays.Float64Array;
-import com.eclipsesource.v8.utils.typedarrays.Int16Array;
-import com.eclipsesource.v8.utils.typedarrays.Int32Array;
-import com.eclipsesource.v8.utils.typedarrays.Int8Array;
-import com.eclipsesource.v8.utils.typedarrays.TypedArray;
-import com.eclipsesource.v8.utils.typedarrays.UInt16Array;
-import com.eclipsesource.v8.utils.typedarrays.UInt32Array;
-import com.eclipsesource.v8.utils.typedarrays.UInt8Array;
-import com.eclipsesource.v8.utils.typedarrays.UInt8ClampedArray;
/**
* A set of static helper methods to convert V8Objects / V8Arrays to
@@ -43,7 +32,57 @@ import com.eclipsesource.v8.utils.typedarrays.UInt8ClampedArray;
*/
public class V8ObjectUtils {
- private static final Object IGNORE = new Object();
+ private static final Object IGNORE = new Object();
+ private static final TypeAdapter DEFAULT_TYPE_ADAPTER = new DefaultTypeAdapter();
+
+ /**
+ * Create a Java Object from a result from V8. V8 can return
+ * basic Java types, or V8Values (V8Object, V8Array, etc...). This method
+ * will attempt to convert the result into a pure Java object using a
+ * deep copy.
+ *
+ * If the input is basic Java type (Integer, Double, Boolean, String)
+ * it will be returned. If the input is a V8Value, it will be converted.
+ *
+ * All elements in the V8Object are released after they are accessed.
+ * However, the root object itself is not released.
+ *
+ * @param v8Object The input to convert.
+ * @return A Java object representing the input.
+ */
+ public static Object getValue(final Object v8Object) {
+ return getValue(v8Object, DEFAULT_TYPE_ADAPTER);
+ }
+
+ /**
+ * Create a Java Object from a result from V8 using a {@link TypeAdapter} to convert
+ * objects. V8 can return basic Java types or V8Values (V8Object, V8Array, etc...). This
+ * method will attempt to convert the result into a pure Java object using
+ * a deep copy.
+ *
+ * If the input is basic Java type (Integer, Double, Boolean, String)
+ * it will be returned. If the input is a V8Value, it will be converted.
+ *
+ * All elements in the V8Object are released after they are accessed.
+ * However, the root object itself is not released.
+ *
+ * @param v8Object The input to convert.
+ * @param adapter The {@link TypeAdapter} to use for the object conversions.
+ * @return A Java object representing the input.
+ */
+ public static Object getValue(final Object v8Object, final TypeAdapter adapter) {
+ V8Map