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 m) { - for (Entry entry : m.entrySet()) { + for (java.util.Map.Entry 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 cache = new V8Map(); + try { + if (v8Object instanceof V8Value) { + int type = ((V8Value) v8Object).getV8Type(); + return getValue(v8Object, type, cache, adapter); + } else { + return v8Object; + } + } finally { + cache.close(); + } + } /** * Creates a Map from a V8Object using a deep copy. All elements @@ -55,11 +94,25 @@ public class V8ObjectUtils { * @return A map representing a deep copy of the V8Object rooted at 'object'. */ public static Map toMap(final V8Object object) { + return toMap(object, DEFAULT_TYPE_ADAPTER); + } + + /** + * Creates a Map from a V8Object using a deep copy and a TypeAdapter to handle + * type conversions. All elements in the V8Object are released after they are accessed. + * However, the root object itself is not released. + * + * @param object The root of the V8Object graph. + * @param adapter The {@link TypeAdapter} to use for the object conversions. + * + * @return A map representing a deep copy of the V8Object rooted at 'object'. + */ + public static Map toMap(final V8Object object, final TypeAdapter adapter) { V8Map cache = new V8Map(); try { - return toMap(object, cache); + return toMap(object, cache, adapter); } finally { - cache.release(); + cache.close(); } } @@ -73,11 +126,25 @@ public class V8ObjectUtils { * @return A list representing a deep copy of the V8Array rooted at 'array'. */ public static List toList(final V8Array array) { + return toList(array, DEFAULT_TYPE_ADAPTER); + } + + /** + * Creates a List from a V8Array using a deep copy and a TypeAdapter to handle + * type conversions. All elements in the V8Array are released after they are accessed. + * However, the root array itself is not released. + * + * @param array The root of the V8Array graph. + * @param adapter The {@link TypeAdapter} to use for the object conversions. + * + * @return A list representing a deep copy of the V8Array rooted at 'array'. + */ + public static List toList(final V8Array array, final TypeAdapter adapter) { V8Map cache = new V8Map(); try { - return toList(array, cache); + return toList(array, cache, adapter); } finally { - cache.release(); + cache.close(); } } @@ -179,7 +246,7 @@ public class V8ObjectUtils { return toV8Object(v8, map, cache).twin(); } finally { for (V8Value v8Object : cache.values()) { - v8Object.release(); + v8Object.close(); } } } @@ -199,7 +266,7 @@ public class V8ObjectUtils { return toV8Array(v8, list, cache).twin(); } finally { for (V8Value v8Object : cache.values()) { - v8Object.release(); + v8Object.close(); } } } @@ -225,13 +292,13 @@ public class V8ObjectUtils { Map cache = new Hashtable(); try { Object result = getV8Result(v8, value, cache); - if (result instanceof V8Object) { - return ((V8Object) result).twin(); + if (result instanceof V8Value) { + return ((V8Value) result).twin(); } return result; } finally { for (V8Value v8Object : cache.values()) { - v8Object.release(); + v8Object.close(); } } } @@ -252,7 +319,7 @@ public class V8ObjectUtils { pushValue(v8, array, value, cache); } finally { for (V8Value v8Object : cache.values()) { - v8Object.release(); + v8Object.close(); } } } @@ -271,10 +338,55 @@ public class V8ObjectUtils { */ public static Object getValue(final V8Array array, final int index) { V8Map cache = new V8Map(); + Object object = null; + int type = V8Value.UNDEFINED; try { - return getValue(array, index, cache); + object = array.get(index); + type = array.getType(index); + Object result = getValue(object, type, cache, DEFAULT_TYPE_ADAPTER); + if ((result == object) && (result instanceof V8Value)) { + return ((V8Value) result).twin(); + } + return result; } finally { - cache.release(); + if (object instanceof Releasable) { + ((Releasable) object).release(); + } + cache.close(); + } + } + + /** + * Gets a Java Object representing the value at the given index in the V8Array. + * A TypeAdapter is used to convert values from V8Objects to Java Objects. + * If the value is a primitive (int, boolean or double) then a boxed instance + * is returned. If the value is a String, then a String is returned. If + * the value is a V8Object or V8Array, then a Map or List is returned. + * + * @param array The array on which to lookup the value. The array is not + * released. + * @param index The index whose element to lookup. + * @param adapter The {@link TypeAdapter} to use for the object conversions. + * + * @return A Java Object representing the value at a given index. + */ + public static Object getValue(final V8Array array, final int index, final TypeAdapter adapter) { + V8Map cache = new V8Map(); + Object object = null; + int type = V8Value.UNDEFINED; + try { + object = array.get(index); + type = array.getType(index); + Object result = getValue(object, type, cache, adapter); + if ((result == object) && (result instanceof V8Value)) { + return ((V8Value) result).twin(); + } + return result; + } finally { + if (object instanceof Releasable) { + ((Releasable) object).release(); + } + cache.close(); } } @@ -291,36 +403,75 @@ public class V8ObjectUtils { * @return A Java Object representing the value at a given key. */ public static Object getValue(final V8Object object, final String key) { + return getValue(object, key, DEFAULT_TYPE_ADAPTER); + } + + /** + * Gets a Java Object representing the value with the given key in the V8Object. + * A TypeAdapter is used to convert values from V8Objects to Java Objects. + * If the value is a primitive (int, boolean or double) then a boxed instance + * is returned. If the value is a String, then a String is returned. If + * the value is a V8Object or V8Array, then a Map or List is returned. + * + * @param v8Object The object on which to lookup the value. The object is not + * released. + * @param key The key to use to lookup the value. + * @param adapter The {@link TypeAdapter} to use for the object conversions. + * + * @return A Java Object representing the value at a given key. + */ + public static Object getValue(final V8Object v8Object, final String key, final TypeAdapter adapter) { V8Map cache = new V8Map(); + Object object = null; + int type = V8Value.UNDEFINED; try { - return getValue(object, key, cache); + object = v8Object.get(key); + type = v8Object.getType(key); + Object result = getValue(object, type, cache, adapter); + if ((result == object) && (result instanceof V8Value)) { + return ((V8Value) result).twin(); + } + return result; } finally { - cache.release(); + if (object instanceof Releasable) { + ((Releasable) object).release(); + } + cache.close(); } } @SuppressWarnings("unchecked") - private static Map toMap(final V8Object object, final V8Map cache) { - if (object == null) { + private static Map toMap(final V8Object v8Object, final V8Map cache, final TypeAdapter adapter) { + if (v8Object == null) { return Collections.emptyMap(); } - if (cache.containsKey(object)) { - return (Map) cache.get(object); + if (cache.containsKey(v8Object)) { + return (Map) cache.get(v8Object); } Map result = new V8PropertyMap(); - cache.put(object, result); - String[] keys = object.getKeys(); + cache.put(v8Object, result); + String[] keys = v8Object.getKeys(); for (String key : keys) { - Object value = getValue(object, key, cache); - if (value != IGNORE) { - result.put(key, value); + Object object = null; + int type = V8Value.UNDEFINED; + try { + object = v8Object.get(key); + type = v8Object.getType(key); + Object value = getValue(object, type, cache, adapter); + if (value != IGNORE) { + result.put(key, value); + } + } finally { + if (object instanceof Releasable) { + ((Releasable) object).release(); + } } } return result; } @SuppressWarnings("unchecked") - private static List toList(final V8Array array, final V8Map cache) { + private static List toList(final V8Array array, final V8Map cache, final TypeAdapter adapter) { if (array == null) { return Collections.emptyList(); } @@ -330,14 +481,42 @@ public class V8ObjectUtils { List result = new ArrayList(); cache.put(array, result); for (int i = 0; i < array.length(); i++) { - Object value = getValue(array, i, cache); - if (value != IGNORE) { - result.add(getValue(array, i, cache)); + Object object = null; + int type = V8Value.UNDEFINED; + try { + object = array.get(i); + type = array.getType(i); + Object value = getValue(object, type, cache, adapter); + if (value != IGNORE) { + result.add(value); + } + } finally { + if (object instanceof Releasable) { + ((Releasable) object).release(); + } } } return result; } + private static V8TypedArray toV8TypedArray(final V8 v8, final TypedArray typeArray, final Map cache) { + if (cache.containsKey(typeArray)) { + return (V8TypedArray) cache.get(typeArray); + } + V8TypedArray result = typeArray.getV8TypedArray(); + cache.put(typeArray, result); + return result; + } + + private static V8ArrayBuffer toV8ArrayBuffer(final V8 v8, final ArrayBuffer arrayBuffer, final Map cache) { + if (cache.containsKey(arrayBuffer)) { + return (V8ArrayBuffer) cache.get(arrayBuffer); + } + V8ArrayBuffer result = arrayBuffer.getV8ArrayBuffer(); + cache.put(arrayBuffer, result); + return result; + } + private static V8Object toV8Object(final V8 v8, final Map map, final Map cache) { if (cache.containsKey(map)) { return (V8Object) cache.get(map); @@ -349,7 +528,7 @@ public class V8ObjectUtils { setValue(v8, result, entry.getKey(), entry.getValue(), cache); } } catch (IllegalStateException e) { - result.release(); + result.close(); throw e; } return result; @@ -367,35 +546,12 @@ public class V8ObjectUtils { pushValue(v8, result, value, cache); } } catch (IllegalStateException e) { - result.release(); + result.close(); throw e; } return result; } - private static V8ArrayBuffer toV8ArrayBuffer(final V8 v8, final ArrayBuffer arrayBuffer, final Map cache) { - if (cache.containsKey(arrayBuffer)) { - return (V8ArrayBuffer) cache.get(arrayBuffer); - } - V8ArrayBuffer result = new V8ArrayBuffer(v8, arrayBuffer.getByteBuffer()); - cache.put(arrayBuffer, result); - return result; - } - - private static V8TypedArray toV8TypedArray(final V8 v8, final TypedArray typedArray, final Map cache) { - if (cache.containsKey(typedArray)) { - return (V8TypedArray) cache.get(typedArray); - } - V8ArrayBuffer arrayBuffer = new V8ArrayBuffer(v8, typedArray.getByteBuffer()); - try { - V8TypedArray result = new V8TypedArray(v8, arrayBuffer, typedArray.getType(), 0, typedArray.length()); - cache.put(typedArray, result); - return result; - } finally { - arrayBuffer.release(); - } - } - @SuppressWarnings("unchecked") private static Object getV8Result(final V8 v8, final Object value, final Map cache) { if (cache.containsKey(value)) { @@ -413,30 +569,30 @@ public class V8ObjectUtils { return value; } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked", "rawtypes", "resource" }) private static void pushValue(final V8 v8, final V8Array result, final Object value, final Map cache) { if (value == null) { result.pushUndefined(); } else if (value instanceof Integer) { - result.push((Integer) value); + result.push(value); } else if (value instanceof Long) { result.push(new Double((Long) value)); } else if (value instanceof Double) { - result.push((Double) value); + result.push(value); } else if (value instanceof Float) { - result.push((Float) value); + result.push(value); } else if (value instanceof String) { result.push((String) value); } else if (value instanceof Boolean) { - result.push((Boolean) value); - } else if (value instanceof V8Object) { - result.push((V8Object) value); + result.push(value); } else if (value instanceof TypedArray) { V8TypedArray v8TypedArray = toV8TypedArray(v8, (TypedArray) value, cache); result.push(v8TypedArray); } else if (value instanceof ArrayBuffer) { V8ArrayBuffer v8ArrayBuffer = toV8ArrayBuffer(v8, (ArrayBuffer) value, cache); result.push(v8ArrayBuffer); + } else if (value instanceof V8Value) { + result.push((V8Value) value); } else if (value instanceof Map) { V8Object object = toV8Object(v8, (Map) value, cache); result.push(object); @@ -448,14 +604,14 @@ public class V8ObjectUtils { } } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked", "rawtypes", "resource" }) private static void setValue(final V8 v8, final V8Object result, final String key, final Object value, final Map cache) { if (value == null) { result.addUndefined(key); } else if (value instanceof Integer) { result.add(key, (Integer) value); } else if (value instanceof Long) { - result.add(key, (int) (long) (Long) value); + result.add(key, (Long) value); } else if (value instanceof Double) { result.add(key, (Double) value); } else if (value instanceof Float) { @@ -464,14 +620,14 @@ public class V8ObjectUtils { result.add(key, (String) value); } else if (value instanceof Boolean) { result.add(key, (Boolean) value); - } else if (value instanceof V8Object) { - result.add(key, (V8Object) value); } else if (value instanceof TypedArray) { - V8TypedArray typedArray = toV8TypedArray(v8, (TypedArray) value, cache); - result.add(key, typedArray); + V8TypedArray v8TypedArray = toV8TypedArray(v8, (TypedArray) value, cache); + result.add(key, v8TypedArray); } else if (value instanceof ArrayBuffer) { V8ArrayBuffer v8ArrayBuffer = toV8ArrayBuffer(v8, (ArrayBuffer) value, cache); result.add(key, v8ArrayBuffer); + } else if (value instanceof V8Value) { + result.add(key, (V8Value) value); } else if (value instanceof Map) { V8Object object = toV8Object(v8, (Map) value, cache); result.add(key, object); @@ -483,142 +639,33 @@ public class V8ObjectUtils { } } - private static Object getValue(final V8Array array, final int index, final V8Map cache) { - int valueType = array.getType(index); - switch (valueType) { - case V8Value.INTEGER: - return array.getInteger(index); - case V8Value.DOUBLE: - return array.getDouble(index); - case V8Value.BOOLEAN: - return array.getBoolean(index); - case V8Value.STRING: - return array.getString(index); - case V8Value.V8_FUNCTION: - return IGNORE; - case V8Value.V8_ARRAY_BUFFER: - V8ArrayBuffer buffer = (V8ArrayBuffer) array.get(index); - try { - return new ArrayBuffer(buffer.getBackingStore()); - } finally { - buffer.release(); - } - case V8Value.V8_TYPED_ARRAY: - V8Array typedArray = array.getArray(index); - try { - return toTypedArray(typedArray); - } finally { - if (typedArray instanceof V8Array) { - typedArray.release(); - } - } - case V8Value.V8_ARRAY: - V8Array arrayValue = array.getArray(index); - try { - return toList(arrayValue, cache); - } finally { - if (arrayValue instanceof V8Array) { - arrayValue.release(); - } - } - case V8Value.V8_OBJECT: - V8Object objectValue = array.getObject(index); - try { - return toMap(objectValue, cache); - } finally { - if (objectValue instanceof V8Object) { - objectValue.release(); - } - } - case V8Value.NULL: - return null; - case V8Value.UNDEFINED: - return V8.getUndefined(); - default: - throw new IllegalStateException("Cannot find type for index: " + index); + private static Object getValue(final Object value, final int valueType, final V8Map cache, final TypeAdapter adapter) { + Object adapterResult = adapter.adapt(valueType, value); + if (TypeAdapter.DEFAULT != adapterResult) { + return adapterResult; } - } - - private static Object toTypedArray(final V8Array typedArray) { - int arrayType = typedArray.getType(); - ByteBuffer buffer = ((V8TypedArray) typedArray).getByteBuffer(); - switch (arrayType) { - case V8Value.INT_8_ARRAY: - return new Int8Array(buffer); - case V8Value.UNSIGNED_INT_8_ARRAY: - return new UInt8Array(buffer); - case V8Value.UNSIGNED_INT_8_CLAMPED_ARRAY: - return new UInt8ClampedArray(buffer); - case V8Value.INT_16_ARRAY: - return new Int16Array(buffer); - case V8Value.UNSIGNED_INT_16_ARRAY: - return new UInt16Array(buffer); - case V8Value.INT_32_ARRAY: - return new Int32Array(buffer); - case V8Value.UNSIGNED_INT_32_ARRAY: - return new UInt32Array(buffer); - case V8Value.FLOAT_32_ARRAY: - return new Float32Array(buffer); - case V8Value.FLOAT_64_ARRAY: - return new Float64Array(buffer); - default: - throw new IllegalStateException("Known Typed Array type: " + V8Value.getStringRepresentaion(arrayType)); - } - } - - private static Object getValue(final V8Object object, final String key, final V8Map cache) { - int valueType = object.getType(key); switch (valueType) { case V8Value.INTEGER: - return object.getInteger(key); case V8Value.DOUBLE: - return object.getDouble(key); case V8Value.BOOLEAN: - return object.getBoolean(key); case V8Value.STRING: - return object.getString(key); + return value; case V8Value.V8_FUNCTION: return IGNORE; case V8Value.V8_ARRAY_BUFFER: - V8ArrayBuffer buffer = (V8ArrayBuffer) object.get(key); - try { - return new ArrayBuffer(buffer.getBackingStore()); - } finally { - buffer.release(); - } + return new ArrayBuffer((V8ArrayBuffer) value); case V8Value.V8_TYPED_ARRAY: - V8Array typedArray = object.getArray(key); - try { - return toTypedArray(typedArray); - } finally { - if (typedArray instanceof V8Array) { - typedArray.release(); - } - } + return new TypedArray((V8TypedArray) value); case V8Value.V8_ARRAY: - V8Array array = object.getArray(key); - try { - return toList(array, cache); - } finally { - if (array instanceof V8Array) { - array.release(); - } - } + return toList((V8Array) value, cache, adapter); case V8Value.V8_OBJECT: - V8Object child = object.getObject(key); - try { - return toMap(child, cache); - } finally { - if (child instanceof V8Object) { - child.release(); - } - } + return toMap((V8Object) value, cache, adapter); case V8Value.NULL: return null; case V8Value.UNDEFINED: return V8.getUndefined(); default: - throw new IllegalStateException("Cannot find type for key: " + key); + throw new IllegalStateException("Cannot convert type " + V8Value.getStringRepresentation(valueType)); } } @@ -626,6 +673,13 @@ public class V8ObjectUtils { } + static class DefaultTypeAdapter implements TypeAdapter { + @Override + public Object adapt(final int type, final Object value) { + return TypeAdapter.DEFAULT; + } + } + static class ListWrapper { private List list; diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/V8PropertyMap.java b/fine-j2v8/src/com/eclipsesource/v8/utils/V8PropertyMap.java index 3b69784aa..db71881c4 100644 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/V8PropertyMap.java +++ b/fine-j2v8/src/com/eclipsesource/v8/utils/V8PropertyMap.java @@ -20,7 +20,7 @@ import java.util.Set; /** * A custom map is needed because the existing HashMaps - * do not self containment, and Hashtables do not + * do not allow self containment, and Hashtables do not * allow nulls as values. * * This class is not considered API. @@ -166,7 +166,7 @@ class V8PropertyMap implements Map { */ @Override public Set> entrySet() { - HashSet> result = new HashSet>(map.entrySet()); + HashSet> result = new HashSet>(map.entrySet()); for (String nullKey : nulls) { result.add(new SimpleEntry(nullKey, null)); } diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/V8Thread.java b/fine-j2v8/src/com/eclipsesource/v8/utils/V8Thread.java index 5f0096f16..0233ffb65 100644 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/V8Thread.java +++ b/fine-j2v8/src/com/eclipsesource/v8/utils/V8Thread.java @@ -47,7 +47,7 @@ public class V8Thread extends Thread { } finally { synchronized (this) { if (runtime.getLocker().hasLock()) { - runtime.release(); + runtime.close(); runtime = null; } } diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/ArrayBuffer.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/ArrayBuffer.java deleted file mode 100644 index ccc1d34a6..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/ArrayBuffer.java +++ /dev/null @@ -1,109 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -/** - * A wrapper class for java.nio.ByteBuffer. This class provides some convenience methods - * for working with the ByteBuffer. Furthermore, this class can be converted to a - * V8ByteBuffer using V8ObjectUtils. - */ -public class ArrayBuffer { - - private ByteBuffer byteBuffer; - - /** - * Create a new ArrayBuffer with an initial capacity. - * - * @param capacity The capacity of this ByteBuffer. - */ - public ArrayBuffer(final int capacity) { - byteBuffer = ByteBuffer.allocateDirect(capacity); - } - - /** - * Create a new ArrayBuffer from a byte array. The array buffer will be allocated with the same - * size as the byte array, and the contents of the byte array will be copied to the ArrayBuffer. - * - * @param src The byte array from which the ArrayBuffer will be initialized. - */ - public ArrayBuffer(final byte[] src) { - byteBuffer = ByteBuffer.allocateDirect(src.length); - byteBuffer.put(src, 0, src.length); - } - - /** - * Create a new ArrayBuffer with the given ByteBuffer as the backing store. The ByteBuffer must - * be created as a DirectBuffer. - * - * @param byteBuffer The ByteBuffer to back this ArrayBuffer. - */ - public ArrayBuffer(final ByteBuffer byteBuffer) { - this.byteBuffer = validateByteBuffer(byteBuffer); - } - - private ByteBuffer validateByteBuffer(final ByteBuffer byteBuffer) { - if (!byteBuffer.isDirect()) { - throw new IllegalArgumentException("ByteBuffer must be a allocated as a direct ByteBuffer"); - } - return byteBuffer; - } - - /** - * Returns the ByteBuffer backing this ArrayBuffer. - * - * @return The ByteBuffer backing this ArrayBuffer. - */ - public ByteBuffer getByteBuffer() { - return byteBuffer; - } - - /** - * Returns the byte at a given index. - * - * @param index The index at which to return the byte. - * @return The byte at the given index. - */ - public byte getByte(final int index) { - return byteBuffer.get(index); - } - - /** - * Returns the byte at a given index as an unsigned integer. - * - * @param index The index at which to return the byte. - * @return The unsigned byte at the given index. - */ - public short getUnsignedByte(final int index) { - return (short) (0xFF & byteBuffer.get(index)); - } - - /** - * Puts a byte into the ByteBuffer at the given index. - * - * @param index The index at which to put the byte. - * @param value The value to put at the index. - */ - public void put(final int index, final byte value) { - byteBuffer.put(index, value); - } - - /** - * Returns this ArrayBuffers limit. - * - * @return This ArrayBuffers limit. - */ - public int limit() { - return byteBuffer.limit(); - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Float32Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Float32Array.java deleted file mode 100644 index a5ab991c5..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Float32Array.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Float32Array typed array represents an array of 32-bit floating - * point numbers. - */ -public class Float32Array extends TypedArray { - - /** - * Creates a Float32Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public Float32Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a Float32Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public Float32Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the floating point (Float32) value at a given index. - * - * @param index The index at which to return the value. - * @return The Float32 value at the given index. - */ - public float get(final int index) { - return buffer.asFloatBuffer().get(index); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.asFloatBuffer().limit(); - } - - /** - * Puts a Float32 value at a particular index. - * - * @param index The index at which to place the value. - * @param value The Float32 value to place into buffer. - */ - public void put(final int index, final float value) { - buffer.asFloatBuffer().put(index, value); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.FLOAT_32_ARRAY; - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Float64Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Float64Array.java deleted file mode 100644 index af7c67099..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Float64Array.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Float64Array typed array represents an array of 64-bit floating - * point numbers. - */ -public class Float64Array extends TypedArray { - - /** - * Creates a Float64Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public Float64Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a Float64Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public Float64Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the floating point (Float64) value at a given index. - * - * @param index The index at which to return the value. - * @return The Double (Float64) value at the given index. - */ - public double get(final int index) { - return buffer.asDoubleBuffer().get(index); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.asDoubleBuffer().limit(); - } - - /** - * Puts a Double (Float64) value at a particular index. - * - * @param index The index at which to place the value. - * @param value The Double to put into the buffer. - */ - public void put(final int index, final double value) { - buffer.asDoubleBuffer().put(index, value); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.FLOAT_64_ARRAY; - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int16Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int16Array.java deleted file mode 100644 index ae365be59..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int16Array.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Int16Array typed array represents an array of twos-complement - * 16-bit signed integers in the platform byte order. - */ -public class Int16Array extends TypedArray { - - /** - * Creates an Int16Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public Int16Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a Int16Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public Int16Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the 16-bit signed integer at the given index - * - * @param index The index at which to return the value. - * @return The 16-bit integer at the index. - */ - public short get(final int index) { - return buffer.asShortBuffer().get(index); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.asShortBuffer().limit(); - } - - /** - * Puts a 16-bit integer at a particular index. - * - * @param index The index at which to place the value. - * @param value The 16-bit integer to put into buffer. - */ - public void put(final int index, final short value) { - buffer.asShortBuffer().put(index, value); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.INT_16_ARRAY; - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int32Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int32Array.java deleted file mode 100644 index 4e69e7316..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int32Array.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Int32Array typed array represents an array of twos-complement 32-bit signed - * integers in the platform byte order. - */ -public class Int32Array extends TypedArray { - - /** - * Creates an Int32Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public Int32Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a Int32Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public Int32Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the 32-bit signed integer at the given index. - * - * @param index The index at which to return the value. - * @return The 32-bit integer at the index. - */ - public int get(final int index) { - return buffer.asIntBuffer().get(index); - } - - @Override - public int length() { - return buffer.asIntBuffer().limit(); - } - - /** - * Puts a 32-bit integer at a particular index. - * - * @param index The index at which to place the value. - * @param value The 32-bit integer to put into buffer. - */ - public void put(final int index, final int value) { - buffer.asIntBuffer().put(index, value); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.INT_32_ARRAY; - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int8Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int8Array.java deleted file mode 100644 index f75b61fd0..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/Int8Array.java +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Int8Array typed array represents an array of twos-complement - * 8-bit signed integers. - */ -public class Int8Array extends TypedArray { - - /** - * Creates an Int8Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public Int8Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a Int8Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public Int8Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the 8-bit signed integer at the given index. - * - * @param index The index at which to return the value. - * @return The 8-bit integer at the index. - */ - public byte get(final int index) { - return buffer.get(index); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.limit(); - } - - /** - * Puts an 8-bit integer at a particular index. - * - * @param index The index at which to place the value. - * @param value The 8-bit integer to put into buffer. - */ - public void put(final int index, final byte value) { - buffer.put(index, value); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.INT_8_ARRAY; - } -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/TypedArray.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/TypedArray.java deleted file mode 100644 index 567b87790..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/TypedArray.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8TypedArray; - -/** - * An abstract class that represents TypedArrays - */ -public abstract class TypedArray { - - protected ByteBuffer buffer; - - protected TypedArray(final ByteBuffer buffer) { - if (!buffer.isDirect()) { - throw new IllegalArgumentException("ByteBuffer must be a allocated as a direct ByteBuffer"); - } - if ((buffer.limit() % V8TypedArray.getStructureSize(getType())) != 0) { - throw new IllegalArgumentException("ByteBuffer must be a allocated as a direct ByteBuffer"); - } - this.buffer = buffer; - } - - /** - * Return the underlying ByteBuffer. - * - * @return The underlying ByteBuffer behind this view - */ - public ByteBuffer getByteBuffer() { - return buffer; - } - - /** - * Return the size of this view. The size of the view is determined by the size - * of the buffer, and the size of the data projected onto it. For example, for a - * buffer size of 8, and a view representing 16bit integers, the size would be 4. - * - * @return The size of this view - */ - public abstract int length(); - - /** - * Returns the 'Type' of this TypedArray using one of the constants defined in V8Value. - * - * @return The 'Type' of this typed array. - */ - public abstract int getType(); -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt16Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt16Array.java deleted file mode 100644 index cd62d2c10..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt16Array.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Uint16Array typed array represents an array of 16-bit unsigned - * integers in the platform byte order. - */ -public class UInt16Array extends TypedArray { - - /** - * Creates an UInt16Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public UInt16Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a UInt16Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public UInt16Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the 16-bit unsigned integer at the given index. - * - * @param index The index at which to return the value. - * @return The 16-bit unsigned integer at the index. - */ - public int get(final int index) { - return 0xFFFF & buffer.asShortBuffer().get(index); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.asShortBuffer().limit(); - } - - /** - * Puts a 16-bit unsigned integer at a particular index. - * - * @param index The index at which to place the value. - * @param value The 16-bit unsigned integer to put into buffer. - */ - public void put(final int index, final int value) { - buffer.asShortBuffer().put(index, (short) (0x0000FFFF & value)); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.UNSIGNED_INT_16_ARRAY; - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt32Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt32Array.java deleted file mode 100644 index fd307e213..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt32Array.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Uint32Array typed array represents an array of 32-bit unsigned - * integers in the platform byte order. - */ -public class UInt32Array extends TypedArray { - - /** - * Creates an UInt32Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public UInt32Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a UInt32Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public UInt32Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the 32-bit unsigned integer at the given index. - * - * @param index The index at which to return the value. - * @return The 32-bit unsigned integer at the index. - */ - public long get(final int index) { - return 0x00000000FFFFFFFF & buffer.asIntBuffer().get(index); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.asIntBuffer().limit(); - } - - /** - * Puts a 32-bit unsigned integer at a particular index. - * - * @param index The index at which to place the value. - * @param value The 32-bit unsigned integer to put into buffer. - */ - public void put(final int index, final long value) { - buffer.asIntBuffer().put(index, (int) (0x00000000FFFFFFFF & value)); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.UNSIGNED_INT_32_ARRAY; - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt8Array.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt8Array.java deleted file mode 100644 index 86e14ca5f..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt8Array.java +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Uint8Array typed array represents an array of 8-bit unsigned integers - */ -public class UInt8Array extends TypedArray { - - /** - * Creates an UInt8Array projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public UInt8Array(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a UInt8Array projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public UInt8Array(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the 8-bit unsigned integer at the given index. - * - * @param index The index at which to return the value. - * @return The 8-bit unsigned integer at the index. - */ - public short get(final int index) { - return (short) (0xFF & buffer.get(index)); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.limit(); - } - - /** - * Puts a 8-bit unsigned integer at a particular index. - * - * @param index The index at which to place the value. - * @param value The 8-bit unsigned integer to put into buffer. - */ - public void put(final int index, final short value) { - buffer.put(index, (byte) (0x00FF & value)); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.UNSIGNED_INT_8_ARRAY; - } - -} diff --git a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt8ClampedArray.java b/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt8ClampedArray.java deleted file mode 100644 index 7327ee676..000000000 --- a/fine-j2v8/src/com/eclipsesource/v8/utils/typedarrays/UInt8ClampedArray.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 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.typedarrays; - -import java.nio.ByteBuffer; - -import com.eclipsesource.v8.V8Value; - -/** - * The Uint8ClampedArray typed array represents an array of 8-bit unsigned - * integers clamped to 0-255; if you specified a value that is out of the - * range of [0,255], 0 or 255 will be set instead. - */ -public class UInt8ClampedArray extends TypedArray { - - /** - * Creates an UInt8ClampedArray projected onto the given ByteBuffer. - * - * @param buffer The ByteBuffer on which the array is projected on. - */ - public UInt8ClampedArray(final ByteBuffer buffer) { - super(buffer); - } - - /** - * Creates a UInt8ClampedArray projected onto the given ArrayBuffer. - * - * @param arrayBuffer The ArrayBuffer on which the array is projected on. - */ - public UInt8ClampedArray(final ArrayBuffer arrayBuffer) { - this(arrayBuffer.getByteBuffer()); - } - - /** - * Returns the 8-bit unsigned integer at the given index. - * - * @param index The index at which to return the value. - * @return The 8-bit unsigned integer at the index. - */ - public short get(final int index) { - return (short) (0xFF & buffer.get(index)); - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#length() - */ - @Override - public int length() { - return buffer.limit(); - } - - /** - * Puts a 8-bit unsigned integer at a particular index. If the unsigned - * integer is outside the range [0,255], 0 or 255 will be used instead. - * - * @param index The index at which to place the value. - * @param value The 8-bit unsigned integer to put into buffer. - */ - public void put(final int index, final short value) { - if (value > 255) { - buffer.put(index, (byte) (255)); - } else if (value < 0) { - buffer.put(index, (byte) (0)); - } else { - buffer.put(index, (byte) (value)); - } - } - - /* - * (non-Javadoc) - * @see com.eclipsesource.v8.utils.typedarrays.TypedArray#getType() - */ - @Override - public int getType() { - return V8Value.UNSIGNED_INT_8_CLAMPED_ARRAY; - } - -} diff --git a/fine-j2v8/src/libj2v8_linux_x86_64.so b/fine-j2v8/src/libj2v8-linux-x86_64.so old mode 100644 new mode 100755 similarity index 66% rename from fine-j2v8/src/libj2v8_linux_x86_64.so rename to fine-j2v8/src/libj2v8-linux-x86_64.so index c2b38db5f..8c7369c11 Binary files a/fine-j2v8/src/libj2v8_linux_x86_64.so and b/fine-j2v8/src/libj2v8-linux-x86_64.so differ diff --git a/fine-j2v8/src/libj2v8_macosx_x86_64.dylib b/fine-j2v8/src/libj2v8-macosx-x86_64.dylib similarity index 61% rename from fine-j2v8/src/libj2v8_macosx_x86_64.dylib rename to fine-j2v8/src/libj2v8-macosx-x86_64.dylib index bd06c09ca..1271ae1fd 100755 Binary files a/fine-j2v8/src/libj2v8_macosx_x86_64.dylib and b/fine-j2v8/src/libj2v8-macosx-x86_64.dylib differ diff --git a/fine-j2v8/src/libj2v8_win32_x86_64.dll b/fine-j2v8/src/libj2v8-windows-x86_64.dll old mode 100644 new mode 100755 similarity index 54% rename from fine-j2v8/src/libj2v8_win32_x86_64.dll rename to fine-j2v8/src/libj2v8-windows-x86_64.dll index ab4e79060..16dc9feb3 Binary files a/fine-j2v8/src/libj2v8_win32_x86_64.dll and b/fine-j2v8/src/libj2v8-windows-x86_64.dll differ