diff --git a/base-third-project/base-third-step1/pom.xml b/base-third-project/base-third-step1/pom.xml index 30ed62fbe..c2101b60d 100644 --- a/base-third-project/base-third-step1/pom.xml +++ b/base-third-project/base-third-step1/pom.xml @@ -26,6 +26,7 @@ ../../fine-iconloader ../../fine-icu4j ../../fine-imageJ + ../../fine-j2v8 ../../fine-jboss-transaction-api ../../fine-jgit diff --git a/build.third_step1-jdk11.gradle b/build.third_step1-jdk11.gradle index f732d6699..3de001b14 100644 --- a/build.third_step1-jdk11.gradle +++ b/build.third_step1-jdk11.gradle @@ -43,6 +43,7 @@ sourceSets{ "${srcDir}/fine-hsqldb/src/main/java", "${srcDir}/fine-iconloader/src/main/java", "${srcDir}/fine-icu4j/src/main/java", + "${srcDir}/fine-j2v8/src/main/java", // "${srcDir}/fine-jai/src/main/java", "${srcDir}/fine-jboss-transaction-api/src/main/java", "${srcDir}/fine-jgit/src/main/java", @@ -95,6 +96,8 @@ def resourceDirs = [ "${srcDir}/fine-iconloader/src/main/resources", "${srcDir}/fine-icu4j/src/main/java", "${srcDir}/fine-icu4j/src/main/resources", + "${srcDir}/fine-j2v8/src/main/java", + "${srcDir}/fine-j2v8/src/main/resources", // "${srcDir}/fine-jai/src/main/java", "${srcDir}/fine-jboss-transaction-api/src/main/java", "${srcDir}/fine-jboss-transaction-api/src/main/resources", diff --git a/build.third_step1.gradle b/build.third_step1.gradle index 12481b146..403fa3c01 100644 --- a/build.third_step1.gradle +++ b/build.third_step1.gradle @@ -44,6 +44,7 @@ sourceSets{ "${srcDir}/fine-hsqldb/src/main/java", "${srcDir}/fine-iconloader/src/main/java", "${srcDir}/fine-icu4j/src/main/java", + "${srcDir}/fine-j2v8/src/main/java", // "${srcDir}/fine-jai/src/main/java", "${srcDir}/fine-jboss-transaction-api/src/main/java", "${srcDir}/fine-jgit/src/main/java", @@ -144,6 +145,8 @@ task copyFiles(type:Copy,dependsOn:'compileJava'){ with dataContent.call("${srcDir}/fine-hsqldb/src/main/resources") with dataContent.call("${srcDir}/fine-icu4j/src/main/java") with dataContent.call("${srcDir}/fine-icu4j/src/main/resources") + with dataContent.call("${srcDir}/fine-j2v8/src/main/java") + with dataContent.call("${srcDir}/fine-j2v8/src/main/resources") // with dataContent.call("${srcDir}/fine-jai/src/main/java") with dataContent.call("${srcDir}/fine-jboss-transaction-api/src/main/java") with dataContent.call("${srcDir}/fine-jboss-transaction-api/src/main/resources") diff --git a/fine-j2v8/pom.xml b/fine-j2v8/pom.xml new file mode 100644 index 000000000..72cbb4efa --- /dev/null +++ b/fine-j2v8/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + + com.fr.third + step1 + ${revision} + ../base-third-project/base-third-step1 + + + fine-j2v8 + ${revision} + + + \ No newline at end of file diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/JavaCallback.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/JavaCallback.java new file mode 100644 index 000000000..2bf5a19b3 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/JavaCallback.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * Classes that implement this interface provide a method + * which can be invoked from JavaScript. The method can return + * a result. + * + * After creating an instance of a class that implements this + * interface it can be registered as a Callback on a V8Object. + */ +public interface JavaCallback { + + /** + * Called when a JS Function invokes a the registered Java + * method. + * + * @param receiver The V8Object that the function was called on. + * @param parameters The parameters passed to the JS Function. The + * parameter array does not need to be released, by any objects accessed + * from the array must be. + * + * @return A result that should be passed back to JavaScript. The + * result must be either an Integer, Double, Boolean, String or V8Value. + */ + public Object invoke(V8Object receiver, V8Array parameters); + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/JavaVoidCallback.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/JavaVoidCallback.java new file mode 100644 index 000000000..7298d5a40 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/JavaVoidCallback.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * Classes that implement this interface provide a method + * which can be invoked from JavaScript. + * + * After creating an instance of a class that implements this + * interface it can be registered as a Callback on a V8Object. + */ +public interface JavaVoidCallback { + + /** + * Called when a JS Function invokes a the registered Java + * method. + * + * @param receiver The V8Object that the function was called on. + * @param parameters The parameters passed to the JS Function. The + * parameter array does not need to be released, by any objects accessed + * from the array must be. + */ + public void invoke(V8Object receiver, V8Array parameters); + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/LibraryLoader.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/LibraryLoader.java new file mode 100644 index 000000000..741b8bd3a --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/LibraryLoader.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2015 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 + * Wolfgang Steiner - code separation PlatformDetector/LibraryLoader + ******************************************************************************/ +package com.eclipsesource.v8; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class LibraryLoader { + + static final String SEPARATOR; + static final String DELIMITER; + + static final String SWT_LIB_DIR = ".j2v8"; + + static { + DELIMITER = System.getProperty("line.separator"); //$NON-NLS-1$ + SEPARATOR = System.getProperty("file.separator"); //$NON-NLS-1$ + } + + /** + * 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; + } + + public static String computeLibraryFullName(boolean withLinuxVendor) { + return "lib" + computeLibraryShortName(withLinuxVendor) + "." + PlatformDetector.OS.getLibFileExtension(); + } + + 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 true; + } + + /* Try loading library from the IDE location */ + if (new File(ideLocation).exists()) { + if (load(ideLocation, message)) { + 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("java.io.tmpdir"); //$NON-NLS-1$ + } + + // 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$ + } + + static boolean load(final String libName, final StringBuffer message) { + try { + if (libName.indexOf(SEPARATOR) != -1) { + System.load(libName); + } else { + System.loadLibrary(libName); + } + return true; + } catch (UnsatisfiedLinkError e) { + if (message.length() == 0) { + message.append(DELIMITER); + } + message.append('\t'); + message.append(e.getMessage()); + message.append(DELIMITER); + } + 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); + boolean extracted = false; + try { + if (file.exists()) { + file.delete(); + } + is = LibraryLoader.class.getResourceAsStream("/" + mappedName); //$NON-NLS-1$ + if (is != null) { + extracted = true; + int read; + byte[] buffer = new byte[4096]; + os = new FileOutputStream(fileName); + while ((read = is.read(buffer)) != -1) { + os.write(buffer, 0, read); + } + os.close(); + is.close(); + chmod("755", fileName); + if (load(fileName, message)) { + return true; + } + } + } catch (Throwable e) { + try { + if (os != null) { + os.close(); + } + } catch (IOException e1) { + } + try { + if (is != null) { + is.close(); + } + } catch (IOException e1) { + } + if (extracted && file.exists()) { + file.delete(); + } + } + return false; + } + + static void chmod(final String permision, final String path) { + if (PlatformDetector.OS.isWindows()) { + return; + } + try { + Runtime.getRuntime().exec(new String[] { "chmod", permision, path }).waitFor(); //$NON-NLS-1$ + } catch (Throwable e) { + } + } +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/Platform.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/Platform.java new file mode 100644 index 000000000..8168935bc --- /dev/null +++ b/fine-j2v8/src/main/java/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/main/java/com/eclipsesource/v8/PlatformDetector.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/PlatformDetector.java new file mode 100644 index 000000000..0913c1a69 --- /dev/null +++ b/fine-j2v8/src/main/java/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)$") || value.startsWith("armv7")) { + return "arm_32"; + } + if ("aarch64".equals(value) || value.startsWith("armv8")) { + 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]+", ""); + } +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/ReferenceHandler.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/ReferenceHandler.java new file mode 100644 index 000000000..740dc18ee --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/ReferenceHandler.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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; + +/** + * Callback used to track when native handles are created and released. + */ +public interface ReferenceHandler { + + /** + * Called when a native handle is first created. The V8Value + * referenced by that handle is passed as a parameter. + * + * @param object The V8Value referenced by the handle + */ + public void v8HandleCreated(V8Value object); + + /** + * Called when a native handle is released. The V8Value + * referenced by that handle is passed as a parameter. + * + * @param object The V8Value referenced by the handle + */ + public void v8HandleDisposed(V8Value object); + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/Releasable.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/Releasable.java new file mode 100644 index 000000000..3edee1e61 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/Releasable.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +import java.io.Closeable; + +/** + * An interface used to denote all V8 Classes which can be released. + */ +public interface Releasable extends Closeable { + + /** + * Release the underlying resources. Once an object is released + * it typically cannot be used again. + */ + void close(); + + /** + * Synonym for {@link #close()}. + */ + void release(); +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/SignatureProvider.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/SignatureProvider.java new file mode 100644 index 000000000..035c5b06b --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/SignatureProvider.java @@ -0,0 +1,5 @@ +package com.eclipsesource.v8; + +public interface SignatureProvider { + public byte[] getSignature(String uri); +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8.java new file mode 100644 index 000000000..abaec598c --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8.java @@ -0,0 +1,1666 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +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 + * on a single runtime, and data is not shared between runtimes. + * A runtime must be created and released when finished. + *

+ * All access to a runtime must come from the same thread, unless + * the thread explicitly gives up control using the V8Locker. + *

+ * A public static factory method can be used to create the runtime. + *

+ * V8 runtime = V8.createV8Runtime(); + */ +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; + protected Map v8WeakReferences = new HashMap(); + + private Map data = null; + private final V8Locker locker; + private SignatureProvider signatureProvider = null; + private long objectReferences = 0; + private long v8RuntimePtr = 0; + private List resources = null; + private V8Map executors = null; + 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 class MethodDescriptor { + Object object; + Method method; + JavaCallback callback; + JavaVoidCallback voidCallback; + boolean includeReceiver; + } + + private synchronized static void load(final String tmpDirectory) { + try { + LibraryLoader.loadLibrary(tmpDirectory); + nativeLibraryLoaded = true; + } catch (Error e) { + nativeLoadError = e; + } catch (Exception e) { + nativeLoadException = e; + } + } + + /** + * Determines if the native libraries are loaded. + * + * @return Returns true if the native libraries are loaded, + * false otherwise. + */ + public static boolean isLoaded() { + return nativeLibraryLoaded; + } + + /** + * Sets the V8 flags on the platform. All runtimes will be created + * with the same flags. Flags must be set before the runtime is + * created. + * + * @param flags The flags to set on V8 + */ + public static void setFlags(final String flags) { + v8Flags = flags; + initialized = false; + } + + /** + * Creates a new V8Runtime and loads the required + * native libraries if they are not already loaded. + * The current thread is given the lock to this runtime. + * + * @return A new isolated V8 Runtime. + */ + public static V8 createV8Runtime() { + return createV8Runtime(null, null); + } + + /** + * Creates a new V8Runtime and loads the required native libraries if they + * are not already loaded. An alias is also set for the global scope. For example, + * 'window' can be set as the global scope name. + *

+ * The current thread is given the lock to this runtime. + * + * @param globalAlias The name to associate with the global scope. + * @return A new isolated V8 Runtime. + */ + public static V8 createV8Runtime(final String globalAlias) { + return createV8Runtime(globalAlias, null); + } + + /** + * Creates a new V8Runtime and loads the required native libraries if they + * are not already loaded. An alias is also set for the global scope. For example, + * 'window' can be set as the global scope name. + *

+ * The current thread is given the lock to this runtime. + * + * @param globalAlias The name to associate with the global scope. + * @param tempDirectory The name of the directory to extract the native + * libraries too. + * @return A new isolated V8 Runtime. + */ + public static V8 createV8Runtime(final String globalAlias, final String tempDirectory) { + if (!nativeLibraryLoaded) { + synchronized (lock) { + if (!nativeLibraryLoaded) { + load(tempDirectory); + } + } + } + checkNativeLibraryLoaded(); + if (!initialized) { + _setFlags(v8Flags); + initialized = true; + } + V8 runtime = new V8(globalAlias); + synchronized (lock) { + runtimeCounter++; + } + return runtime; + } + + public void setSignatureProvider(final SignatureProvider signatureProvider) { + this.signatureProvider = signatureProvider; + } + + /** + * Adds a ReferenceHandler to track when new V8Objects are created. + * + * @param handler The ReferenceHandler to add + */ + public void addReferenceHandler(final ReferenceHandler handler) { + 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. + * + * @param handler The reference handler to remove + */ + public void removeReferenceHandler(final ReferenceHandler handler) { + 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); + } + } + + private void notifyReferenceDisposed(final V8Value object) { + for (ReferenceHandler referenceHandler : referenceHandlers) { + referenceHandler.v8HandleDisposed(object); + } + } + + 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(message, nativeLoadError); + } else if (nativeLoadException != null) { + throw new IllegalStateException(message, nativeLoadException); + } else { + throw new IllegalStateException(message); + } + } + } + + protected V8() { + this(null); + } + + protected V8(final String globalAlias) { + super(null); + released = false; + v8RuntimePtr = _createIsolate(globalAlias); + locker = new V8Locker(this); + checkThread(); + objectHandle = _getGlobalObject(v8RuntimePtr); + } + +// public void dispatchProtocolMessage(final long V8InspectorPtr, final String protocolMessage) { +// checkThread(); +// _dispatchProtocolMessage(v8RuntimePtr, V8InspectorPtr, protocolMessage); +// } + +// public void schedulePauseOnNextStatement(final long V8InspectorPtr, final String reason) { +// checkThread(); +// _schedulePauseOnNextStatement(v8RuntimePtr, V8InspectorPtr, reason); +// } + + /** + * Returns an UNDEFINED constant. + * + * @return The UNDEFINED constant value. + */ + public static V8Value getUndefined() { + return undefined; + } + + /** + * Returns the number of active runtimes. + * + * @return The number of active runtimes. + */ + public static int getActiveRuntimes() { + return runtimeCounter; + } + + /** + * Returns the number of Object References for this runtime. + * + * @return The number of Object References on this runtime. + */ + public long getObjectReferenceCount() { + return objectReferences - v8WeakReferences.size(); + } + + public long getV8RuntimePtr() { + return v8RuntimePtr; + } + + /** + * Gets the version of the V8 engine + * + * @return The version of the V8 Engine. + */ + public static String getV8Version() { + 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); + } + + /** + * Terminates any JavaScript executing on this runtime. Once + * the runtime is released, any executors that were spawned + * will also be force terminated. + */ + public void terminateExecution() { + forceTerminateExecutors = true; + terminateExecution(v8RuntimePtr); + } + + /** + * Release native resources associated with this runtime. Once + * released, a runtime cannot be reused. + * + * @param reportMemoryLeaks True if memory leaks should be + * reported by throwing an IllegalStateException if any + * objects were not released. + */ + public void release(final boolean reportMemoryLeaks) { + if (isReleased()) { + return; + } + checkThread(); + 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"); + } + } + } + + private void releaseNativeMethodDescriptors() { + Set nativeMethodDescriptors = functionRegistry.keySet(); + for (Long nativeMethodDescriptor : nativeMethodDescriptors) { + releaseMethodDescriptor(v8RuntimePtr, nativeMethodDescriptor); + } + } + + private void releaseResources() { + if (resources != null) { + for (Releasable releasable : resources) { + releasable.release(); + } + resources.clear(); + resources = null; + } + } + + /** + * Registers an executor with this runtime. An executor is another + * runtime with its own thread. By registering an executor, it can be + * terminated when this runtime is released. + * + * @param key The key to associate the executor with. + * @param executor The executor itself. + */ + public void registerV8Executor(final V8Object key, final V8Executor executor) { + checkThread(); + if (executors == null) { + executors = new V8Map(); + } + executors.put(key, executor); + } + + /** + * Removes the executor from this runtime. The executor is + * *NOT* shutdown, simply removed from the list of known + * executors. + * + * @param key The key the executor was associated with. + * @return The executor or null if it does not exist. + */ + public V8Executor removeExecutor(final V8Object key) { + checkThread(); + if (executors == null) { + return null; + } + return executors.remove(key); + } + + /** + * Returns the executor associated with the given key. + * + * @param key The key the executor was associated with. + * @return The executor or null if it does not exist. + */ + public V8Executor getExecutor(final V8Object key) { + checkThread(); + if (executors == null) { + return null; + } + return executors.get(key); + } + + /** + * Shutdown all executors associated with this runtime. + * If force terminate is specified, it will forcefully terminate + * the executors, otherwise it will simply signal that they + * should terminate. + * + * @param forceTerminate Specify if the executors should be + * forcefully terminated, or simply notified to shutdown when ready. + */ + public void shutdownExecutors(final boolean forceTerminate) { + checkThread(); + if (executors == null) { + return; + } + for (V8Executor executor : executors.values()) { + if (forceTerminate) { + executor.forceTermination(); + } else { + executor.shutdown(); + } + } + } + + /** + * Registers a resource with this runtime. All registered + * resources will be released before the runtime is released. + * + * @param resource The resource to register. + */ + public void registerResource(final Releasable resource) { + checkThread(); + if (resources == null) { + resources = new ArrayList(); + } + resources.add(resource); + } + + /** + * Executes a JS Script on this runtime and returns the result as an integer. + * If the result is not an integer, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @return The result of the script as an integer, or V8ResultUndefinedException if + * the result is not an integer. + */ + public int executeIntegerScript(final String script) { + return executeIntegerScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as an integer. + * If the result is not an integer, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @param scriptName The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for excepton purposes. + * @return The result of the script as an integer, or V8ResultUndefinedException if + * the result is not an integer. + */ + public int executeIntegerScript(final String script, final String scriptName, final int lineNumber) { + checkThread(); + checkScript(script); + return executeIntegerScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + protected void createTwin(final V8Value value, final V8Value twin) { + checkThread(); + createTwin(v8RuntimePtr, value.getHandle(), twin.getHandle()); + } + + /** + * Executes a JS Script on this runtime and returns the result as a double. + * If the result is not a double, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @return The result of the script as a double, or V8ResultUndefinedException if + * the result is not a double. + */ + public double executeDoubleScript(final String script) { + return executeDoubleScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a double. + * If the result is not a double, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @param scriptName The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for exception stack trace purposes. + * @return The result of the script as a double, or V8ResultUndefinedException if + * the result is not a double. + */ + public double executeDoubleScript(final String script, final String scriptName, final int lineNumber) { + checkThread(); + checkScript(script); + return executeDoubleScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + /** + * Executes a JS Script on this runtime and returns the result as a String. + * If the result is not a String, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @return The result of the script as a String, or V8ResultUndefinedException if + * the result is not a String. + */ + public String executeStringScript(final String script) { + return executeStringScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a String. + * If the result is not a String, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @param scriptName The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for exception stack trace purposes. + * @return The result of the script as a String, or V8ResultUndefinedException if + * the result is not a String. + */ + public String executeStringScript(final String script, final String scriptName, final int lineNumber) { + checkThread(); + checkScript(script); + return executeStringScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + /** + * Executes a JS Script on this runtime and returns the result as a boolean. + * If the result is not a boolean, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @return The result of the script as a boolean, or V8ResultUndefinedException if + * the result is not a boolean. + */ + public boolean executeBooleanScript(final String script) { + return executeBooleanScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a boolean. + * If the result is not a boolean, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @param scriptName The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for exception stack trace purposes. + * @return The result of the script as a boolean, or V8ResultUndefinedException if + * the result is not a boolean. + */ + public boolean executeBooleanScript(final String script, final String scriptName, final int lineNumber) { + checkThread(); + checkScript(script); + return executeBooleanScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + /** + * Executes a JS Script on this runtime and returns the result as a V8Array. + * If the result is not a V8Array, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @return The result of the script as a V8Array, or V8ResultUndefinedException if + * the result is not a V8Array. + */ + public V8Array executeArrayScript(final String script) { + return executeArrayScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a V8Array. + * If the result is not a V8Array, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @param scriptName The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for exception stack trace purposes. + * @return The result of the script as a V8Array, or V8ResultUndefinedException if + * the result is not a V8Array. + */ + public V8Array executeArrayScript(final String script, final String scriptName, final int lineNumber) { + checkThread(); + Object result = this.executeScript(script, scriptName, lineNumber); + if (result instanceof V8Array) { + return (V8Array) result; + } + throw new V8ResultUndefined(); + } + + /** + * Executes a JS Script on this runtime and returns the result as a Java Object. + * Primitives will be boxed. + * + * @param script The script to execute. + * @return The result of the script as a Java Object. + */ + public Object executeScript(final String script) { + return executeScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a Java Object. + * Primitives will be boxed. + * + * @param script The script to execute. + * @param uri The name of the script + * @return The result of the script as a Java Object. + */ + public Object executeScript(final String script, final String uri) { + checkThread(); + checkScript(script); + return executeScript(getV8RuntimePtr(), UNKNOWN, script, uri, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a Java Object. + * Primitives will be boxed. + * + * @param script The script to execute. + * @param uri The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for exception stack trace purposes. + * @return The result of the script as a Java Object. + */ + public Object executeScript(final String script, final String uri, final int lineNumber) { + checkThread(); + checkScript(script); + return executeScript(getV8RuntimePtr(), UNKNOWN, script, uri, lineNumber); + } + + /** + * Executes a JS Script module on this runtime and returns the result as a Java Object. + * Primitives will be boxed. + *

+ * If the script does not match the signature (as verified with the public key) then a + * V8SecurityException will be thrown. + * + * @param script The signed script to execute + * @param modulePrefix The module prefix + * @param modulePostfix The module postfix + * @param uri The name of the script + * @return The result of the script as a Java Object. + */ + public Object executeModule(final String script, final String modulePrefix, final String modulePostfix, final String uri) { + checkThread(); + checkScript(script); + return executeScript(getV8RuntimePtr(), UNKNOWN, modulePrefix + script + modulePostfix, uri, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a V8Object. + * If the result is not a V8Object, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @return The result of the script as a V8Object, or V8ResultUndefinedException if + * the result is not a V8Object. + */ + public V8Object executeObjectScript(final String script) { + return this.executeObjectScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime and returns the result as a V8Object. + * If the result is not a V8Object, then a V8ResultUndefinedException is thrown. + * + * @param script The script to execute. + * @param scriptName The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for exception stack trace purposes. + * @return The result of the script as a V8Object, or V8ResultUndefinedException if + * the result is not a V8Object. + */ + public V8Object executeObjectScript(final String script, final String scriptName, final int lineNumber) { + checkThread(); + Object result = this.executeScript(script, scriptName, lineNumber); + if (result instanceof V8Object) { + return (V8Object) result; + } + throw new V8ResultUndefined(); + } + + /** + * Executes a JS Script on this runtime. + * + * @param script The script to execute. + */ + public void executeVoidScript(final String script) { + executeVoidScript(script, null, 0); + } + + /** + * Executes a JS Script on this runtime. + * + * @param script The script to execute. + * @param scriptName The name of the script + * @param lineNumber The line number that is considered to be the first line of + * the script. Typically 0, but could be set to another value for exception stack trace purposes. + */ + public void executeVoidScript(final String script, final String scriptName, final int lineNumber) { + checkThread(); + checkScript(script); + executeVoidScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + /** + * Returns the locker associated with this runtime. The locker allows + * threads to give up control of the runtime and other threads to acquire + * control. + * + * @return The locker associated with this runtime. + */ + public V8Locker getLocker() { + return locker; + } + + /** + * Returns the unique build ID of the native library. + * + * @return The unique build ID of the Native library. + */ + public static long getBuildID() { + 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()) { + throw new Error("Runtime disposed error"); + } + } + + static void checkScript(final String script) { + if (script == null) { + throw new NullPointerException("Script is null"); + } + } + + void registerCallback(final Object object, final Method method, final long objectHandle, final String jsFunctionName, final boolean includeReceiver) { + MethodDescriptor methodDescriptor = new MethodDescriptor(); + methodDescriptor.object = object; + methodDescriptor.method = method; + methodDescriptor.includeReceiver = includeReceiver; + long methodID = registerJavaMethod(getV8RuntimePtr(), objectHandle, jsFunctionName, isVoidMethod(method)); + functionRegistry.put(methodID, methodDescriptor); + } + + void registerVoidCallback(final JavaVoidCallback callback, final long objectHandle, final String jsFunctionName) { + MethodDescriptor methodDescriptor = new MethodDescriptor(); + methodDescriptor.voidCallback = callback; + long methodID = registerJavaMethod(getV8RuntimePtr(), objectHandle, jsFunctionName, true); + functionRegistry.put(methodID, methodDescriptor); + } + + void registerCallback(final JavaCallback callback, final long objectHandle, final String jsFunctionName) { + long methodID = registerJavaMethod(getV8RuntimePtr(), objectHandle, jsFunctionName, false); + createAndRegisterMethodDescriptor(callback, methodID); + } + + void createAndRegisterMethodDescriptor(final JavaCallback callback, final long methodID) { + MethodDescriptor methodDescriptor = new MethodDescriptor(); + methodDescriptor.callback = callback; + functionRegistry.put(methodID, methodDescriptor); + } + + private boolean isVoidMethod(final Method method) { + Class returnType = method.getReturnType(); + if (returnType.equals(Void.TYPE)) { + return true; + } + return false; + } + + private Object getDefaultValue(final Class type) { + if (type.equals(V8Object.class)) { + return new Undefined(); + } else if (type.equals(V8Array.class)) { + return new V8Array.Undefined(); + } + return invalid; + } + + protected void disposeMethodID(final long methodID) { + 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) { + return checkResult(methodDescriptor.callback.invoke(receiver, parameters)); + } + boolean hasVarArgs = methodDescriptor.method.isVarArgs(); + Object[] args = getArgs(receiver, methodDescriptor, parameters, hasVarArgs); + checkArgs(args); + try { + Object result = methodDescriptor.method.invoke(methodDescriptor.object, args); + return checkResult(result); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (IllegalAccessException e) { + throw e; + } catch (IllegalArgumentException e) { + throw e; + } finally { + releaseArguments(args, hasVarArgs); + } + } + + private Object checkResult(final Object result) { + if (result == null) { + return result; + } + if (result instanceof Float) { + return ((Float) result).doubleValue(); + } + if ((result instanceof Integer) || (result instanceof Double) || (result instanceof Boolean) + || (result instanceof String)) { + return result; + } + if (result instanceof V8Value) { + if (((V8Value) result).isReleased()) { + throw new V8RuntimeException("V8Value already released"); + } + return result; + } + throw new V8RuntimeException("Unknown return type: " + result.getClass()); + } + + protected void callVoidJavaMethod(final long methodID, final V8Object receiver, final V8Array parameters) throws Throwable { + MethodDescriptor methodDescriptor = functionRegistry.get(methodID); + if (methodDescriptor.voidCallback != null) { + methodDescriptor.voidCallback.invoke(receiver, parameters); + return; + } + boolean hasVarArgs = methodDescriptor.method.isVarArgs(); + Object[] args = getArgs(receiver, methodDescriptor, parameters, hasVarArgs); + checkArgs(args); + try { + methodDescriptor.method.invoke(methodDescriptor.object, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (IllegalAccessException e) { + throw e; + } catch (IllegalArgumentException e) { + throw e; + } finally { + releaseArguments(args, hasVarArgs); + } + } + + private void checkArgs(final Object[] args) { + for (Object argument : args) { + if (argument == invalid) { + throw new IllegalArgumentException("argument type mismatch"); + } + } + } + + private void releaseArguments(final Object[] args, final boolean hasVarArgs) { + if (hasVarArgs && ((args.length > 0) && (args[args.length - 1] instanceof Object[]))) { + Object[] varArgs = (Object[]) args[args.length - 1]; + for (Object object : varArgs) { + if (object instanceof V8Value) { + ((V8Value) object).close(); + } + } + } + for (Object arg : args) { + if (arg instanceof V8Value) { + ((V8Value) arg).close(); + } + } + } + + private Object[] getArgs(final V8Object receiver, final MethodDescriptor methodDescriptor, final V8Array parameters, final boolean hasVarArgs) { + int numberOfParameters = methodDescriptor.method.getParameterTypes().length; + int varArgIndex = hasVarArgs ? numberOfParameters - 1 : numberOfParameters; + Object[] args = setDefaultValues(new Object[numberOfParameters], methodDescriptor.method.getParameterTypes(), receiver, methodDescriptor.includeReceiver); + List varArgs = new ArrayList(); + populateParamters(parameters, varArgIndex, args, varArgs, methodDescriptor.includeReceiver); + if (hasVarArgs) { + Object varArgContainer = getVarArgContainer(methodDescriptor.method.getParameterTypes(), varArgs.size()); + System.arraycopy(varArgs.toArray(), 0, varArgContainer, 0, varArgs.size()); + args[varArgIndex] = varArgContainer; + } + return args; + } + + private Object getVarArgContainer(final Class[] parameterTypes, final int size) { + Class clazz = parameterTypes[parameterTypes.length - 1]; + if (clazz.isArray()) { + clazz = clazz.getComponentType(); + } + Object result = java.lang.reflect.Array.newInstance(clazz, size); + return result; + } + + private void populateParamters(final V8Array parameters, final int varArgIndex, final Object[] args, final List varArgs, final boolean includeReceiver) { + int start = 0; + if (includeReceiver) { + start = 1; + } + for (int i = start; i < (parameters.length() + start); i++) { + if (i >= varArgIndex) { + varArgs.add(getArrayItem(parameters, i - start)); + } else { + args[i] = getArrayItem(parameters, i - start); + } + } + } + + private Object[] setDefaultValues(final Object[] parameters, final Class[] parameterTypes, final V8Object receiver, final boolean includeReceiver) { + int start = 0; + if (includeReceiver) { + start = 1; + parameters[0] = receiver; + } + for (int i = start; i < parameters.length; i++) { + parameters[i] = getDefaultValue(parameterTypes[i]); + } + return parameters; + } + + private Object getArrayItem(final V8Array array, final int index) { + try { + int type = array.getType(index); + switch (type) { + case INTEGER: + return array.getInteger(index); + case DOUBLE: + return array.getDouble(index); + case BOOLEAN: + return array.getBoolean(index); + case STRING: + return array.getString(index); + case V8_ARRAY: + case V8_TYPED_ARRAY: + return array.getArray(index); + case V8_OBJECT: + return array.getObject(index); + case V8_FUNCTION: + return array.getObject(index); + case V8_ARRAY_BUFFER: + return array.get(index); + case UNDEFINED: + return V8.getUndefined(); + } + } catch (V8ResultUndefined e) { + // do nothing + } + return null; + } + +// void createNodeRuntime(final String fileName) { +// _startNodeJS(v8RuntimePtr, fileName); +// } + + boolean pumpMessageLoop() { + return _pumpMessageLoop(v8RuntimePtr); + } + + boolean isRunning() { + return _isRunning(v8RuntimePtr); + } + + protected long initNewV8Object(final long v8RuntimePtr) { + return _initNewV8Object(v8RuntimePtr); + } + + public 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); + } + + protected int executeIntegerScript(final long v8RuntimePtr, final String script, final String scriptName, final int lineNumber) { + return _executeIntegerScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + protected double executeDoubleScript(final long v8RuntimePtr, final String script, final String scriptName, final int lineNumber) { + return _executeDoubleScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + protected String executeStringScript(final long v8RuntimePtr, final String script, final String scriptName, final int lineNumber) { + return _executeStringScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + protected boolean executeBooleanScript(final long v8RuntimePtr, final String script, final String scriptName, final int lineNumber) { + return _executeBooleanScript(v8RuntimePtr, script, scriptName, lineNumber); + } + + protected Object executeScript(final long v8RuntimePtr, final int expectedType, final String script, final String scriptName, final int lineNumber) { + return _executeScript(v8RuntimePtr, expectedType, script, scriptName, lineNumber); + } + + protected void executeVoidScript(final long v8RuntimePtr, final String script, final String scriptName, final int lineNumber) { + _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); + } + + protected boolean contains(final long v8RuntimePtr, final long objectHandle, final String key) { + return _contains(v8RuntimePtr, objectHandle, key); + } + + protected String[] getKeys(final long v8RuntimePtr, final long objectHandle) { + return _getKeys(v8RuntimePtr, objectHandle); + } + + protected int getInteger(final long v8RuntimePtr, final long objectHandle, final String key) { + return _getInteger(v8RuntimePtr, objectHandle, key); + } + + protected boolean getBoolean(final long v8RuntimePtr, final long objectHandle, final String key) { + return _getBoolean(v8RuntimePtr, objectHandle, key); + } + + protected double getDouble(final long v8RuntimePtr, final long objectHandle, final String key) { + return _getDouble(v8RuntimePtr, objectHandle, key); + } + + protected String getString(final long v8RuntimePtr, final long objectHandle, final String key) { + return _getString(v8RuntimePtr, objectHandle, key); + } + + protected Object get(final long v8RuntimePtr, final int expectedType, final long objectHandle, final String key) { + return _get(v8RuntimePtr, expectedType, objectHandle, key); + } + + protected int executeIntegerFunction(final long v8RuntimePtr, final long objectHandle, final String name, final long parametersHandle) { + return _executeIntegerFunction(v8RuntimePtr, objectHandle, name, parametersHandle); + } + + protected double executeDoubleFunction(final long v8RuntimePtr, final long objectHandle, final String name, final long parametersHandle) { + return _executeDoubleFunction(v8RuntimePtr, objectHandle, name, parametersHandle); + } + + protected String executeStringFunction(final long v8RuntimePtr, final long handle, final String name, final long parametersHandle) { + return _executeStringFunction(v8RuntimePtr, handle, name, parametersHandle); + } + + protected boolean executeBooleanFunction(final long v8RuntimePtr, final long handle, final String name, final long parametersHandle) { + return _executeBooleanFunction(v8RuntimePtr, handle, name, parametersHandle); + } + + protected Object executeFunction(final long v8RuntimePtr, final int expectedType, final long objectHandle, final String name, final long parametersHandle) { + return _executeFunction(v8RuntimePtr, expectedType, objectHandle, name, parametersHandle); + } + + protected Object executeFunction(final long v8RuntimePtr, final long receiverHandle, final long functionHandle, final long parametersHandle) { + return _executeFunction(v8RuntimePtr, receiverHandle, functionHandle, parametersHandle); + } + + protected void executeVoidFunction(final long v8RuntimePtr, final long objectHandle, final String name, final long parametersHandle) { + _executeVoidFunction(v8RuntimePtr, objectHandle, name, parametersHandle); + } + + protected boolean equals(final long v8RuntimePtr, final long objectHandle, final long that) { + return _equals(v8RuntimePtr, objectHandle, that); + } + + protected String toString(final long v8RuntimePtr, final long objectHandle) { + return _toString(v8RuntimePtr, objectHandle); + } + + protected boolean strictEquals(final long v8RuntimePtr, final long objectHandle, final long that) { + return _strictEquals(v8RuntimePtr, objectHandle, that); + } + + protected boolean sameValue(final long v8RuntimePtr, final long objectHandle, final long that) { + return _sameValue(v8RuntimePtr, objectHandle, that); + } + + protected int identityHash(final long v8RuntimePtr, final long objectHandle) { + return _identityHash(v8RuntimePtr, objectHandle); + } + + protected void add(final long v8RuntimePtr, final long objectHandle, final String key, final int value) { + _add(v8RuntimePtr, objectHandle, key, value); + } + + protected void addObject(final long v8RuntimePtr, final long objectHandle, final String key, final long value) { + _addObject(v8RuntimePtr, objectHandle, key, value); + } + + protected void add(final long v8RuntimePtr, final long objectHandle, final String key, final boolean value) { + _add(v8RuntimePtr, objectHandle, key, value); + } + + protected void add(final long v8RuntimePtr, final long objectHandle, final String key, final double value) { + _add(v8RuntimePtr, objectHandle, key, value); + } + + protected void add(final long v8RuntimePtr, final long objectHandle, final String key, final String value) { + _add(v8RuntimePtr, objectHandle, key, value); + } + + protected void addUndefined(final long v8RuntimePtr, final long objectHandle, final String key) { + _addUndefined(v8RuntimePtr, objectHandle, key); + } + + protected void addNull(final long v8RuntimePtr, final long objectHandle, final String key) { + _addNull(v8RuntimePtr, objectHandle, key); + } + + protected long registerJavaMethod(final long v8RuntimePtr, final long objectHandle, final String functionName, final boolean voidMethod) { + return _registerJavaMethod(v8RuntimePtr, objectHandle, functionName, voidMethod); + } + + protected long initNewV8ArrayBuffer(final long v8RuntimePtr, final ByteBuffer buffer, final int capacity) { + return _initNewV8ArrayBuffer(v8RuntimePtr, buffer, capacity); + } + + protected long initNewV8ArrayBuffer(final long v8RuntimePtr, final int capacity) { + return _initNewV8ArrayBuffer(v8RuntimePtr, capacity); + } + + public long initNewV8Int32Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8Int32Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8Float32Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8Float32Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8Float64Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8Float64Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8UInt32Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8UInt32Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8UInt16Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8UInt16Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8Int16Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8Int16Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8UInt8Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8UInt8Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8Int8Array(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8Int8Array(runtimePtr, bufferHandle, offset, size); + } + + public long initNewV8UInt8ClampedArray(final long runtimePtr, final long bufferHandle, final int offset, final int size) { + return _initNewV8UInt8ClampedArray(runtimePtr, bufferHandle, offset, size); + } + + + protected ByteBuffer createV8ArrayBufferBackingStore(final long v8RuntimePtr, final long objectHandle, final int capacity) { + return _createV8ArrayBufferBackingStore(v8RuntimePtr, objectHandle, capacity); + } + + protected long initNewV8Array(final long v8RuntimePtr) { + return _initNewV8Array(v8RuntimePtr); + } + + protected long[] initNewV8Function(final long v8RuntimePtr) { + checkThread(); + return _initNewV8Function(v8RuntimePtr); + } + + protected int arrayGetSize(final long v8RuntimePtr, final long arrayHandle) { + return _arrayGetSize(v8RuntimePtr, arrayHandle); + } + + protected int arrayGetInteger(final long v8RuntimePtr, final long arrayHandle, final int index) { + return _arrayGetInteger(v8RuntimePtr, arrayHandle, index); + } + + protected boolean arrayGetBoolean(final long v8RuntimePtr, final long arrayHandle, final int index) { + return _arrayGetBoolean(v8RuntimePtr, arrayHandle, index); + } + + protected byte arrayGetByte(final long v8RuntimePtr, final long arrayHandle, final int index) { + return _arrayGetByte(v8RuntimePtr, arrayHandle, index); + } + + protected double arrayGetDouble(final long v8RuntimePtr, final long arrayHandle, final int index) { + return _arrayGetDouble(v8RuntimePtr, arrayHandle, index); + } + + protected String arrayGetString(final long v8RuntimePtr, final long arrayHandle, final int index) { + return _arrayGetString(v8RuntimePtr, arrayHandle, index); + } + + protected Object arrayGet(final long v8RuntimePtr, final int expectedType, final long arrayHandle, final int index) { + return _arrayGet(v8RuntimePtr, expectedType, arrayHandle, index); + } + + protected void addArrayIntItem(final long v8RuntimePtr, final long arrayHandle, final int value) { + _addArrayIntItem(v8RuntimePtr, arrayHandle, value); + } + + protected void addArrayBooleanItem(final long v8RuntimePtr, final long arrayHandle, final boolean value) { + _addArrayBooleanItem(v8RuntimePtr, arrayHandle, value); + } + + protected void addArrayDoubleItem(final long v8RuntimePtr, final long arrayHandle, final double value) { + _addArrayDoubleItem(v8RuntimePtr, arrayHandle, value); + } + + protected void addArrayStringItem(final long v8RuntimePtr, final long arrayHandle, final String value) { + _addArrayStringItem(v8RuntimePtr, arrayHandle, value); + } + + protected void addArrayObjectItem(final long v8RuntimePtr, final long arrayHandle, final long value) { + _addArrayObjectItem(v8RuntimePtr, arrayHandle, value); + } + + protected void addArrayUndefinedItem(final long v8RuntimePtr, final long arrayHandle) { + _addArrayUndefinedItem(v8RuntimePtr, arrayHandle); + } + + protected void addArrayNullItem(final long v8RuntimePtr, final long arrayHandle) { + _addArrayNullItem(v8RuntimePtr, arrayHandle); + } + + protected String getConstructorName(final long v8RuntimePtr, final long objectHandle) { + return _getConstructorName(v8RuntimePtr, objectHandle); + } + + 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); + } + + protected int getType(final long v8RuntimePtr, final long objectHandle, final int index) { + return _getType(v8RuntimePtr, objectHandle, index); + } + + protected int getArrayType(final long v8RuntimePtr, final long objectHandle) { + return _getArrayType(v8RuntimePtr, objectHandle); + } + + protected int getType(final long v8RuntimePtr, final long objectHandle, final int index, final int length) { + return _getType(v8RuntimePtr, objectHandle, index, length); + } + + protected void setPrototype(final long v8RuntimePtr, final long objectHandle, final long prototypeHandle) { + _setPrototype(v8RuntimePtr, objectHandle, prototypeHandle); + } + + protected int[] arrayGetIntegers(final long v8RuntimePtr, final long objectHandle, final int index, final int length) { + return _arrayGetIntegers(v8RuntimePtr, objectHandle, index, length); + } + + protected double[] arrayGetDoubles(final long v8RuntimePtr, final long objectHandle, final int index, final int length) { + return _arrayGetDoubles(v8RuntimePtr, objectHandle, index, length); + } + + protected boolean[] arrayGetBooleans(final long v8RuntimePtr, final long objectHandle, final int index, final int length) { + return _arrayGetBooleans(v8RuntimePtr, objectHandle, index, length); + } + + protected byte[] arrayGetBytes(final long v8RuntimePtr, final long objectHandle, final int index, final int length) { + return _arrayGetBytes(v8RuntimePtr, objectHandle, index, length); + } + + protected String[] arrayGetStrings(final long v8RuntimePtr, final long objectHandle, final int index, final int length) { + return _arrayGetStrings(v8RuntimePtr, objectHandle, index, length); + } + + protected int arrayGetIntegers(final long v8RuntimePtr, final long objectHandle, final int index, final int length, final int[] resultArray) { + return _arrayGetIntegers(v8RuntimePtr, objectHandle, index, length, resultArray); + } + + protected int arrayGetDoubles(final long v8RuntimePtr, final long objectHandle, final int index, final int length, final double[] resultArray) { + return _arrayGetDoubles(v8RuntimePtr, objectHandle, index, length, resultArray); + } + + protected int arrayGetBooleans(final long v8RuntimePtr, final long objectHandle, final int index, final int length, final boolean[] resultArray) { + return _arrayGetBooleans(v8RuntimePtr, objectHandle, index, length, resultArray); + } + + protected int arrayGetBytes(final long v8RuntimePtr, final long objectHandle, final int index, final int length, final byte[] resultArray) { + return _arrayGetBytes(v8RuntimePtr, objectHandle, index, length, resultArray); + } + + protected int arrayGetStrings(final long v8RuntimePtr, final long objectHandle, final int index, final int length, final String[] resultArray) { + return _arrayGetStrings(v8RuntimePtr, objectHandle, index, length, resultArray); + } + + protected void terminateExecution(final long v8RuntimePtr) { + _terminateExecution(v8RuntimePtr); + } + + protected void releaseMethodDescriptor(final long v8RuntimePtr, final long methodDescriptor) { + _releaseMethodDescriptor(v8RuntimePtr, methodDescriptor); + } + + 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); + + private native long _createIsolate(String globalAlias); + + // private native long _createInspector(long v8RuntimePtr, final V8InspectorDelegate inspectorDelegate, final String contextName); + + // private native void _dispatchProtocolMessage(final long v8RuntimePtr, long v8InspectorPtr, final String protocolMessage); + + // private native void _schedulePauseOnNextStatement(final long v8RuntimePtr, long v8InspectorPtr, final String reason); + + private native int _executeIntegerScript(long v8RuntimePtr, final String script, final String scriptName, final int lineNumber); + + private native double _executeDoubleScript(long v8RuntimePtr, final String script, final String scriptName, final int lineNumber); + + private native String _executeStringScript(long v8RuntimePtr, final String script, final String scriptName, final int lineNumber); + + private native boolean _executeBooleanScript(long v8RuntimePtr, final String script, final String scriptName, final int lineNumber); + + private native Object _executeScript(long v8RuntimePtr, int expectedType, String script, String scriptName, int lineNumber); + + private native void _executeVoidScript(long v8RuntimePtr, String script, String scriptName, int lineNumber); + + private native void _release(long v8RuntimePtr, long objectHandle); + + private native void _releaseMethodDescriptor(long v8RuntimePtr, long methodDescriptor); + + private native boolean _contains(long v8RuntimePtr, long objectHandle, final String key); + + private native String[] _getKeys(long v8RuntimePtr, long objectHandle); + + private native int _getInteger(long v8RuntimePtr, long objectHandle, final String key); + + private native boolean _getBoolean(long v8RuntimePtr, long objectHandle, final String key); + + private native double _getDouble(long v8RuntimePtr, long objectHandle, final String key); + + private native String _getString(long v8RuntimePtr, long objectHandle, final String key); + + private native Object _get(long v8RuntimePtr, int expectedType, long objectHandle, final String key); + + private native int _executeIntegerFunction(long v8RuntimePtr, long objectHandle, String name, long parametersHandle); + + private native double _executeDoubleFunction(long v8RuntimePtr, long objectHandle, String name, long parametersHandle); + + private native String _executeStringFunction(long v8RuntimePtr2, long handle, String name, long parametersHandle); + + private native boolean _executeBooleanFunction(long v8RuntimePtr2, long handle, String name, long parametersHandle); + + private native Object _executeFunction(long v8RuntimePtr, int expectedType, long objectHandle, String name, long parametersHandle); + + private native Object _executeFunction(long v8RuntimePtr, long receiverHandle, long functionHandle, long parametersHandle); + + private native void _executeVoidFunction(long v8RuntimePtr, long objectHandle, final String name, final long parametersHandle); + + private native boolean _equals(long v8RuntimePtr, long objectHandle, long that); + + private native String _toString(long v8RuntimePtr, long ObjectHandle); + + private native boolean _strictEquals(long v8RuntimePtr, long objectHandle, long that); + + private native boolean _sameValue(long v8RuntimePtr, long objectHandle, long that); + + private native int _identityHash(long v8RuntimePtr, long objectHandle); + + private native void _add(long v8RuntimePtr, long objectHandle, final String key, final int value); + + private native void _addObject(long v8RuntimePtr, long objectHandle, final String key, final long value); + + private native void _add(long v8RuntimePtr, long objectHandle, final String key, final boolean value); + + private native void _add(long v8RuntimePtr, long objectHandle, final String key, final double value); + + private native void _add(long v8RuntimePtr, long objectHandle, final String key, final String value); + + private native void _addUndefined(long v8RuntimePtr, long objectHandle, final String key); + + private native void _addNull(long v8RuntimePtr, long objectHandle, final String key); + + private native long _registerJavaMethod(long v8RuntimePtr, long objectHandle, final String functionName, final boolean voidMethod); + + private native long _initNewV8Array(long v8RuntimePtr); + + private native long[] _initNewV8Function(long v8RuntimePtr); + + private native int _arrayGetSize(long v8RuntimePtr, long arrayHandle); + + private native int _arrayGetInteger(long v8RuntimePtr, long arrayHandle, int index); + + private native boolean _arrayGetBoolean(long v8RuntimePtr, long arrayHandle, int index); + + private native byte _arrayGetByte(long v8RuntimePtr, long arrayHandle, int index); + + private native double _arrayGetDouble(long v8RuntimePtr, long arrayHandle, int index); + + private native String _arrayGetString(long v8RuntimePtr, long arrayHandle, int index); + + private native Object _arrayGet(long v8RuntimePtr, int expectedType, long arrayHandle, int index); + + private native void _addArrayIntItem(long v8RuntimePtr, long arrayHandle, int value); + + private native void _addArrayBooleanItem(long v8RuntimePtr, long arrayHandle, boolean value); + + private native void _addArrayDoubleItem(long v8RuntimePtr, long arrayHandle, double value); + + private native void _addArrayStringItem(long v8RuntimePtr, long arrayHandle, String value); + + private native void _addArrayObjectItem(long v8RuntimePtr, long arrayHandle, long value); + + private native void _addArrayUndefinedItem(long v8RuntimePtr, long arrayHandle); + + private native void _addArrayNullItem(long v8RuntimePtr, long arrayHandle); + + private native int _getType(long v8RuntimePtr, long objectHandle, final String key); + + private native int _getType(long v8RuntimePtr, long objectHandle, final int index); + + private native int _getArrayType(long v8RuntimePtr, long objectHandle); + + private native void _setPrototype(long v8RuntimePtr, long objectHandle, long prototypeHandle); + + private native String _getConstructorName(long v8RuntimePtr, long objectHandle); + + 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); + + private native int[] _arrayGetIntegers(final long v8RuntimePtr, final long objectHandle, final int index, final int length); + + private native boolean[] _arrayGetBooleans(final long v8RuntimePtr, final long objectHandle, final int index, final int length); + + private native byte[] _arrayGetBytes(final long v8RuntimePtr, final long objectHandle, final int index, final int length); + + private native String[] _arrayGetStrings(final long v8RuntimePtr, final long objectHandle, final int index, final int length); + + private native int _arrayGetIntegers(final long v8RuntimePtr, final long objectHandle, final int index, final int length, int[] resultArray); + + private native int _arrayGetDoubles(final long v8RuntimePtr, final long objectHandle, final int index, final int length, double[] resultArray); + + private native int _arrayGetBooleans(final long v8RuntimePtr, final long objectHandle, final int index, final int length, boolean[] resultArray); + + private native int _arrayGetBytes(final long v8RuntimePtr, final long objectHandle, final int index, final int length, byte[] resultArray); + + private native int _arrayGetStrings(final long v8RuntimePtr, final long objectHandle, final int index, final int length, String[] resultArray); + + private native long _initNewV8ArrayBuffer(long v8RuntimePtr, int capacity); + + private native long _initNewV8ArrayBuffer(long v8RuntimePtr, ByteBuffer buffer, int capacity); + + private native long _initNewV8Int32Array(long runtimePtr, long bufferHandle, int offset, int size); + + private native long _initNewV8UInt32Array(long runtimePtr, long bufferHandle, int offset, int size); + + private native long _initNewV8Float32Array(long runtimePtr, long bufferHandle, int offset, int size); + + private native long _initNewV8Float64Array(long runtimePtr, long bufferHandle, int offset, int size); + + private native long _initNewV8Int16Array(long runtimePtr, long bufferHandle, int offset, int size); + + private native long _initNewV8UInt16Array(long runtimePtr, long bufferHandle, int offset, int size); + + private native long _initNewV8Int8Array(long runtimePtr, long bufferHandle, int offset, int size); + + private native long _initNewV8UInt8Array(long runtimePtr, long bufferHandle, int offset, int size); + + 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(); + + private static native void _setFlags(String v8flags); + + private native void _terminateExecution(final long v8RuntimePtr); + + private native long _getGlobalObject(final long v8RuntimePtr); + + private native static long _getBuildID(); + + // private native static void _startNodeJS(final long v8RuntimePtr, final String fileName); + + private native static boolean _pumpMessageLoop(final long v8RuntimePtr); + + 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()) { + notifyReferenceCreated(reference); + } + } + + void releaseObjRef(final V8Value reference) { + if (!referenceHandlers.isEmpty()) { + notifyReferenceDisposed(reference); + } + objectReferences--; + } + + public static void main(String[] args) { + V8 v8 = V8.createV8Runtime(); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Array.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Array.java new file mode 100644 index 000000000..9f3be54b8 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Array.java @@ -0,0 +1,1189 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * A V8Value that represents a JavaScript array. + * JavaScript Arrays contain elements by index, but + * can also contain elements by 'key' which is why V8Array + * extends V8Object. + */ +public class V8Array extends V8Object { + + protected V8Array() { + + } + + /** + * Creates a new V8Array and associates it with the given runtime. + * V8Arrays have native resources and as such, must be released. + * + * @param v8 The runtime on which to associate the V8Array. + */ + public V8Array(final V8 v8) { + super(v8); + v8.checkThread(); + } + + protected V8Array(final V8 v8, final Object data) { + super(v8, data); + } + + @Override + protected V8Value createTwin() { + return new V8Array(v8); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#twin() + */ + @Override + public V8Array twin() { + 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); + released = false; + addObjectReference(handle); + } + + /** + * Returns the length of this array. + * + * @return The length of the array. + */ + public int length() { + v8.checkThread(); + checkReleased(); + return v8.arrayGetSize(v8.getV8RuntimePtr(), getHandle()); + } + + /** + * Returns the type of element at this given index. + * + * @param index The index at which to lookup the type of. + * + * @return The type of the element at the index. + */ + public int getType(final int index) { + v8.checkThread(); + checkReleased(); + return v8.getType(v8.getV8RuntimePtr(), getHandle(), index); + } + + /** + * Gets the type of the array. Returns a 'type' if all the elements in the array + * have the same type, otherwise UNDEFINED is returned. + * + * @return The type of all the elements of the array, or UNDEFINED if they + * are not all the same type. + */ + public int getType() { + v8.checkThread(); + checkReleased(); + return v8.getArrayType(v8.getV8RuntimePtr(), getHandle()); + } + + /** + * Gets the type of a subset of the array. The subset is specified by a start index + * and a length. UNDEFINED is returned if all the elements in the subset are not + * of the same type. + * + * @param index The start index. + * @param length The length. + * + * @return The type of the subset of the array or UNDEFINED if the subset is not + * all the same type. + */ + public int getType(final int index, final int length) { + v8.checkThread(); + checkReleased(); + return v8.getType(v8.getV8RuntimePtr(), getHandle(), index, length); + } + + /** + * Returns the integer value associated at this index. If the value + * at this index does not exist, or if it's not an integer, then + * V8ResultUndefined exception is thrown. + * + * @param index The index whose value to return. + * + * @return The integer value at this index or V8ResultUndefined + * if the index does not exist or the value is not an integer. + */ + public int getInteger(final int index) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetInteger(v8.getV8RuntimePtr(), getHandle(), index); + } + + /** + * Returns the boolean value associated at this index. If the value + * at this index does not exist, or if it's not a boolean, then + * V8ResultUndefined exception is thrown. + * + * @param index The index whose value to return. + * + * @return The boolean value at this index or V8ResultUndefined + * if the index does not exist or the value is not a boolean. + */ + public boolean getBoolean(final int index) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetBoolean(v8.getV8RuntimePtr(), getHandle(), index); + } + + /** + * Returns the byte value associated at this index. If the value at + * this index does not exist, or cannot be cast to a byte, then + * V8ResultUndefined exception is thrown. + * + * @param index The index whose value to return + * @return The byte value at this index or V8ResultUndefined + * if the index does not exist or the value cannot be cast to a byte. + */ + public byte getByte(final int index) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetByte(v8.getV8RuntimePtr(), getHandle(), index); + } + + /** + * Returns the double value associated at this index. If the value + * at this index does not exist, or if it's not a double, then + * V8ResultUndefined exception is thrown. + * + * @param index The index whose value to return. + * + * @return The double value at this index or V8ResultUndefined + * if the index does not exist or the value is not a double. + */ + public double getDouble(final int index) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetDouble(v8.getV8RuntimePtr(), getHandle(), index); + } + + /** + * Returns the String value associated at this index. If the value + * at this index does not exist, or if it's not a String, then + * V8ResultUndefined exception is thrown. + * + * @param index The index whose value to return. + * + * @return The String value at this index or V8ResultUndefined + * if the index does not exist or the value is not a String. + */ + public String getString(final int index) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetString(v8.getV8RuntimePtr(), getHandle(), index); + } + + /** + * Returns the integers contained in a subset of a V8Array. If the subset + * contains elements other than integers, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. + * + * @param index The starting index. + * @param length The length. + * + * @return The integers contained in the subset of the array, or V8ResultUndefined + * exception. + */ + public int[] getIntegers(final int index, final int length) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetIntegers(v8.getV8RuntimePtr(), getHandle(), index, length); + } + + /** + * Gets the integers contained in a subset of a V8Array. If the subset + * contains elements other than integers, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. Finally, if the resultArray + * is not large enough to hold the results then IndexOutOfBoundsException is thrown. + * + * @param index The starting index. + * @param length The length. + * @param resultArray The array to put the results in. + * + * @return The number of elements added to the array. + */ + public int getIntegers(final int index, final int length, final int[] resultArray) { + v8.checkThread(); + checkReleased(); + if (length > resultArray.length) { + throw new IndexOutOfBoundsException(); + } + return v8.arrayGetIntegers(v8.getV8RuntimePtr(), getHandle(), index, length, resultArray); + } + + /** + * Returns the doubles contained in a subset of a V8Array. If the subset + * contains elements other than doubles, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. + * + * @param index The starting index. + * @param length The length. + * + * @return The doubles contained in the subset of the array, or V8ResultUndefined + * exception. + */ + public double[] getDoubles(final int index, final int length) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetDoubles(v8.getV8RuntimePtr(), getHandle(), index, length); + } + + /** + * Gets the doubles contained in a subset of a V8Array. If the subset + * contains elements other than doubles, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. Finally, if the resultArray + * is not large enough to hold the results then IndexOutOfBoundsException is thrown. + * + * @param index The starting index. + * @param length The length. + * @param resultArray The array to put the results in. + * + * @return The number of elements added to the array. + */ + public int getDoubles(final int index, final int length, final double[] resultArray) { + v8.checkThread(); + checkReleased(); + if (length > resultArray.length) { + throw new IndexOutOfBoundsException(); + } + return v8.arrayGetDoubles(v8.getV8RuntimePtr(), getHandle(), index, length, resultArray); + } + + /** + * Returns the booleans contained in a subset of a V8Array. If the subset + * contains elements other than booleans, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. + * + * @param index The starting index. + * @param length The length. + * + * @return The booleans contained in the subset of the array, or V8ResultUndefined + * exception. + */ + public boolean[] getBooleans(final int index, final int length) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetBooleans(v8.getV8RuntimePtr(), getHandle(), index, length); + } + + /** + * Returns the bytes contained in a subset of a V8Array. If the subset + * contains elements that cannot be cast to bytes, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. + * + * @param index The starting index. + * @param length The length. + * + * @return The bytes contained in the subset of the array, or V8ResultUndefined + * exception. + */ + public byte[] getBytes(final int index, final int length) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetBytes(v8.getV8RuntimePtr(), getHandle(), index, length); + } + + /** + * Gets the booleans contained in a subset of a V8Array. If the subset + * contains elements other than booleans, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. Finally, if the resultArray + * is not large enough to hold the results then IndexOutOfBoundsException is thrown. + * + * @param index The starting index. + * @param length The length. + * @param resultArray The array to put the results in. + * + * @return The number of elements added to the array. + */ + public int getBooleans(final int index, final int length, final boolean[] resultArray) { + v8.checkThread(); + checkReleased(); + if (length > resultArray.length) { + throw new IndexOutOfBoundsException(); + } + return v8.arrayGetBooleans(v8.getV8RuntimePtr(), getHandle(), index, length, resultArray); + } + + /** + * Gets the bytes contained in a subset of a V8Array. If the subset + * contains elements that cannot be cast to bytes, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. Finally, if the resultArray + * is not large enough to hold the results then IndexOutOfBoundsException is thrown. + * + * @param index The starting index. + * @param length The length. + * @param resultArray The array to put the results in. + * + * @return The number of elements added to the array. + */ + public int getBytes(final int index, final int length, final byte[] resultArray) { + v8.checkThread(); + checkReleased(); + if (length > resultArray.length) { + throw new IndexOutOfBoundsException(); + } + return v8.arrayGetBytes(v8.getV8RuntimePtr(), getHandle(), index, length, resultArray); + } + + /** + * Returns the Strings contained in a subset of a V8Array. If the subset + * contains elements other than Strings, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. + * + * @param index The starting index. + * @param length The length. + * + * @return The Strings contained in the subset of the array, or V8ResultUndefined + * exception. + */ + public String[] getStrings(final int index, final int length) { + v8.checkThread(); + checkReleased(); + return v8.arrayGetStrings(v8.getV8RuntimePtr(), getHandle(), index, length); + } + + /** + * Gets the Strings contained in a subset of a V8Array. If the subset + * contains elements other than Strings, then a V8ResultUndefined exception + * is thrown. Furthermore, if the subset is not entirely contained within the array, + * then V8ResultUndefined exception is also thrown. Finally, if the resultArray + * is not large enough to hold the results then IndexOutOfBoundsException is thrown. + * + * @param index The starting index. + * @param length The length. + * @param resultArray The array to put the results in. + * + * @return The number of elements added to the array. + */ + public int getStrings(final int index, final int length, final String[] resultArray) { + v8.checkThread(); + checkReleased(); + if (length > resultArray.length) { + throw new IndexOutOfBoundsException(); + } + return v8.arrayGetStrings(v8.getV8RuntimePtr(), getHandle(), index, length, resultArray); + } + + /** + * Gets the value at a given index as a Java Object. Primitives are boxed. + * + * @param index The index to get the value at. + * + * @return The value at the given index. + */ + public Object get(final int index) { + v8.checkThread(); + checkReleased(); + return v8.arrayGet(v8.getV8RuntimePtr(), V8_OBJECT, objectHandle, index); + } + + /** + * Returns the V8Array value associated at this index. If the value + * at this index does not exist, or if it's not a V8Array, then + * V8ResultUndefined exception is thrown. + * + * @param index The index whose value to return. + * + * @return The V8Array value at this index or V8ResultUndefined + * if the index does not exist or the value is not a V8Array. + */ + public V8Array getArray(final int index) { + v8.checkThread(); + checkReleased(); + Object result = v8.arrayGet(v8.getV8RuntimePtr(), V8_ARRAY, objectHandle, index); + if ((result == null) || (result instanceof V8Array)) { + return (V8Array) result; + } + throw new V8ResultUndefined(); + } + + /** + * Returns the V8Object value associated at this index. If the value + * at this index does not exist, or if it's not a V8Object, then + * V8ResultUndefined exception is thrown. + * + * @param index The index whose value to return. + * + * @return The V8Object value at this index or V8ResultUndefined + * if the index does not exist or the value is not a V8Object. + */ + public V8Object getObject(final int index) { + v8.checkThread(); + checkReleased(); + Object result = v8.arrayGet(v8.getV8RuntimePtr(), V8_OBJECT, objectHandle, index); + if ((result == null) || (result instanceof V8Object)) { + return (V8Object) result; + } + throw new V8ResultUndefined(); + } + + /** + * Pushes an integer value 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 int value) { + v8.checkThread(); + checkReleased(); + v8.addArrayIntItem(v8.getV8RuntimePtr(), getHandle(), value); + return this; + } + + /** + * Pushes a boolean value 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 boolean value) { + v8.checkThread(); + checkReleased(); + v8.addArrayBooleanItem(v8.getV8RuntimePtr(), getHandle(), value); + return this; + } + + /** + * Pushes a double value 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 double value) { + v8.checkThread(); + checkReleased(); + v8.addArrayDoubleItem(v8.getV8RuntimePtr(), getHandle(), value); + return this; + } + + /** + * Pushes a String value 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 String value) { + v8.checkThread(); + checkReleased(); + if (value == null) { + v8.addArrayNullItem(v8.getV8RuntimePtr(), getHandle()); + } else if (value.equals(V8.getUndefined())) { + v8.addArrayUndefinedItem(v8.getV8RuntimePtr(), getHandle()); + } else { + v8.addArrayStringItem(v8.getV8RuntimePtr(), getHandle(), value); + } + return this; + } + + /** + * Pushes a V8Value 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 V8Value value) { + v8.checkThread(); + checkReleased(); + v8.checkRuntime(value); + if (value == null) { + v8.addArrayNullItem(v8.getV8RuntimePtr(), getHandle()); + } else if (value.equals(V8.getUndefined())) { + v8.addArrayUndefinedItem(v8.getV8RuntimePtr(), getHandle()); + } else { + v8.addArrayObjectItem(v8.getV8RuntimePtr(), getHandle(), value.getHandle()); + } + 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; + * + * @return The receiver. + */ + public V8Array pushNull() { + v8.checkThread(); + checkReleased(); + v8.addArrayNullItem(v8.getV8RuntimePtr(), getHandle()); + return this; + } + + /** + * Pushes undefined to the next available spot in the Array. In + * particular, this[length] = undefined; + * + * @return The receiver. + */ + public V8Array pushUndefined() { + v8.checkThread(); + checkReleased(); + v8.addArrayUndefinedItem(v8.getV8RuntimePtr(), getHandle()); + return this; + } + + public static class Undefined extends V8Array { + + public Undefined() { + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#isUndefined() + */ + @Override + public boolean isUndefined() { + return true; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#isReleased() + */ + @Override + public boolean isReleased() { + 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() { + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#twin() + */ + @Override + public V8Array.Undefined twin() { + return (V8Array.Undefined) super.twin(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#toString() + */ + @Override + public String toString() { + return "undefined"; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object that) { + if ((that instanceof V8Object) && ((V8Object) that).isUndefined()) { + return true; + } + return false; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#hashCode() + */ + @Override + public int hashCode() { + return 919; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#getRuntime() + */ + @Override + public V8 getRuntime() { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, boolean) + */ + @Override + public V8Object add(final String key, final boolean value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, double) + */ + @Override + public V8Object add(final String key, final double value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, int) + */ + @Override + public V8Object add(final String key, final int value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, java.lang.String) + */ + @Override + public V8Object add(final String key, final String value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, com.eclipsesource.v8.V8Value) + */ + @Override + public V8Object add(final String key, final V8Value value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#addUndefined(java.lang.String) + */ + @Override + public V8Object addUndefined(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#contains(java.lang.String) + */ + @Override + public boolean contains(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeArrayFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public V8Array executeArrayFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeBooleanFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public boolean executeBooleanFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeDoubleFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public double executeDoubleFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeIntegerFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public int executeIntegerFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeObjectFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public V8Object executeObjectFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeStringFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public String executeStringFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeVoidFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public void executeVoidFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getArray(java.lang.String) + */ + @Override + public V8Array getArray(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getBoolean(java.lang.String) + */ + @Override + public boolean getBoolean(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getDouble(java.lang.String) + */ + @Override + public double getDouble(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getInteger(java.lang.String) + */ + @Override + public int getInteger(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getKeys() + */ + @Override + public String[] getKeys() { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getObject(java.lang.String) + */ + @Override + public V8Object getObject(final String key) throws V8ResultUndefined { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getString(java.lang.String) + */ + @Override + public String getString(final String key) throws V8ResultUndefined { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getType(java.lang.String) + */ + @Override + public int getType(final String key) throws V8ResultUndefined { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#registerJavaMethod(com.eclipsesource.v8.JavaCallback, java.lang.String) + */ + @Override + public V8Object registerJavaMethod(final JavaCallback callback, final String jsFunctionName) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#registerJavaMethod(com.eclipsesource.v8.JavaVoidCallback, java.lang.String) + */ + @Override + public V8Object registerJavaMethod(final JavaVoidCallback callback, final String jsFunctionName) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#registerJavaMethod(java.lang.Object, java.lang.String, java.lang.String, java.lang.Class[], boolean) + */ + @Override + public V8Object registerJavaMethod(final Object object, final String methodName, final String jsFunctionName, final Class[] parameterTypes, final boolean includeReceiver) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#setPrototype(com.eclipsesource.v8.V8Object) + */ + @Override + public V8Object setPrototype(final V8Object value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#get(int) + */ + @Override + public Object get(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getArray(int) + */ + @Override + public V8Array getArray(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getBoolean(int) + */ + @Override + public boolean getBoolean(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getBooleans(int, int) + */ + @Override + public boolean[] getBooleans(final int index, final int length) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getBytes(int, int) + */ + @Override + public byte[] getBytes(final int index, final int length) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getBytes(int, int) + */ + @Override + public int getBytes(final int index, final int length, final byte[] resultArray) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getByte(int) + */ + @Override + public byte getByte(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getBooleans(int, int, boolean[]) + */ + @Override + public int getBooleans(final int index, final int length, final boolean[] resultArray) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getDouble(int) + */ + @Override + public double getDouble(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getDoubles(int, int) + */ + @Override + public double[] getDoubles(final int index, final int length) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getDoubles(int, int, double[]) + */ + @Override + public int getDoubles(final int index, final int length, final double[] resultArray) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getInteger(int) + */ + @Override + public int getInteger(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getIntegers(int, int) + */ + @Override + public int[] getIntegers(final int index, final int length) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getIntegers(int, int, int[]) + */ + @Override + public int getIntegers(final int index, final int length, final int[] resultArray) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getObject(int) + */ + @Override + public V8Object getObject(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getString(int) + */ + @Override + public String getString(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getStrings(int, int) + */ + @Override + public String[] getStrings(final int index, final int length) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getStrings(int, int, java.lang.String[]) + */ + @Override + public int getStrings(final int index, final int length, final String[] resultArray) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getType() + */ + @Override + public int getType() { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getType(int) + */ + @Override + public int getType(final int index) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#getType(int, int) + */ + @Override + public int getType(final int index, final int length) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#length() + */ + @Override + public int length() { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#push(boolean) + */ + @Override + public V8Array push(final boolean value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#push(double) + */ + @Override + public V8Array push(final double value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#push(int) + */ + @Override + public V8Array push(final int value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#push(java.lang.String) + */ + @Override + public V8Array push(final String value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#push(com.eclipsesource.v8.V8Value) + */ + @Override + public V8Array push(final V8Value value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Array#pushUndefined() + */ + @Override + public V8Array pushUndefined() { + throw new UnsupportedOperationException(); + } + + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ArrayBuffer.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ArrayBuffer.java new file mode 100644 index 000000000..88d0ee38e --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ArrayBuffer.java @@ -0,0 +1,472 @@ +/******************************************************************************* + * 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; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * V8ArrayBuffers represent ArrayBuffers from V8, but are backed by a + * java.nio.ByteBuffer. This means that any data stored in a TypedArray + * can be accessed by the java.nio.ByteBuffer. This significantly improves + * performance of data access from Java to JavaScript. + * + * V8ArrayBuffers can either be constructed in Java, or returned from + * JavaScript. + * + */ +public class V8ArrayBuffer extends V8Value { + + public ByteBuffer byteBuffer; + + /** + * Creates a new V8ArrayBuffer on a given V8Runtime with a + * given capacity. + * + * @param v8 The runtime on which to create the ArrayBuffer + * @param capacity The capacity of the buffer + */ + public V8ArrayBuffer(final V8 v8, final int capacity) { + super(v8); + initialize(v8.getV8RuntimePtr(), capacity); + byteBuffer = v8.createV8ArrayBufferBackingStore(v8.getV8RuntimePtr(), objectHandle, capacity); + byteBuffer.order(ByteOrder.nativeOrder()); + } + + 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"); + } + initialize(v8.getV8RuntimePtr(), byteBuffer); + this.byteBuffer = byteBuffer; + byteBuffer.order(ByteOrder.nativeOrder()); + } + + @Override + protected void initialize(final long runtimePtr, final Object data) { + v8.checkThread(); + if (data instanceof ByteBuffer) { + ByteBuffer buffer = (ByteBuffer) data; + int capacity = buffer.limit(); + objectHandle = v8.initNewV8ArrayBuffer(v8.getV8RuntimePtr(), buffer, capacity); + } else { + int capacity = (Integer) data; + objectHandle = v8.initNewV8ArrayBuffer(v8.getV8RuntimePtr(), capacity); + } + released = false; + addObjectReference(objectHandle); + } + + @Override + protected V8Value createTwin() { + return new V8ArrayBuffer(v8, byteBuffer); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#twin() + */ + @Override + public V8ArrayBuffer twin() { + v8.checkThread(); + checkReleased(); + return (V8ArrayBuffer) super.twin(); + } + + /** + * Returns the buffers limit + * + * @return the buffers limit + */ + public int limit() { + v8.checkThread(); + checkReleased(); + return byteBuffer.limit(); + } + + /** + * Returns the buffers capacity + * + * @return the buffers capacity + */ + 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(); + checkReleased(); + return byteBuffer.asDoubleBuffer().limit(); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Function.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Function.java new file mode 100644 index 000000000..918a38f93 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Function.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +/** + * A V8Value that represents a JavaScript function. + * JavaScript functions cannot be created in Java, but + * can be returned as the result of invoking a JS script + * or JS Function. + */ +public class V8Function extends V8Object { + + /** + * Create a JavaScript function, that when invoked will call + * the javaCallback passed to the receiver. + * + * @param v8 The v8 runtime on which to create this function + * @param javaCallback The callback to invoke + */ + public V8Function(final V8 v8, final JavaCallback javaCallback) { + super(v8, javaCallback); + } + + public V8Function(final V8 v8) { + this(v8, null); + } + + @Override + protected V8Value createTwin() { + 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) { + super.initialize(runtimePtr, null); + return; + } + JavaCallback javaCallback = (JavaCallback) data; + long[] pointers = v8.initNewV8Function(runtimePtr); + // position 0 is the object reference, position 1 is the function reference + v8.createAndRegisterMethodDescriptor(javaCallback, pointers[1]); + released = false; + addObjectReference(pointers[0]); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#twin() + */ + @Override + public V8Function twin() { + return (V8Function) super.twin(); + } + + /** + * Invoke the JavaScript function on the current runtime. + * + * @param receiver The object on which to call the function on. The + * receiver will be mapped to 'this' in JavaScript. If receiver is null + * or undefined, then the V8 runtime will be used instead. + * @param parameters The parameters passed to the JS Function. + * + * @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(); + return v8.executeFunction(v8.getV8RuntimePtr(), receiverHandle, objectHandle, parametersHandle); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Locker.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Locker.java new file mode 100644 index 000000000..2d839cd14 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Locker.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +/** + * Represents a lock for a V8Runtime that can be moved between + * threads. When instantiated, the lock is automatically assigned + * to the current thread. If another thread wishes to acquire the + * lock, it must first be released. + */ +public class V8Locker { + + private Thread thread = null; + private boolean released = false; + private V8 runtime; + + public 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 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: 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. 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; + } + + /** + * Checks if the locker has access to the current thread. + * If the locker holds a different thread, than an Error + * 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: current thread is " + Thread.currentThread() + " while the locker has thread " + thread); + } + } + + /** + * Check if the current thread holds this lock. + * + * @return Returns true if the current thread holds the lock, + * false otherwise. + */ + public boolean hasLock() { + return thread == Thread.currentThread(); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Object.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Object.java new file mode 100644 index 000000000..c0bdf3737 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Object.java @@ -0,0 +1,1005 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +import java.lang.reflect.Method; + +/** + * The concrete class for all V8 Objects. V8Objects are + * JavaScript objects accessible in java. Specialized + * subclasses exist for V8Arrays and V8Functions. + * + * V8Object are JavaScript object with key value pairs. + * Specific get methods exist to access values as primitives. + * General get methods also exist, which return Java Objects + * and can be casted to the correct subclass. + * + * V8Object have native resources and must be released + * when they are no longer need in Java. + */ +public class V8Object extends V8Value { + + /** + * Create a new V8Object and associate it with a runtime. + * Once created, it must be released. + * + * @param v8 The runtime on which to associate the V8Object. + */ + public V8Object(final V8 v8) { + this(v8, null); + } + + protected V8Object(final V8 v8, final Object data) { + super(v8); + if (v8 != null) { + this.v8.checkThread(); + initialize(this.v8.getV8RuntimePtr(), data); + } + } + + protected V8Object() { + + } + + @Override + protected V8Value createTwin() { + return new V8Object(v8); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#twin() + */ + @Override + public V8Object twin() { + return (V8Object) super.twin(); + } + + /** + * Determine if a key/value pair with this key exists in + * the Object. + * + * @param key The key to check + * @return True if the key exists, false otherwise. + */ + public boolean contains(final String key) { + v8.checkThread(); + checkReleased(); + checkKey(key); + return v8.contains(v8.getV8RuntimePtr(), objectHandle, key); + } + + /** + * Returns all the keys associated with this JavaScript Object. + * Keys associated with the objects prototype are not returned. + * + * @return The keys associated with this JavaScript Object. + */ + public String[] getKeys() { + v8.checkThread(); + checkReleased(); + return v8.getKeys(v8.getV8RuntimePtr(), objectHandle); + } + + /** + * Returns the type of the value associated with this Key, or + * UNDEFINED if the key does not exist. Types are specified as + * integer constants. The types are all defined in V8Value. + * + * @param key The key whose type to lookup. + * + * @return The Type of the value associated with this key + */ + public int getType(final String key) { + v8.checkThread(); + checkReleased(); + checkKey(key); + return v8.getType(v8.getV8RuntimePtr(), objectHandle, key); + } + + /** + * Returns the value associated with this key. Values are Java Objects. + * If the value is a primitive, its boxed type is returned. If the + * value is a V8Value, it must be released. + * + * @param key The key whose value to return. + * + * @return The value associated with this key. + */ + public Object get(final String key) { + v8.checkThread(); + checkReleased(); + checkKey(key); + return v8.get(v8.getV8RuntimePtr(), V8_OBJECT, objectHandle, key); + } + + /** + * Returns the integer value associated with this key. If the value + * associated with this key does not exist, or if it's not an integer, then + * V8ResultUndefined exception is thrown. + * + * @param key The key whose value to return. + * + * @return The integer value associated with this key, or V8ResultUndefined + * if the key does not exist or the value is not an integer. + */ + public int getInteger(final String key) { + v8.checkThread(); + checkReleased(); + checkKey(key); + return v8.getInteger(v8.getV8RuntimePtr(), objectHandle, key); + } + + /** + * Returns the boolean value associated with this key. If the value + * associated with this key does not exist, or if it's not a boolean, then + * V8ResultUndefined exception is thrown. + * + * @param key The key whose value to return. + * + * @return The boolean value associated with this key, or V8ResultUndefined + * if the key does not exist or the value is not a boolean. + */ + public boolean getBoolean(final String key) { + v8.checkThread(); + checkReleased(); + checkKey(key); + return v8.getBoolean(v8.getV8RuntimePtr(), objectHandle, key); + } + + /** + * Returns the double value associated with this key. If the value + * associated with this key does not exist, or if it's not a double, then + * V8ResultUndefined exception is thrown. + * + * @param key The key whose value to return. + * + * @return The double value associated with this key, or V8ResultUndefined + * if the key does not exist or the value is not a double. + */ + public double getDouble(final String key) { + v8.checkThread(); + checkReleased(); + checkKey(key); + return v8.getDouble(v8.getV8RuntimePtr(), objectHandle, key); + } + + /** + * Returns the String value associated with this key. If the value + * associated with this key does not exist, or if it's not a String, then + * V8ResultUndefined exception is thrown. + * + * @param key The key whose value to return. + * + * @return The String value associated with this key, or V8ResultUndefined + * if the key does not exist or the value is not a String. + */ + public String getString(final String key) { + v8.checkThread(); + checkReleased(); + checkKey(key); + return v8.getString(v8.getV8RuntimePtr(), objectHandle, key); + } + + /** + * Returns the V8Array value associated with this key. If the value + * associated with this key does not exist then UNDEFINED is returned. + * If the value exists but is not an array then + * V8ResultUndefined exception is thrown. + * + * @param key The key whose value to return. + * + * @return The V8Array value associated with this key. + */ + 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; + } + throw new V8ResultUndefined(); + } + + /** + * Returns the V8Object value associated with this key. If the value + * associated with this key does not exist then UNDEFINED is returned. + * If the value exists but is not an JS Object then + * V8ResultUndefined exception is thrown. + * + * @param key The key whose value to return. + * + * @return The V8Object value associated with this key. + */ + 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; + } + throw new V8ResultUndefined(); + } + + /** + * Invoke a JavaScript function and return the result as a integer. If the + * result is not an integer, or does not exist, then V8ResultUndefined is thrown. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + * + * @return An integer representing the result of the function call or V8ResultUndefined + * if the result is not an integer. + */ + 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); + } + + /** + * Invoke a JavaScript function and return the result as a double. If the + * result is not a double, or does not exist, then V8ResultUndefined is thrown. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + * + * @return A double representing the result of the function call or V8ResultUndefined + * if the result is not a double. + */ + 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); + } + + /** + * Invoke a JavaScript function and return the result as a String. If the + * result is not a String, or does not exist, then V8ResultUndefined is thrown. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + * + * @return A String representing the result of the function call or V8ResultUndefined + * if the result is not a String. + */ + 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); + } + + /** + * Invoke a JavaScript function and return the result as a boolean. If the + * result is not a boolean, or does not exist, then V8ResultUndefined is thrown. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + * + * @return A boolean representing the result of the function call or V8ResultUndefined + * if the result is not a boolean. + */ + 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); + } + + /** + * Invoke a JavaScript function and return the result as a V8Array. If the + * result is not a V8Array then V8ResultUndefined is thrown. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + * + * @return A V8Array representing the result of the function call or V8ResultUndefined + * if the result is not a V8Array. The result must be released. + */ + 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) { + return (V8Array) result; + } + throw new V8ResultUndefined(); + } + + /** + * Invoke a JavaScript function and return the result as a V8Object. If the + * result is not a V8Object then V8ResultUndefined is thrown. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + * + * @return A V8Object representing the result of the function call or V8ResultUndefined + * if the result is not a V8Object. The result must be released. + */ + 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) { + return (V8Object) result; + } + throw new V8ResultUndefined(); + } + + /** + * Invoke a JavaScript function and return the result as a Java Object. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + * + * @return A Java Object representing the result of the function call. + */ + 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); + } + + /** + * Invoke a JavaScript function and return the result as a Java Object. + * + * @param name The name of the JS Function to call + * @return The result of this JS Function + */ + public Object executeJSFunction(final String name) { + return executeFunction(name, null); + } + + /** + * Invoke a JavaScript function and return the result as a Java Object. + * + * @param name The name of the JS Function to call. + * @param parameters The parameters to pass to the function. + * @return A Java Object representing the result of the function call. + */ + public Object executeJSFunction(final String name, final Object... parameters) { + if (parameters == null) { + return executeFunction(name, null); + } + V8Array parameterArray = new V8Array(v8.getRuntime()); + try { + for (Object object : parameters) { + if (object == null) { + parameterArray.pushNull(); + } else if (object instanceof V8Value) { + parameterArray.push((V8Value) object); + } else if (object instanceof Integer) { + parameterArray.push(object); + } else if (object instanceof Double) { + 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(object); + } else if (object instanceof String) { + parameterArray.push((String) object); + } else { + throw new IllegalArgumentException("Unsupported Object of type: " + object.getClass()); + } + } + return executeFunction(name, parameterArray); + } finally { + parameterArray.close(); + } + } + + /** + * Invokes a JavaScript function which does not return a result. + * + * @param name The name of the JS Function to call. + * + * @param parameters The parameters to pass to the function. Parameters must be released. + */ + 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); + } + + /** + * Adds a key value pair to the receiver where the value is an integer. + * + * @param key The key to associate the value with. + * @param value The value to add. + * + * @return The receiver. + */ + public V8Object add(final String key, final int value) { + v8.checkThread(); + checkReleased(); + v8.add(v8.getV8RuntimePtr(), objectHandle, key, value); + return this; + } + + /** + * Adds a key value pair to the receiver where the value is a boolean. + * + * @param key The key to associate the value with. + * @param value The value to add. + * + * @return The receiver. + */ + public V8Object add(final String key, final boolean value) { + v8.checkThread(); + checkReleased(); + v8.add(v8.getV8RuntimePtr(), objectHandle, key, value); + return this; + } + + /** + * Adds a key value pair to the receiver where the value is a double. + * + * @param key The key to associate the value with. + * @param value The value to add. + * + * @return The receiver. + */ + public V8Object add(final String key, final double value) { + v8.checkThread(); + checkReleased(); + v8.add(v8.getV8RuntimePtr(), objectHandle, key, value); + return this; + } + + /** + * Adds a key value pair to the receiver where the value is a String. + * + * @param key The key to associate the value with. + * @param value The value to add. + * + * @return The receiver. + */ + public V8Object add(final String key, final String value) { + v8.checkThread(); + checkReleased(); + if (value == null) { + v8.addNull(v8.getV8RuntimePtr(), objectHandle, key); + } else if (value.equals(V8.getUndefined())) { + v8.addUndefined(v8.getV8RuntimePtr(), objectHandle, key); + } else { + v8.add(v8.getV8RuntimePtr(), objectHandle, key, value); + } + return this; + } + + /** + * Adds a key value pair to the receiver where the value is a V8Value. + * + * @param key The key to associate the value with. + * @param value The value to add. + * + * @return The receiver. + */ + 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())) { + v8.addUndefined(v8.getV8RuntimePtr(), objectHandle, key); + } else { + v8.addObject(v8.getV8RuntimePtr(), objectHandle, key, value.getHandle()); + } + return this; + } + + /** + * Associate UNDEFINED with the given key. + * + * @param key The key to associate UNDEFINED with. + * + * @return The receiver. + */ + public V8Object addUndefined(final String key) { + v8.checkThread(); + checkReleased(); + v8.addUndefined(v8.getV8RuntimePtr(), objectHandle, key); + return this; + } + + /** + * Associate NULL with the given key. + * + * @param key The key to associate NULL with. + * + * @return The receiver. + */ + public V8Object addNull(final String key) { + v8.checkThread(); + checkReleased(); + v8.addNull(v8.getV8RuntimePtr(), objectHandle, key); + return this; + } + + /** + * Sets the prototype of the receiver. + * + * @param value The prototype to associate with this V8Object. + * + * @return The receiver. + */ + public V8Object setPrototype(final V8Object value) { + v8.checkThread(); + checkReleased(); + v8.setPrototype(v8.getV8RuntimePtr(), objectHandle, value.getHandle()); + return this; + } + + /** + * Register a Java method as a JavaScript function. When the JS Function is invoked + * the Java method will be called. + * + * @param callback The JavaCallback to call when the JSFunction is invoked. + * @param jsFunctionName The name of the JSFunction. + * + * @return The receiver. + */ + public V8Object registerJavaMethod(final JavaCallback callback, final String jsFunctionName) { + v8.checkThread(); + checkReleased(); + v8.registerCallback(callback, getHandle(), jsFunctionName); + return this; + } + + /** + * Register a void Java method as a JavaScript function. When the JS Function is invoked + * the Java method will be called. + * + * @param callback The JavaVoidCallback to call when the JSFunction is invoked. + * @param jsFunctionName The name of the JSFunction. + * + * @return The receiver. + */ + public V8Object registerJavaMethod(final JavaVoidCallback callback, final String jsFunctionName) { + v8.checkThread(); + checkReleased(); + v8.registerVoidCallback(callback, getHandle(), jsFunctionName); + return this; + } + + /** + * Register a Java method reflectively given it's name a signature. + * + * @param object The Java Object on which the method is defined. + * @param methodName The name of the method to register. + * @param jsFunctionName The name of the JavaScript function to register the + * method with. + * @param parameterTypes The parameter types of the method. + * + * @return The receiver. + */ + public V8Object registerJavaMethod(final Object object, final String methodName, final String jsFunctionName, final Class[] parameterTypes) { + return registerJavaMethod(object, methodName, jsFunctionName, parameterTypes, false); + } + + /** + * Register a Java method reflectively given it's name a signature. The option to include + * the JS Object in the callback can be specified by setting includeReceiver true. + * + * @param object The Java Object on which the method is defined. + * @param methodName The name of the method to register. + * @param jsFunctionName The name of the JavaScript function to register the + * method with. + * @param parameterTypes The parameter types of the method. + * @param includeReceiver True if the first parameter should include the JS Object, + * false otherwise. + * + * @return The receiver. + */ + public V8Object registerJavaMethod(final Object object, final String methodName, final String jsFunctionName, final Class[] parameterTypes, final boolean includeReceiver) { + v8.checkThread(); + checkReleased(); + try { + Method method = object.getClass().getMethod(methodName, parameterTypes); + method.setAccessible(true); + v8.registerCallback(object, method, getHandle(), jsFunctionName, includeReceiver); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(e); + } catch (SecurityException e) { + throw new IllegalStateException(e); + } + return this; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + if (isReleased() || v8.isReleased()) { + return "[Object released]"; + } + v8.checkThread(); + return v8.toString(v8.getV8RuntimePtr(), getHandle()); + } + + private void checkKey(final String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + } + + public static class Undefined extends V8Object { + + public Undefined() { + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#isUndefined() + */ + @Override + public boolean isUndefined() { + return true; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#isReleased() + */ + @Override + public boolean isReleased() { + 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() { + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#twin() + */ + @Override + public Undefined twin() { + return (Undefined) super.twin(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#toString() + */ + @Override + public String toString() { + return "undefined"; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object that) { + if ((that instanceof V8Object) && ((V8Object) that).isUndefined()) { + return true; + } + return false; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#hashCode() + */ + @Override + public int hashCode() { + return 919; + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, boolean) + */ + @Override + public V8Object add(final String key, final boolean value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Value#getRuntime() + */ + @Override + public V8 getRuntime() { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, double) + */ + @Override + public V8Object add(final String key, final double value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, int) + */ + @Override + public V8Object add(final String key, final int value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeJSFunction(java.lang.String, java.lang.Object[]) + */ + @Override + public Object executeJSFunction(final String name, final Object... parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public Object executeFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, java.lang.String) + */ + @Override + public V8Object add(final String key, final String value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#add(java.lang.String, com.eclipsesource.v8.V8Value) + */ + @Override + public V8Object add(final String key, final V8Value value) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#addUndefined(java.lang.String) + */ + @Override + public V8Object addUndefined(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#contains(java.lang.String) + */ + @Override + public boolean contains(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeArrayFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public V8Array executeArrayFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeBooleanFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public boolean executeBooleanFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeDoubleFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public double executeDoubleFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeIntegerFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public int executeIntegerFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeObjectFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public V8Object executeObjectFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeStringFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public String executeStringFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#executeVoidFunction(java.lang.String, com.eclipsesource.v8.V8Array) + */ + @Override + public void executeVoidFunction(final String name, final V8Array parameters) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getArray(java.lang.String) + */ + @Override + public V8Array getArray(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getBoolean(java.lang.String) + */ + @Override + public boolean getBoolean(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getDouble(java.lang.String) + */ + @Override + public double getDouble(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getInteger(java.lang.String) + */ + @Override + public int getInteger(final String key) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getKeys() + */ + @Override + public String[] getKeys() { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getObject(java.lang.String) + */ + @Override + public V8Object getObject(final String key) throws V8ResultUndefined { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getString(java.lang.String) + */ + @Override + public String getString(final String key) throws V8ResultUndefined { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#getType(java.lang.String) + */ + @Override + public int getType(final String key) throws V8ResultUndefined { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#registerJavaMethod(com.eclipsesource.v8.JavaCallback, java.lang.String) + */ + @Override + public V8Object registerJavaMethod(final JavaCallback callback, final String jsFunctionName) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#registerJavaMethod(com.eclipsesource.v8.JavaVoidCallback, java.lang.String) + */ + @Override + public V8Object registerJavaMethod(final JavaVoidCallback callback, final String jsFunctionName) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#registerJavaMethod(java.lang.Object, java.lang.String, java.lang.String, java.lang.Class[], boolean) + */ + @Override + public V8Object registerJavaMethod(final Object object, final String methodName, final String jsFunctionName, final Class[] parameterTypes, final boolean includeReceiver) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see com.eclipsesource.v8.V8Object#setPrototype(com.eclipsesource.v8.V8Object) + */ + @Override + public V8Object setPrototype(final V8Object value) { + throw new UnsupportedOperationException(); + } + + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ResultUndefined.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ResultUndefined.java new file mode 100644 index 000000000..4d15d6378 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ResultUndefined.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * An exception that's used to indicate that method that should have returned a + * primitive, returned an Undefined instead. + * + * In Java, Undefined cannot be returned for all methods, especially if + * the method returns a primitive (int, double, boolean) or a String. + * In this case, if an Undefined should be returned from JS, then an instance + * of this exception is thrown. + */ +@SuppressWarnings("serial") +public class V8ResultUndefined extends V8RuntimeException { + + V8ResultUndefined(final String message) { + super(message); + } + + V8ResultUndefined() { + super(); + } +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8RuntimeException.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8RuntimeException.java new file mode 100644 index 000000000..2ea34af12 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8RuntimeException.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * A top-level exception used to indicate that a script failed. In most cases + * a more specific exception will be thrown. + */ +@SuppressWarnings("serial") +public class V8RuntimeException extends RuntimeException { + + V8RuntimeException() { + } + + V8RuntimeException(final String message) { + super(message); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptCompilationException.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptCompilationException.java new file mode 100644 index 000000000..30fcf1447 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptCompilationException.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * An exception used to indicate that a script failed to compile. + */ +@SuppressWarnings("serial") +public class V8ScriptCompilationException extends V8ScriptException { + + public V8ScriptCompilationException(final String fileName, final int lineNumber, + final String message, final String sourceLine, final int startColumn, final int endColumn) { + super(fileName, lineNumber, message, sourceLine, startColumn, endColumn, null, null); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptException.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptException.java new file mode 100644 index 000000000..aa59be0bf --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptException.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * An exception that indicates that the execution of a script failed. + * Details about the exception, such as the line number, stack trace, and + * message can retrieved using the accessors. + */ +@SuppressWarnings("serial") +public abstract class V8ScriptException extends V8RuntimeException { + + private final String fileName; + private final int lineNumber; + private final String jsMessage; + private final String sourceLine; + private final int startColumn; + private final int endColumn; + private final String jsStackTrace; + + V8ScriptException(final String fileName, + final int lineNumber, + final String jsMessage, + final String sourceLine, + final int startColumn, + final int endColumn, + final String jsStackTrace, + final Throwable cause) { + this.fileName = fileName; + this.lineNumber = lineNumber; + this.jsMessage = jsMessage; + this.sourceLine = sourceLine; + this.startColumn = startColumn; + this.endColumn = endColumn; + this.jsStackTrace = jsStackTrace; + if (cause != null) { + initCause(cause); + } + } + + /** + * Get the JavaScript Stack as a String. + * + * @return The JavaScript stack. + */ + public String getJSStackTrace() { + return jsStackTrace; + } + + /** + * Get the file name contains the script that was currently executing. + * + * @return The file name that contains the script. + */ + public String getFileName() { + return fileName; + } + + /** + * Get the line number that the failure occurred on. + * + * @return The line number the failure occurred on. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * Get the JavaScript column where the error begins. + * + * @return The JavaScript column where the error begins. + */ + public int getStartColumn() { + return startColumn; + } + + /** + * Get the JavaScript column where the error ends. + * + * @return The JavaScript column where the error ends. + */ + public int getEndColumn() { + return endColumn; + } + + /** + * Get the JavaScript line of source that caused the error. + * + * @return The JavaScript line of source that caused the error. + */ + public String getSourceLine() { + return sourceLine; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(createMessageLine()); + result.append(createMessageDetails()); + result.append(createJSStackDetails()); + result.append("\n"); + result.append(this.getClass().getName()); + return result.toString(); + } + + /* + * (non-Javadoc) + * @see java.lang.Throwable#getMessage() + */ + @Override + public String getMessage() { + return createMessageLine(); + } + + /** + * Get the message set by the JavaScript exception. + * + * @return The message set by the JavaScript exception. + */ + public String getJSMessage() { + return jsMessage; + } + + private String createMessageLine() { + return fileName + ":" + lineNumber + ": " + jsMessage; + } + + private String createJSStackDetails() { + if (jsStackTrace != null) { + return "\n" + jsStackTrace; + } + return ""; + } + + private String createMessageDetails() { + StringBuilder result = new StringBuilder(); + if ((sourceLine != null) && !sourceLine.isEmpty()) { + result.append('\n'); + result.append(sourceLine); + result.append('\n'); + if (startColumn >= 0) { + result.append(createCharSequence(startColumn, ' ')); + result.append(createCharSequence(endColumn - startColumn, '^')); + } + } + return result.toString(); + } + + private char[] createCharSequence(final int length, final char c) { + char[] result = new char[length]; + for (int i = 0; i < length; i++) { + result[i] = c; + } + return result; + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptExecutionException.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptExecutionException.java new file mode 100644 index 000000000..e320f8a2d --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8ScriptExecutionException.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * An exception used to indicate that a script failed to execute. + */ +@SuppressWarnings("serial") +public class V8ScriptExecutionException extends V8ScriptException { + + V8ScriptExecutionException(final String fileName, + final int lineNumber, + final String message, + final String sourceLine, + final int startColumn, + final int endColumn, + final String jsStackTrace) { + this(fileName, lineNumber, message, sourceLine, startColumn, endColumn, jsStackTrace, null); + } + + public V8ScriptExecutionException(final String fileName, + final int lineNumber, + final String message, + final String sourceLine, + final int startColumn, + final int endColumn, + final String jsStackTrace, + final Throwable cause) { + super(fileName, lineNumber, message, sourceLine, startColumn, endColumn, jsStackTrace, cause); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8TypedArray.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8TypedArray.java new file mode 100644 index 000000000..423f9a71a --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8TypedArray.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * 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; + +/** + * A representation of a JS TypedArray in Java. The typed array is simply a 'view' onto + * a back buffer. + */ +public class V8TypedArray extends V8Array { + + /** + * Create a new TypedArray from a specified ArrayBuffer, type, offset and size. For + * example, a V8Int32Array is a typed array where each value is a 32-bit integer. The + * typed array is backed by the V8ArrayBuffer. + * + * @param v8 The V8Runtime on which to create this Int32Array + * @param type The type of Array to create. Currently Integer and Byte are supported. + * @param buffer The buffer used to back the typed array + * @param offset The offset into the buffer at which to start the the array + * @param size The size of the typed array + */ + public V8TypedArray(final V8 v8, final V8ArrayBuffer buffer, final int type, final int offset, final int size) { + super(v8, new V8ArrayData(buffer, offset, size, type)); + } + + private V8TypedArray(final V8 v8) { + 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. + * + * @return The V8ArrayBuffer used to back this TypedArray. + */ + public V8ArrayBuffer getBuffer() { + return (V8ArrayBuffer) get("buffer"); + } + + @Override + protected void initialize(final long runtimePtr, final Object data) { + v8.checkThread(); + if (data == null) { + super.initialize(runtimePtr, data); + return; + } + V8ArrayData arrayData = (V8ArrayData) data; + checkArrayProperties(arrayData); + long handle = createTypedArray(runtimePtr, arrayData); + released = false; + addObjectReference(handle); + } + + private long createTypedArray(final long runtimePtr, final V8ArrayData arrayData) { + switch (arrayData.type) { + case V8Value.FLOAT_32_ARRAY: + return v8.initNewV8Float32Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + case V8Value.FLOAT_64_ARRAY: + return v8.initNewV8Float64Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + case V8Value.UNSIGNED_INT_32_ARRAY: + return v8.initNewV8UInt32Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + case V8Value.INT_16_ARRAY: + return v8.initNewV8Int16Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + case V8Value.UNSIGNED_INT_16_ARRAY: + return v8.initNewV8UInt16Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + case V8Value.INTEGER: + return v8.initNewV8Int32Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + case V8Value.UNSIGNED_INT_8_ARRAY: + return v8.initNewV8UInt8Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + case V8Value.INT_8_ARRAY: + return v8.initNewV8Int8Array(runtimePtr, arrayData.buffer.objectHandle, arrayData.offset, arrayData.size); + 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.getStringRepresentation(arrayData.type)); + } + } + + /** + * Computes the size of the structures required for each TypedArray variation. + * + * @param type The type of the TypeArray + * @return The size of the structures required + */ + public static int getStructureSize(final int type) { + switch (type) { + case V8Value.FLOAT_64_ARRAY: + return 8; + case V8Value.INT_32_ARRAY: + case V8Value.UNSIGNED_INT_32_ARRAY: + case V8Value.FLOAT_32_ARRAY: + return 4; + case V8Value.UNSIGNED_INT_16_ARRAY: + case V8Value.INT_16_ARRAY: + return 2; + case V8Value.INT_8_ARRAY: + case V8Value.UNSIGNED_INT_8_ARRAY: + case V8Value.UNSIGNED_INT_8_CLAMPED_ARRAY: + return 1; + default: + throw new IllegalArgumentException("Cannot create a typed array of type " + V8Value.getStringRepresentation(type)); + } + } + + private void checkArrayProperties(final V8ArrayData arrayData) { + checkOffset(arrayData); + checkSize(arrayData); + } + + private void checkSize(final V8ArrayData arrayData) { + if (arrayData.size < 0) { + throw new IllegalStateException("RangeError: Invalid typed array length"); + } + int limit = (arrayData.size * getStructureSize(arrayData.type)) + arrayData.offset; + if (limit > arrayData.buffer.limit()) { + throw new IllegalStateException("RangeError: Invalid typed array length"); + } + } + + private void checkOffset(final V8ArrayData arrayData) { + if ((arrayData.offset % getStructureSize(arrayData.type)) != 0) { + throw new IllegalStateException("RangeError: Start offset of Int32Array must be a multiple of " + getStructureSize(arrayData.type)); + } + } + + private static class V8ArrayData { + private V8ArrayBuffer buffer; + private int offset; + private int size; + private int type; + + public V8ArrayData(final V8ArrayBuffer buffer, final int offset, final int size, final int type) { + this.buffer = buffer; + this.offset = offset; + this.size = size; + this.type = type; + } + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Value.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Value.java new file mode 100644 index 000000000..533410b95 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/V8Value.java @@ -0,0 +1,393 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +/** + * A base class for all V8 resources. V8 resources must + * be closed/released. The rules for releasing resources is as + * follows: + *

+ * 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 close it. + */ +abstract public class V8Value implements Releasable { + + public static final int NULL = 0; + public static final int UNKNOWN = 0; + public static final int INTEGER = 1; + public static final int INT_32_ARRAY = 1; + public static final int DOUBLE = 2; + public static final int FLOAT_64_ARRAY = 2; + public static final int BOOLEAN = 3; + public static final int STRING = 4; + public static final int V8_ARRAY = 5; + public static final int V8_OBJECT = 6; + public static final int V8_FUNCTION = 7; + public static final int V8_TYPED_ARRAY = 8; + public static final int BYTE = 9; + public static final int INT_8_ARRAY = 9; + public static final int V8_ARRAY_BUFFER = 10; + public static final int UNSIGNED_INT_8_ARRAY = 11; + public static final int UNSIGNED_INT_8_CLAMPED_ARRAY = 12; + public static final int INT_16_ARRAY = 13; + public static final int UNSIGNED_INT_16_ARRAY = 14; + public static final int UNSIGNED_INT_32_ARRAY = 15; + public static final int FLOAT_32_ARRAY = 16; + public static final int UNDEFINED = 99; + + protected V8 v8; + protected long objectHandle; + protected boolean released = true; + + protected V8Value() { + super(); + } + + protected V8Value(final V8 v8) { + if (v8 == null) { + this.v8 = (V8) this; + } else { + this.v8 = v8; + } + } + + protected void initialize(final long runtimePtr, final Object data) { + long objectHandle = v8.initNewV8Object(runtimePtr); + released = false; + addObjectReference(objectHandle); + } + + protected void addObjectReference(final long objectHandle) throws Error { + this.objectHandle = objectHandle; + try { + v8.addObjRef(this); + } catch (Error e) { + release(); + throw e; + } catch (RuntimeException e) { + release(); + throw e; + } + } + + + /** + * 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"; + case INTEGER: + return "Integer"; + case DOUBLE: + return "Double"; + case BOOLEAN: + return "Boolean"; + case STRING: + return "String"; + case V8_ARRAY: + return "V8Array"; + case V8_OBJECT: + return "V8Object"; + case V8_FUNCTION: + return "V8Function"; + case V8_TYPED_ARRAY: + return "V8TypedArray"; + case BYTE: + return "Byte"; + case V8_ARRAY_BUFFER: + return "V8ArrayBuffer"; + case UNSIGNED_INT_8_ARRAY: + return "UInt8Array"; + case UNSIGNED_INT_8_CLAMPED_ARRAY: + return "UInt8ClampedArray"; + case INT_16_ARRAY: + return "Int16Array"; + case UNSIGNED_INT_16_ARRAY: + return "UInt16Array"; + case UNSIGNED_INT_32_ARRAY: + return "UInt32Array"; + case FLOAT_32_ARRAY: + return "Float32Array"; + case UNDEFINED: + return "Undefined"; + default: + throw new IllegalArgumentException("Invalid V8 type: " + type); + } + } + + /** + * Returns a constructor name of the V8 Value. + * + * @return The V8Value constructor name as a string. + */ + public String getConstructorName() { + v8.checkThread(); + v8.checkReleased(); + return v8.getConstructorName(v8.getV8RuntimePtr(), objectHandle); + } + + /** + * Determines if this value is undefined. + * + * @return Returns true if the value is undefined, false otherwise + */ + public boolean isUndefined() { + return false; + } + + /** + * Gets the runtime this Value was created on. + * + * @return Returns the V8 runtime this value is associated with. + */ + public V8 getRuntime() { + 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 + * changing existing ones) then both the original and twin + * will be updated. Twins are .equal and .strict equals, but + * not == in Java. + *

+ * Twins must be closed separately since they have their own + * native resources. + * + * @return A new Java object pointing at the same V8 Value + * as this. + */ + public V8Value twin() { + if (isUndefined()) { + return this; + } + v8.checkThread(); + v8.checkReleased(); + V8Value twin = createTwin(); + v8.createTwin(this, twin); + return twin; + } + + /** + * 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 close() { + v8.checkThread(); + if (!released) { + try { + v8.releaseObjRef(this); + } finally { + released = true; + v8.release(v8.getV8RuntimePtr(), objectHandle); + } + } + } + + /** + * 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. + * + * @return Returns true if this object has been released, false otherwise. + */ + public boolean isReleased() { + return released; + } + + /** + * Performs a JS === on the parameter and the receiver. + * + * @param that The Object to compare this object against. + * @return Returns true iff this === that + */ + public boolean strictEquals(final Object that) { + v8.checkThread(); + checkReleased(); + if (that == this) { + return true; + } + if (that == null) { + return false; + } + if (!(that instanceof V8Value)) { + return false; + } + if (isUndefined() && ((V8Value) that).isUndefined()) { + return true; + } + if (((V8Value) that).isUndefined()) { + return false; + } + return v8.strictEquals(v8.getV8RuntimePtr(), getHandle(), ((V8Value) that).getHandle()); + } + + protected long getHandle() { + checkReleased(); + return objectHandle; + } + + protected abstract V8Value createTwin(); + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object that) { + return strictEquals(that); + } + + /** + * Performs a JS == on the parameter and the receiver. + * + * @param that The Object to compare this object against. + * @return Returns true iff this == that + */ + public boolean jsEquals(final Object that) { + v8.checkThread(); + checkReleased(); + if (that == this) { + return true; + } + if (that == null) { + return false; + } + if (!(that instanceof V8Value)) { + return false; + } + if (isUndefined() && ((V8Value) that).isUndefined()) { + return true; + } + if (((V8Value) that).isUndefined()) { + return false; + } + return v8.equals(v8.getV8RuntimePtr(), getHandle(), ((V8Value) that).getHandle()); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + v8.checkThread(); + checkReleased(); + return v8.identityHash(v8.getV8RuntimePtr(), getHandle()); + } + + protected void checkReleased() { + if (released) { + throw new IllegalStateException("Object released"); + } + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/ArrayBuffer.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/ArrayBuffer.java new file mode 100644 index 000000000..d1a39b8ec --- /dev/null +++ b/fine-j2v8/src/main/java/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/main/java/com/eclipsesource/v8/utils/ConcurrentV8.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/ConcurrentV8.java new file mode 100644 index 000000000..34b3be25b --- /dev/null +++ b/fine-j2v8/src/main/java/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 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/main/java/com/eclipsesource/v8/utils/MemoryManager.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/MemoryManager.java new file mode 100644 index 000000000..7f236ae56 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/MemoryManager.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * 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; + +import java.util.ArrayList; +import java.util.Iterator; + +import com.eclipsesource.v8.ReferenceHandler; +import com.eclipsesource.v8.V8; +import com.eclipsesource.v8.V8Value; + +/** + * A memory manager that tracks all V8 Handles while the object is registered. + * Once released, all V8 handles that were created while the memory manager + * was active, will be released. + * + * It is important that no V8 handles (V8Objects, V8Arrays, etc...) that are + * created while the memory manager is active, are persisted. + * + */ +public class MemoryManager { + + private MemoryManagerReferenceHandler memoryManagerReferenceHandler; + private V8 v8; + private ArrayList references = new ArrayList(); + private boolean releasing = false; + private boolean released = false; + + /** + * Creates and registered a Memory Manager. After this, all V8 handles will be + * tracked until release is called. + * + * @param v8 The V8 runtime to register this Memory Manager on + */ + public MemoryManager(final V8 v8) { + this.v8 = v8; + memoryManagerReferenceHandler = new MemoryManagerReferenceHandler(); + v8.addReferenceHandler(memoryManagerReferenceHandler); + } + + /** + * Returns the number of handles currently being tracked by this + * memory manager. + * + * Throws an IllegalStateException if the memory manager is used after it's + * been released. + * + * @return The object reference count + */ + public int getObjectReferenceCount() { + checkReleased(); + return references.size(); + } + + /** + * Persist an object that is currently being managed by this Manager. + * + * Objects that are being managed by a MemoryManager will be released + * once the MemoryManager is released. If an object is persisted, it will + * be remove from the MemoryManager's control and therefore will not + * be released. + * + * @param object The object to persist + */ + public void persist(final V8Value object) { + v8.getLocker().checkThread(); + checkReleased(); + references.remove(object); + } + + /** + * Checks if the memory manager has been released or not. Released memory + * managers can no longer be used. + * + * @return True if this memory manager has been released, false otherwise. + */ + public boolean isReleased() { + return released; + } + + /** + * Releases this Memory Manager and all V8Objects that were created while + * this memory manager was active. + */ + public void release() { + v8.getLocker().checkThread(); + if (released) { + return; + } + releasing = true; + try { + for (V8Value reference : references) { + reference.close(); + } + v8.removeReferenceHandler(memoryManagerReferenceHandler); + references.clear(); + } finally { + releasing = false; + } + released = true; + } + + private void checkReleased() { + if (released) { + throw new IllegalStateException("Memory manager released"); + } + } + + private class MemoryManagerReferenceHandler implements ReferenceHandler { + + @Override + public void v8HandleCreated(final V8Value object) { + references.add(object); + } + + @Override + public void v8HandleDisposed(final V8Value object) { + if (!releasing) { + Iterator iterator = references.iterator(); + while (iterator.hasNext()) { + if (iterator.next() == object) { + iterator.remove(); + return; + } + } + } + } + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/SingleTypeAdapter.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/SingleTypeAdapter.java new file mode 100644 index 000000000..d8efca58d --- /dev/null +++ b/fine-j2v8/src/main/java/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/main/java/com/eclipsesource/v8/utils/TypeAdapter.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/TypeAdapter.java new file mode 100644 index 000000000..7bae5eb2f --- /dev/null +++ b/fine-j2v8/src/main/java/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/main/java/com/eclipsesource/v8/utils/TypedArray.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/TypedArray.java new file mode 100644 index 000000000..1c1616ab7 --- /dev/null +++ b/fine-j2v8/src/main/java/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/main/java/com/eclipsesource/v8/utils/V8Executor.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Executor.java new file mode 100644 index 000000000..1227bcb92 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Executor.java @@ -0,0 +1,253 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +import java.util.LinkedList; + +import com.eclipsesource.v8.JavaVoidCallback; +import com.eclipsesource.v8.Releasable; +import com.eclipsesource.v8.V8; +import com.eclipsesource.v8.V8Array; +import com.eclipsesource.v8.V8Object; + +/** + * Executes a JS Script on a new V8 runtime in its own thread, and once finished, + * will optionally wait on a message queue. If the executor is *not* long running, + * when the JS Script finishes, the executor will shutdown. If the executor + * *is* long running, the the script will execute, and when finished, the executor + * will wait for messages to arrive. When messages arrive, the messageHandler + * will be invoked with the contents of the message. + *

+ * Executors can be shutdown in two different ways. forceTermination() will + * stop any executing scripts and immediately terminate the executor. shutdown() + * will indicate that the executor should shutdown, but this will only happen + * once any scripts finish executing and the message queue becomes empty. + */ +public class V8Executor extends Thread { + + private final String script; + private V8 runtime; + private String result; + private volatile boolean terminated = false; + private volatile boolean shuttingDown = false; + private volatile boolean forceTerminating = false; + private Exception exception = null; + private LinkedList messageQueue = new LinkedList(); + private boolean longRunning; + private String messageHandler; + + /** + * Create a new executor and execute the given script on it. Once + * the script has finished executing, the executor can optionally + * wait on a message queue. + * + * @param script The script to execute on this executor. + * @param longRunning True to indicate that this executor should be longRunning. + * @param messageHandler The name of the message handler that should be notified + * when messages are delivered. + */ + public V8Executor(final String script, final boolean longRunning, final String messageHandler) { + this.script = script; + this.longRunning = longRunning; + this.messageHandler = messageHandler; + } + + /** + * Create a new executor and execute the given script on it. + * + * @param script The script to execute on this executor. + */ + public V8Executor(final String script) { + this(script, false, null); + } + + /** + * Override to provide a custom setup for this V8 runtime. + * This method can be overridden to configure the V8 runtime, + * for example, to add callbacks or to add some additional + * functionality to the global scope. + * + * @param runtime The runtime to configure. + */ + protected void setup(final V8 runtime) { + + } + + /** + * Gets the result of the JavaScript that was executed + * on this executor. + * + * @return The result of the JS Script that was executed on + * this executor. + */ + public String getResult() { + return result; + } + + /** + * Posts a message to the receiver to be processed by the executor + * and sent to the V8 runtime via the messageHandler. + * + * @param message The message to send to the messageHandler + */ + public void postMessage(final String... message) { + synchronized (this) { + messageQueue.add(message); + notify(); + } + } + + /* + * (non-Javadoc) + * @see java.lang.Thread#run() + */ + @Override + public void run() { + synchronized (this) { + runtime = V8.createV8Runtime(); + runtime.registerJavaMethod(new ExecutorTermination(), "__j2v8__checkThreadTerminate"); + setup(runtime); + } + try { + if (!forceTerminating) { + Object scriptResult = runtime.executeScript("__j2v8__checkThreadTerminate();\n" + script, getName(), -1); + if (scriptResult != null) { + result = scriptResult.toString(); + } + if (scriptResult instanceof Releasable) { + ((Releasable) scriptResult).release(); + } + } + while (!forceTerminating && longRunning) { + synchronized (this) { + if (messageQueue.isEmpty() && !shuttingDown) { + wait(); + } + if ((messageQueue.isEmpty() && shuttingDown) || forceTerminating) { + return; + } + } + if (!messageQueue.isEmpty()) { + String[] message = messageQueue.remove(0); + V8Array parameters = new V8Array(runtime); + V8Array strings = new V8Array(runtime); + try { + for (String string : message) { + strings.push(string); + } + parameters.push(strings); + runtime.executeVoidFunction(messageHandler, parameters); + } finally { + strings.close(); + parameters.close(); + } + } + } + } catch (Exception e) { + exception = e; + } finally { + synchronized (this) { + if (runtime.getLocker().hasLock()) { + runtime.close(); + runtime = null; + } + terminated = true; + } + } + } + + /** + * Determines if an exception was thrown during the JavaScript execution. + * + * @return True if an exception was thrown during the JavaScript execution, + * false otherwise. + */ + public boolean hasException() { + return exception != null; + } + + /** + * Gets the exception that was thrown during the JavaScript execution. + * + * @return The exception that was thrown during the JavaScript execution, + * or null if no such exception was thrown. + */ + public Exception getException() { + return exception; + } + + /** + * Determines if the executor has terminated. + * + * @return True if the executor has terminated, false otherwise. + */ + public boolean hasTerminated() { + return terminated; + } + + /** + * Forces the executor to shutdown immediately. Any currently executing + * JavaScript will be interrupted and all outstanding messages will be + * ignored. + */ + public void forceTermination() { + synchronized (this) { + forceTerminating = true; + shuttingDown = true; + if (runtime != null) { + runtime.terminateExecution(); + } + notify(); + } + } + + /** + * Indicates to the executor that it should shutdown. Any currently + * executing JavaScript will be allowed to finish, and any outstanding + * messages will be processed. Only once the message queue is empty, + * will the executor actually shtutdown. + */ + public void shutdown() { + synchronized (this) { + shuttingDown = true; + notify(); + } + } + + /** + * Returns true if shutdown() or forceTermination() was called to + * shutdown this executor. + * + * @return True if shutdown() or forceTermination() was called, false otherwise. + */ + public boolean isShuttingDown() { + return shuttingDown; + } + + /** + * Returns true if forceTermination was called to shutdown + * this executor. + * + * @return True if forceTermination() was called, false otherwise. + */ + public boolean isTerminating() { + return forceTerminating; + } + + class ExecutorTermination implements JavaVoidCallback { + @Override + public void invoke(final V8Object receiver, final V8Array parameters) { + if (forceTerminating) { + throw new RuntimeException("V8Thread Termination"); + } + } + } +} \ No newline at end of file diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Map.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Map.java new file mode 100644 index 000000000..4e0ddb925 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Map.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.eclipsesource.v8.Releasable; +import com.eclipsesource.v8.V8Value; + +/** + * A Map that maps V8Values to arbitrary Java Objects. + * Once stored in the map, the keys (V8Values) can be released + * as the map will handle their resource management for you. + *

+ * Once the map is no longer needed, it should be released. To + * tie a map to the lifecycle of a V8 runtime, it can be registered + * as a resource with V8.registerResource. + */ +public class V8Map implements Map, Releasable { + + private Map map; + private Map twinMap; + + /** + * Creates a V8Map. + */ + public V8Map() { + map = new HashMap(); + 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() { + close(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#size() + */ + @Override + public int size() { + return map.size(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#isEmpty() + */ + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#containsKey(java.lang.Object) + */ + @Override + public boolean containsKey(final Object key) { + return map.containsKey(key); + } + + /* + * (non-Javadoc) + * @see java.util.Map#containsValue(java.lang.Object) + */ + @Override + public boolean containsValue(final Object value) { + return map.containsValue(value); + } + + /* + * (non-Javadoc) + * @see java.util.Map#get(java.lang.Object) + */ + @Override + public V get(final Object key) { + return map.get(key); + } + + /* + * (non-Javadoc) + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + @Override + public V put(final V8Value key, final V value) { + this.remove(key); + V8Value twin = key.twin(); + twinMap.put(twin, twin); + return map.put(twin, value); + } + + /* + * (non-Javadoc) + * @see java.util.Map#remove(java.lang.Object) + */ + @Override + public V remove(final Object key) { + V result = map.remove(key); + V8Value twin = twinMap.remove(key); + if (twin != null) { + twin.close(); + } + return result; + } + + /* + * (non-Javadoc) + * @see java.util.Map#putAll(java.util.Map) + */ + @Override + public void putAll(final Map m) { + for (Entry entry : m.entrySet()) { + this.put(entry.getKey(), entry.getValue()); + } + } + + /* + * (non-Javadoc) + * @see java.util.Map#clear() + */ + @Override + public void clear() { + map.clear(); + for (V8Value V8Value : twinMap.keySet()) { + V8Value.close(); + } + twinMap.clear(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#keySet() + */ + @Override + public Set keySet() { + return map.keySet(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#values() + */ + @Override + public Collection values() { + return map.values(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#entrySet() + */ + @Override + public Set> entrySet() { + return map.entrySet(); + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8ObjectUtils.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8ObjectUtils.java new file mode 100644 index 000000000..b5edac215 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8ObjectUtils.java @@ -0,0 +1,690 @@ +/******************************************************************************* + * Copyright (c) 2014 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +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; + +/** + * A set of static helper methods to convert V8Objects / V8Arrays to + * java.util Maps and Lists and back again. These conversions + * perform a deep copy. + */ +public class V8ObjectUtils { + + 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 + * 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. + * @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, adapter); + } finally { + cache.close(); + } + } + + /** + * Creates a List from a V8Array using a deep copy. 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. + * @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, adapter); + } finally { + cache.close(); + } + } + + /** + * Populates a Java array from a V8Array. The type of the array must be specified. + * Currently, only INTEGER, DOUBLE, BOOLEAN and STRING are supported. + * The V8Array must only contain elements of type 'arrayType'. The result + * can be optionally passed in as a parameter. + *

+ * This method will use J2V8's bulk array copy making it faster than iterating over + * all the elements in the array. + * + * @param array The V8Array to convert to a Java Array. + * @param arrayType The type of the V8Array to convert. + * @param result The array to use as the result. If null, a new array will be created. + * @return A Java array representing a V8Array. + */ + public static Object getTypedArray(final V8Array array, final int arrayType, final Object result) { + int length = array.length(); + if (arrayType == V8Value.INTEGER) { + int[] intArray = (int[]) result; + if ((intArray == null) || (intArray.length < length)) { + intArray = new int[length]; + } + array.getIntegers(0, length, intArray); + return intArray; + } else if (arrayType == V8Value.DOUBLE) { + double[] doubleArray = (double[]) result; + if ((doubleArray == null) || (doubleArray.length < length)) { + doubleArray = new double[length]; + } + array.getDoubles(0, length, doubleArray); + return doubleArray; + } else if (arrayType == V8Value.BOOLEAN) { + boolean[] booleanArray = (boolean[]) result; + if ((booleanArray == null) || (booleanArray.length < length)) { + booleanArray = new boolean[length]; + } + array.getBooleans(0, length, booleanArray); + return booleanArray; + } else if (arrayType == V8Value.STRING) { + String[] stringArray = (String[]) result; + if ((stringArray == null) || (stringArray.length < length)) { + stringArray = new String[length]; + } + array.getStrings(0, length, stringArray); + return stringArray; + } else if (arrayType == V8Value.BYTE) { + byte[] byteArray = (byte[]) result; + if ((byteArray == null) || (byteArray.length < length)) { + byteArray = new byte[length]; + } + array.getBytes(0, length, byteArray); + return byteArray; + } + throw new RuntimeException("Unsupported bulk load type: " + arrayType); + } + + /** + * Creates a Java array from a V8Array. The type of the Array must be specified. + * Currently, only INTEGER, DOUBLE, BOOLEAN and STRING are supported. + * The V8Array must only contain elements of type 'arrayType'. + *

+ * This method will use J2V8's bulk array copy making it faster than iterating over + * all the elements in the array. + * + * @param array The V8Array to convert to a Java Array. + * @param arrayType The type of the V8Array to convert. + * @return A Java array representing a V8Array. + */ + public static Object getTypedArray(final V8Array array, final int arrayType) { + int length = array.length(); + if (arrayType == V8Value.INTEGER) { + return array.getIntegers(0, length); + } else if (arrayType == V8Value.DOUBLE) { + return array.getDoubles(0, length); + } else if (arrayType == V8Value.BOOLEAN) { + return array.getBooleans(0, length); + } else if (arrayType == V8Value.STRING) { + return array.getStrings(0, length); + } + throw new RuntimeException("Unsupported bulk load type: " + arrayType); + } + + /** + * Creates a V8Object from a java.util.Map. This is a deep copy, so if the map + * contains other maps (or lists) they will also be converted. + * + * @param v8 The runtime on which to create the result. + * @param map The map to convert to a V8Object. + * @return A V8Object representing the map. + */ + public static V8Object toV8Object(final V8 v8, final Map map) { + Map cache = new Hashtable(); + try { + return toV8Object(v8, map, cache).twin(); + } finally { + for (V8Value v8Object : cache.values()) { + v8Object.close(); + } + } + } + + /** + * Creates a V8Array from a java.util.List. This is a deep copy, so if the list + * contains other lists (or maps) they will also be converted. + * + * @param v8 The runtime on which to create the result. + * @param list The list to convert to a V8Array. + * @return A V8Array representing the list. + */ + public static V8Array toV8Array(final V8 v8, final List list) { + Map cache = new Hashtable(); + try { + return toV8Array(v8, list, cache).twin(); + } finally { + for (V8Value v8Object : cache.values()) { + v8Object.close(); + } + } + } + + /** + * Returns an object usable with a V8 Runtime which represents + * the parameter 'value'. If 'value' is an Integer, Boolean, Double + * or String, then 'value' is simply returned as these are directly + * usable on V8. If 'value' is a map / list, then it's converted to + * a V8Object / V8Array first. + *

+ * If the result is a V8Value, it must be released. + * + * @param v8 The runtime on which to create V8Values. + * @param value The value to convert to an object usable with V8 + * @return An object which can be used directly with a V8 runtime. + */ + public static Object getV8Result(final V8 v8, final Object value) { + if (value == null) { + return null; + } + Map cache = new Hashtable(); + try { + Object result = getV8Result(v8, value, cache); + if (result instanceof V8Value) { + return ((V8Value) result).twin(); + } + return result; + } finally { + for (V8Value v8Object : cache.values()) { + v8Object.close(); + } + } + } + + /** + * Pushes a Java Object to a V8Array by first converting it to a V8Value if needed. + * If the value is a boxed primitive, then the primitive will be pushed. If the object + * is a Map / List then a deep copy will be performed, converting the object to a + * V8Object / V8Array first. + * + * @param v8 The runtime on which to create any needed V8Values. + * @param array The array to push the elements to. + * @param value The value to push to the array. + */ + public static void pushValue(final V8 v8, final V8Array array, final Object value) { + Map cache = new Hashtable(); + try { + pushValue(v8, array, value, cache); + } finally { + for (V8Value v8Object : cache.values()) { + v8Object.close(); + } + } + } + + /** + * Gets a Java Object representing the value at the given index in the V8Array. + * 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. + * @return A Java Object representing the value at a given index. + */ + public static Object getValue(final V8Array array, final int index) { + 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, DEFAULT_TYPE_ADAPTER); + if ((result == object) && (result instanceof V8Value)) { + return ((V8Value) result).twin(); + } + return result; + } finally { + 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(); + } + } + + /** + * Gets a Java Object representing the value with the given key in the V8Object. + * 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 object The object on which to lookup the value. The object is not + * released. + * @param key The key to use to lookup the value. + * @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 { + 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 { + if (object instanceof Releasable) { + ((Releasable) object).release(); + } + cache.close(); + } + } + + @SuppressWarnings("unchecked") + private static Map toMap(final V8Object v8Object, final V8Map cache, final TypeAdapter adapter) { + if (v8Object == null) { + return Collections.emptyMap(); + } + if (cache.containsKey(v8Object)) { + return (Map) cache.get(v8Object); + } + Map result = new V8PropertyMap(); + cache.put(v8Object, result); + String[] keys = v8Object.getKeys(); + for (String key : keys) { + 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, final TypeAdapter adapter) { + if (array == null) { + return Collections.emptyList(); + } + if (cache.containsKey(array)) { + return (List) cache.get(array); + } + List result = new ArrayList(); + cache.put(array, result); + for (int i = 0; i < array.length(); i++) { + 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); + } + V8Object result = new V8Object(v8); + cache.put(map, result); + try { + for (Entry entry : map.entrySet()) { + setValue(v8, result, entry.getKey(), entry.getValue(), cache); + } + } catch (IllegalStateException e) { + result.close(); + throw e; + } + return result; + } + + private static V8Array toV8Array(final V8 v8, final List list, final Map cache) { + if (cache.containsKey(new ListWrapper(list))) { + return (V8Array) cache.get(new ListWrapper(list)); + } + V8Array result = new V8Array(v8); + cache.put(new ListWrapper(list), result); + try { + for (int i = 0; i < list.size(); i++) { + Object value = list.get(i); + pushValue(v8, result, value, cache); + } + } catch (IllegalStateException e) { + result.close(); + throw e; + } + return result; + } + + @SuppressWarnings("unchecked") + private static Object getV8Result(final V8 v8, final Object value, final Map cache) { + if (cache.containsKey(value)) { + return cache.get(value); + } + if (value instanceof Map) { + return toV8Object(v8, (Map) value, cache); + } else if (value instanceof List) { + return toV8Array(v8, (List) value, cache); + } else if (value instanceof TypedArray) { + return toV8TypedArray(v8, (TypedArray) value, cache); + } else if (value instanceof ArrayBuffer) { + return toV8ArrayBuffer(v8, (ArrayBuffer) value, cache); + } + return value; + } + + @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(value); + } else if (value instanceof Long) { + result.push(Double.valueOf((Long) value)); + } else if (value instanceof Double) { + result.push(value); + } else if (value instanceof Float) { + result.push(value); + } else if (value instanceof String) { + result.push((String) value); + } else if (value instanceof Boolean) { + 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); + } else if (value instanceof List) { + V8Array array = toV8Array(v8, (List) value, cache); + result.push(array); + } else { + throw new IllegalStateException("Unsupported Object of type: " + value.getClass()); + } + } + + @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, (Long) value); + } else if (value instanceof Double) { + result.add(key, (Double) value); + } else if (value instanceof Float) { + result.add(key, (Float) value); + } else if (value instanceof String) { + result.add(key, (String) value); + } else if (value instanceof Boolean) { + result.add(key, (Boolean) value); + } else if (value instanceof 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); + } else if (value instanceof List) { + V8Array array = toV8Array(v8, (List) value, cache); + result.add(key, array); + } else { + throw new IllegalStateException("Unsupported Object of type: " + value.getClass()); + } + } + + 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; + } + switch (valueType) { + case V8Value.INTEGER: + case V8Value.DOUBLE: + case V8Value.BOOLEAN: + case V8Value.STRING: + return value; + case V8Value.V8_FUNCTION: + return IGNORE; + case V8Value.V8_ARRAY_BUFFER: + return new ArrayBuffer((V8ArrayBuffer) value); + case V8Value.V8_TYPED_ARRAY: + return new TypedArray((V8TypedArray) value); + case V8Value.V8_ARRAY: + return toList((V8Array) value, cache, adapter); + case V8Value.V8_OBJECT: + return toMap((V8Object) value, cache, adapter); + case V8Value.NULL: + return null; + case V8Value.UNDEFINED: + return V8.getUndefined(); + default: + throw new IllegalStateException("Cannot convert type " + V8Value.getStringRepresentation(valueType)); + } + } + + private 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; + + public ListWrapper(final List list) { + this.list = list; + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof ListWrapper) { + return ((ListWrapper) obj).list == list; + } + return false; + } + + @Override + public int hashCode() { + return System.identityHashCode(list); + } + } +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8PropertyMap.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8PropertyMap.java new file mode 100644 index 000000000..a5d159d00 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8PropertyMap.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +/** + * A custom map is needed because the existing HashMaps + * do not allow self containment, and Hashtables do not + * allow nulls as values. + * + * This class is not considered API. + */ +class V8PropertyMap implements Map { + + private Hashtable map = new Hashtable(); + private Set nulls = new HashSet(); + + /* + * (non-Javadoc) + * @see java.util.Map#size() + */ + @Override + public int size() { + return map.size() + nulls.size(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#isEmpty() + */ + @Override + public boolean isEmpty() { + return map.isEmpty() && nulls.isEmpty(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#containsKey(java.lang.Object) + */ + @Override + public boolean containsKey(final Object key) { + return map.containsKey(key) || nulls.contains(key); + } + + /* + * (non-Javadoc) + * @see java.util.Map#containsValue(java.lang.Object) + */ + @Override + public boolean containsValue(final Object value) { + if ((value == null) && !nulls.isEmpty()) { + return true; + } else if (value == null) { + return false; + } + return map.containsValue(value); + } + + /* + * (non-Javadoc) + * @see java.util.Map#get(java.lang.Object) + */ + @Override + public V get(final Object key) { + if (nulls.contains(key)) { + return null; + } + return map.get(key); + } + + /* + * (non-Javadoc) + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + @Override + public V put(final String key, final V value) { + if (value == null) { + if (map.containsKey(key)) { + map.remove(key); + } + nulls.add(key); + return null; + } + if (nulls.contains(key)) { + nulls.remove(key); + } + return map.put(key, value); + } + + /* + * (non-Javadoc) + * @see java.util.Map#remove(java.lang.Object) + */ + @Override + public V remove(final Object key) { + if (nulls.contains(key)) { + nulls.remove(key); + return null; + } + return map.remove(key); + } + + /* + * (non-Javadoc) + * @see java.util.Map#putAll(java.util.Map) + */ + @Override + public void putAll(final Map m) { + for (Entry entry : m.entrySet()) { + this.put(entry.getKey(), entry.getValue()); + } + } + + /* + * (non-Javadoc) + * @see java.util.Map#clear() + */ + @Override + public void clear() { + map.clear(); + nulls.clear(); + } + + /* + * (non-Javadoc) + * @see java.util.Map#keySet() + */ + @Override + public Set keySet() { + HashSet result = new HashSet(map.keySet()); + result.addAll(nulls); + return result; + } + + /* + * (non-Javadoc) + * @see java.util.Map#values() + */ + @Override + public Collection values() { + ArrayList result = new ArrayList(map.values()); + for (int i = 0; i < nulls.size(); i++) { + result.add(null); + } + return result; + } + + /* + * (non-Javadoc) + * @see java.util.Map#entrySet() + */ + @Override + public Set> entrySet() { + HashSet> result = new HashSet>(map.entrySet()); + for (String nullKey : nulls) { + result.add(new SimpleEntry(nullKey, null)); + } + return result; + } + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Runnable.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Runnable.java new file mode 100644 index 000000000..7df197a5b --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Runnable.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +import com.eclipsesource.v8.V8; + +/** + * Classes can implement this interface to execute arbitrary code on + * isolated V8 runtime on its own thread. Instances of classes that + * implement this interface can be passed to V8Thread. + */ +public interface V8Runnable { + + /** + * Execute the code on the provided runtime. + * + * @param runtime The V8 runtime assigned to this runnable. + */ + public void run(final V8 runtime); + +} diff --git a/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Thread.java b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Thread.java new file mode 100644 index 000000000..0233ffb65 --- /dev/null +++ b/fine-j2v8/src/main/java/com/eclipsesource/v8/utils/V8Thread.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015 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; + +import com.eclipsesource.v8.V8; + +/** + * A Thread with its own V8 runtime. The thread will create a runtime, + * and execute a runnable on that runtime. When the thread ends, + * the runtime will be released. + * + * It's suggested that you *DO NOT* release the lock on the runtime. + * If the lock is released, you will need to ensure that the runtime + * is properly released. + */ +public class V8Thread extends Thread { + + private final V8Runnable target; + private V8 runtime; + + /** + * Create as new Thread with its own V8Runtime. + * + * @param target The code to execute with the given runtime. + */ + public V8Thread(final V8Runnable target) { + this.target = target; + } + + /* + * (non-Javadoc) + * @see java.lang.Thread#run() + */ + @Override + public void run() { + runtime = V8.createV8Runtime(); + try { + target.run(runtime); + } finally { + synchronized (this) { + if (runtime.getLocker().hasLock()) { + runtime.close(); + runtime = null; + } + } + } + } + +} \ No newline at end of file diff --git a/fine-j2v8/src/main/resources/libj2v8-linux-x86_64.so b/fine-j2v8/src/main/resources/libj2v8-linux-x86_64.so new file mode 100644 index 000000000..19c95ad78 Binary files /dev/null and b/fine-j2v8/src/main/resources/libj2v8-linux-x86_64.so differ diff --git a/fine-j2v8/src/main/resources/libj2v8-macosx-aarch_64.dylib b/fine-j2v8/src/main/resources/libj2v8-macosx-aarch_64.dylib new file mode 100644 index 000000000..d38c3d088 Binary files /dev/null and b/fine-j2v8/src/main/resources/libj2v8-macosx-aarch_64.dylib differ diff --git a/fine-j2v8/src/main/resources/libj2v8-macosx-x86_64.dylib b/fine-j2v8/src/main/resources/libj2v8-macosx-x86_64.dylib new file mode 100644 index 000000000..f12bbafa6 Binary files /dev/null and b/fine-j2v8/src/main/resources/libj2v8-macosx-x86_64.dylib differ diff --git a/fine-j2v8/src/main/resources/libj2v8-windows-x86_64.dll b/fine-j2v8/src/main/resources/libj2v8-windows-x86_64.dll new file mode 100644 index 000000000..bcb827091 Binary files /dev/null and b/fine-j2v8/src/main/resources/libj2v8-windows-x86_64.dll differ