aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlana <none@none>2014-04-16 15:05:39 -0700
committerlana <none@none>2014-04-16 15:05:39 -0700
commit596d50091a45d4eefd891c53e57104c9c088625e (patch)
tree15614e20d22cd183a94045725297e0a3bede20b2
parent8a970c519c72143c7fca1b9a276d650d922f20a6 (diff)
parent21a127ef32c5634863b2bd971f75f2540ebf65a2 (diff)
-rw-r--r--src/jdk/internal/dynalink/beans/BeanLinker.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java37
-rw-r--r--src/jdk/nashorn/internal/codegen/MapCreator.java4
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/AccessorProperty.java38
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeInstaller.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeStore.java179
-rw-r--r--src/jdk/nashorn/internal/runtime/CompiledScript.java127
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java142
-rw-r--r--src/jdk/nashorn/internal/runtime/JSType.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/Property.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyMap.java54
-rw-r--r--src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java105
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptEnvironment.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunctionData.java6
-rw-r--r--src/jdk/nashorn/internal/runtime/Source.java39
-rw-r--r--src/jdk/nashorn/internal/runtime/UserAccessorProperty.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Options.properties8
-rw-r--r--test/script/basic/JDK-8030199.js50
-rw-r--r--test/script/basic/JDK-8030199.js.EXPECTED14
-rw-r--r--test/script/basic/JDK-8030200.js36
-rw-r--r--test/script/basic/JDK-8030200.js.EXPECTED3
-rw-r--r--test/script/basic/JDK-8039387.js39
-rw-r--r--test/script/basic/JDK-8039387.js.EXPECTED4
-rw-r--r--test/script/basic/NASHORN-173.js.EXPECTED2
-rw-r--r--test/script/basic/list.js2
-rw-r--r--test/script/basic/list.js.EXPECTED2
-rw-r--r--test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java159
-rw-r--r--test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java135
30 files changed, 1139 insertions, 96 deletions
diff --git a/src/jdk/internal/dynalink/beans/BeanLinker.java b/src/jdk/internal/dynalink/beans/BeanLinker.java
index 519036b3..d0ad96ba 100644
--- a/src/jdk/internal/dynalink/beans/BeanLinker.java
+++ b/src/jdk/internal/dynalink/beans/BeanLinker.java
@@ -113,6 +113,8 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
// explicit property is beneficial for them.
// REVISIT: is it maybe a code smell that "dyn:getLength" is not needed?
setPropertyGetter("length", GET_ARRAY_LENGTH, ValidationType.IS_ARRAY);
+ } else if(List.class.isAssignableFrom(clazz)) {
+ setPropertyGetter("length", GET_COLLECTION_LENGTH, ValidationType.INSTANCE_OF);
}
}
diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index e0ca67a4..9cf0adb9 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -3228,7 +3228,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
final String className = SCRIPTFUNCTION_IMPL_OBJECT;
final int fieldCount = ObjectClassGenerator.getPaddedFieldCount(functionNode.countThisProperties());
final String allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount));
- final PropertyMap allocatorMap = PropertyMap.newMap(null, 0, fieldCount, 0);
+ final PropertyMap allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, fieldCount, 0);
method._new(className).dup();
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), allocatorClassName, allocatorMap));
diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java
index b53b8fdf..e4af587b 100644
--- a/src/jdk/nashorn/internal/codegen/Compiler.java
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java
@@ -405,32 +405,8 @@ public final class Compiler {
return newFunctionNode;
}
- private Class<?> install(final String className, final byte[] code) {
- LOG.fine("Installing class ", className);
-
- final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
-
- try {
- final Object[] constants = getConstantData().toArray();
- // Need doPrivileged because these fields are private
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
- @Override
- public Void run() throws Exception {
- //use reflection to write source and constants table to installed classes
- final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
- final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
- sourceField.setAccessible(true);
- constantsField.setAccessible(true);
- sourceField.set(null, source);
- constantsField.set(null, constants);
- return null;
- }
- });
- } catch (final PrivilegedActionException e) {
- throw new RuntimeException(e);
- }
-
- return clazz;
+ private Class<?> install(final String className, final byte[] code, final Object[] constants) {
+ return installer.install(className, code, source, constants);
}
/**
@@ -444,10 +420,15 @@ public final class Compiler {
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
final Map<String, Class<?>> installedClasses = new HashMap<>();
+ final Object[] constants = getConstantData().toArray();
final String rootClassName = firstCompileUnitName();
final byte[] rootByteCode = bytecode.get(rootClassName);
- final Class<?> rootClass = install(rootClassName, rootByteCode);
+ final Class<?> rootClass = install(rootClassName, rootByteCode, constants);
+
+ if (!isLazy()) {
+ installer.storeCompiledScript(source, rootClassName, bytecode, constants);
+ }
int length = rootByteCode.length;
@@ -461,7 +442,7 @@ public final class Compiler {
final byte[] code = entry.getValue();
length += code.length;
- installedClasses.put(className, install(className, code));
+ installedClasses.put(className, install(className, code, constants));
}
for (final CompileUnit unit : compileUnits) {
diff --git a/src/jdk/nashorn/internal/codegen/MapCreator.java b/src/jdk/nashorn/internal/codegen/MapCreator.java
index 8012adf5..436622bd 100644
--- a/src/jdk/nashorn/internal/codegen/MapCreator.java
+++ b/src/jdk/nashorn/internal/codegen/MapCreator.java
@@ -83,7 +83,7 @@ public class MapCreator {
}
}
- return PropertyMap.newMap(properties, fieldCount, fieldMaximum, 0);
+ return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0);
}
PropertyMap makeSpillMap(final boolean hasArguments) {
@@ -100,7 +100,7 @@ public class MapCreator {
}
}
- return PropertyMap.newMap(properties, 0, 0, spillIndex);
+ return PropertyMap.newMap(properties, structure.getName(), 0, 0, spillIndex);
}
/**
diff --git a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
index d757ee3a..d69adc10 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
@@ -89,6 +89,15 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
}
@Override
+ protected void setImpl(final int index, final long value) {
+ if (JSType.isRepresentableAsInt(value)) {
+ setImpl(index, (int)value);
+ } else {
+ buffer.getByteArray()[byteIndex(index)] = value > 0 ? (byte)0xff : 0;
+ }
+ }
+
+ @Override
protected void setImpl(final int key, final double value) {
setImpl(key, (int)Math.rint(value));
}
diff --git a/src/jdk/nashorn/internal/runtime/AccessorProperty.java b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
index d22a9567..e8ff153d 100644
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
@@ -39,6 +39,9 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getNumberOfAcces
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -51,11 +54,12 @@ import jdk.nashorn.internal.lookup.MethodHandleFactory;
* An AccessorProperty is the most generic property type. An AccessorProperty is
* represented as fields in a ScriptObject class.
*/
-public final class AccessorProperty extends Property {
+public final class AccessorProperty extends Property implements Serializable {
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class);
private static final int NOOF_TYPES = getNumberOfAccessorTypes();
+ private static final long serialVersionUID = 3371720170182154920L;
/**
* Properties in different maps for the same structure class will share their field getters and setters. This could
@@ -71,7 +75,7 @@ public final class AccessorProperty extends Property {
};
/** Property getter cache */
- private MethodHandle[] getters = new MethodHandle[NOOF_TYPES];
+ private transient MethodHandle[] getters = new MethodHandle[NOOF_TYPES];
private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES];
private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES];
@@ -122,16 +126,16 @@ public final class AccessorProperty extends Property {
}
/** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
- private MethodHandle primitiveGetter;
+ private transient MethodHandle primitiveGetter;
/** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
- private MethodHandle primitiveSetter;
+ private transient MethodHandle primitiveSetter;
/** Seed getter for the Object version of this field */
- private MethodHandle objectGetter;
+ private transient MethodHandle objectGetter;
/** Seed setter for the Object version of this field */
- private MethodHandle objectSetter;
+ private transient MethodHandle objectSetter;
/**
* Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
@@ -243,6 +247,12 @@ public final class AccessorProperty extends Property {
public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) {
super(key, flags, slot);
+ initGetterSetter(structure);
+ }
+
+ private void initGetterSetter(final Class<?> structure) {
+ final int slot = getSlot();
+ final String key = getKey();
/*
* primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also
* works in dual field mode, it only means that the property never has a primitive
@@ -305,6 +315,12 @@ public final class AccessorProperty extends Property {
setCurrentType(property.getCurrentType());
}
+ private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ // Restore getters array
+ getters = new MethodHandle[NOOF_TYPES];
+ }
+
private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) {
if (mh == null) {
return null;
@@ -364,6 +380,16 @@ public final class AccessorProperty extends Property {
}
@Override
+ void initMethodHandles(final Class<?> structure) {
+ if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) {
+ throw new IllegalArgumentException();
+ }
+ if (!isSpill()) {
+ initGetterSetter(structure);
+ }
+ }
+
+ @Override
public MethodHandle getGetter(final Class<?> type) {
final int i = getAccessorTypeIndex(type);
ensureObjectGetter();
diff --git a/src/jdk/nashorn/internal/runtime/CodeInstaller.java b/src/jdk/nashorn/internal/runtime/CodeInstaller.java
index 28c9630e..579b6f3e 100644
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java
+++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.runtime;
+import java.util.Map;
import jdk.nashorn.internal.codegen.ClassEmitter;
/**
@@ -52,7 +53,7 @@ public interface CodeInstaller<T> {
* @param bytecode bytecode
* @return the installed class
*/
- public Class<?> install(final String className, final byte[] bytecode);
+ public Class<?> install(final String className, final byte[] bytecode, final Source source, final Object[] constants);
/**
* Verify generated bytecode before emission. This is called back from the
@@ -74,4 +75,13 @@ public interface CodeInstaller<T> {
* @return unique eval id
*/
public long getUniqueEvalId();
+
+ /**
+ * Store a compiled script for later reuse
+ * @param source the script source
+ * @param mainClassName the main class name
+ * @param classBytes map of class names to class bytes
+ * @param constants constants array
+ */
+ public void storeCompiledScript(Source source, String mainClassName, Map<String, byte[]> classBytes, Object[] constants);
}
diff --git a/src/jdk/nashorn/internal/runtime/CodeStore.java b/src/jdk/nashorn/internal/runtime/CodeStore.java
new file mode 100644
index 00000000..8f595999
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/CodeStore.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Base64;
+import java.util.Map;
+
+/**
+ * A code cache for persistent caching of compiled scripts.
+ */
+final class CodeStore {
+
+ private final File dir;
+ private final int minSize;
+
+ // Message digest to file name encoder
+ private final static Base64.Encoder BASE64 = Base64.getUrlEncoder().withoutPadding();
+
+ // Default minimum size for storing a compiled script class
+ private final static int DEFAULT_MIN_SIZE = 1000;
+
+ /**
+ * Constructor
+ * @param path directory to store code in
+ * @throws IOException
+ */
+ public CodeStore(final String path) throws IOException {
+ this(path, DEFAULT_MIN_SIZE);
+ }
+
+ /**
+ * Constructor
+ * @param path directory to store code in
+ * @param minSize minimum file size for caching scripts
+ * @throws IOException
+ */
+ public CodeStore(final String path, final int minSize) throws IOException {
+ this.dir = checkDirectory(path);
+ this.minSize = minSize;
+ }
+
+ private static File checkDirectory(final String path) throws IOException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
+ @Override
+ public File run() throws IOException {
+ final File dir = new File(path).getAbsoluteFile();
+ if (!dir.exists() && !dir.mkdirs()) {
+ throw new IOException("Could not create directory: " + dir);
+ } else if (!dir.isDirectory()) {
+ throw new IOException("Not a directory: " + dir);
+ } else if (!dir.canRead() || !dir.canWrite()) {
+ throw new IOException("Directory not readable or writable: " + dir);
+ }
+ return dir;
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ }
+
+ /**
+ * Return a compiled script from the cache, or null if it isn't found.
+ *
+ * @param source the source
+ * @return the compiled script or null
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ public CompiledScript getScript(final Source source) throws IOException, ClassNotFoundException {
+ if (source.getLength() < minSize) {
+ return null;
+ }
+
+ final String digest = BASE64.encodeToString(source.getDigest());
+ final File file = new File(dir, digest);
+
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<CompiledScript>() {
+ @Override
+ public CompiledScript run() throws IOException, ClassNotFoundException {
+ if (!file.exists()) {
+ return null;
+ }
+ try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
+ CompiledScript compiledScript = (CompiledScript) in.readObject();
+ compiledScript.setSource(source);
+ return compiledScript;
+ }
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ final Exception ex = e.getException();
+ if (ex instanceof IOException) {
+ throw (IOException) ex;
+ } else if (ex instanceof ClassNotFoundException) {
+ throw (ClassNotFoundException) ex;
+ }
+ throw (new RuntimeException(ex));
+ }
+ }
+
+ /**
+ * Store a compiled script in the cache.
+ *
+ * @param source the source
+ * @param mainClassName the main class name
+ * @param classBytes a map of class bytes
+ * @param constants the constants array
+ * @throws IOException
+ */
+ public void putScript(final Source source, final String mainClassName, final Map<String, byte[]> classBytes, final Object[] constants)
+ throws IOException {
+ if (source.getLength() < minSize) {
+ return;
+ }
+ for (final Object constant : constants) {
+ // Make sure all constant data is serializable
+ if (! (constant instanceof Serializable)) {
+ return;
+ }
+ }
+
+ final String digest = BASE64.encodeToString(source.getDigest());
+ final File file = new File(dir, digest);
+ final CompiledScript script = new CompiledScript(source, mainClassName, classBytes, constants);
+
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws IOException {
+ try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
+ out.writeObject(script);
+ }
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ }
+}
+
diff --git a/src/jdk/nashorn/internal/runtime/CompiledScript.java b/src/jdk/nashorn/internal/runtime/CompiledScript.java
new file mode 100644
index 00000000..6bb8e9a0
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/CompiledScript.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Class representing a compiled script.
+ */
+final class CompiledScript implements Serializable {
+
+ /** Main class name. */
+ private final String mainClassName;
+
+ /** Map of class names to class bytes. */
+ private final Map<String, byte[]> classBytes;
+
+ /** Constants array. */
+ private final Object[] constants;
+
+ /** The source */
+ private transient Source source;
+
+ private static final long serialVersionUID = 2958227232195298340L;
+
+ /**
+ * Constructor.
+ *
+ * @param mainClassName main class name
+ * @param classBytes map of class names to class bytes
+ * @param constants constants array
+ */
+ CompiledScript(final Source source, final String mainClassName, final Map<String, byte[]> classBytes, final Object[] constants) {
+ this.source = source;
+ this.mainClassName = mainClassName;
+ this.classBytes = classBytes;
+ this.constants = constants;
+ }
+
+ /**
+ * Returns the main class name.
+ * @return the main class name
+ */
+ public String getMainClassName() {
+ return mainClassName;
+ }
+
+ /**
+ * Returns a map of class names to class bytes.
+ * @return map of class bytes
+ */
+ public Map<String, byte[]> getClassBytes() {
+ return classBytes;
+ }
+
+ /**
+ * Returns the constants array.
+ * @return constants array
+ */
+ public Object[] getConstants() {
+ return constants;
+ }
+
+ /**
+ * Returns the source of this cached script.
+ * @return the source
+ */
+ public Source getSource() {
+ return source;
+ }
+
+ /**
+ * Sets the source of this cached script.
+ * @param source the source
+ */
+ void setSource(final Source source) {
+ this.source = source;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = mainClassName.hashCode();
+ hash = 31 * hash + classBytes.hashCode();
+ hash = 31 * hash + Arrays.hashCode(constants);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof CompiledScript)) {
+ return false;
+ }
+
+ final CompiledScript cs = (CompiledScript) obj;
+ return mainClassName.equals(cs.mainClassName)
+ && classBytes.equals(cs.classBytes)
+ && Arrays.equals(constants, cs.constants);
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java
index eae6a3ce..c4fa6e0d 100644
--- a/src/jdk/nashorn/internal/runtime/Context.java
+++ b/src/jdk/nashorn/internal/runtime/Context.java
@@ -25,7 +25,9 @@
package jdk.nashorn.internal.runtime;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
@@ -38,6 +40,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicLong;
import java.net.MalformedURLException;
@@ -48,7 +51,10 @@ import java.security.CodeSigner;
import java.security.CodeSource;
import java.security.Permissions;
import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -134,8 +140,32 @@ public final class Context {
}
@Override
- public Class<?> install(final String className, final byte[] bytecode) {
- return loader.installClass(className, bytecode, codeSource);
+ public Class<?> install(final String className, final byte[] bytecode, final Source source, final Object[] constants) {
+ Compiler.LOG.fine("Installing class ", className);
+
+ final String binaryName = Compiler.binaryName(className);
+ final Class<?> clazz = loader.installClass(binaryName, bytecode, codeSource);
+
+ try {
+ // Need doPrivileged because these fields are private
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ //use reflection to write source and constants table to installed classes
+ final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
+ final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
+ sourceField.setAccessible(true);
+ constantsField.setAccessible(true);
+ sourceField.set(null, source);
+ constantsField.set(null, constants);
+ return null;
+ }
+ });
+ } catch (final PrivilegedActionException e) {
+ throw new RuntimeException(e);
+ }
+
+ return clazz;
}
@Override
@@ -152,6 +182,18 @@ public final class Context {
public long getUniqueEvalId() {
return context.getUniqueEvalId();
}
+
+ @Override
+ public void storeCompiledScript(final Source source, final String mainClassName,
+ final Map<String, byte[]> classBytes, final Object[] constants) {
+ if (context.codeStore != null) {
+ try {
+ context.codeStore.putScript(source, mainClassName, classBytes, constants);
+ } catch (final IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
}
/** Is Context global debug mode enabled ? */
@@ -159,9 +201,12 @@ public final class Context {
private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>();
- // class cache
+ // in-memory cache for loaded classes
private ClassCache classCache;
+ // persistent code store
+ private CodeStore codeStore;
+
/**
* Get the current global scope
* @return the current global scope
@@ -369,6 +414,19 @@ public final class Context {
classCache = new ClassCache(cacheSize);
}
+ if (env._persistent_cache) {
+ if (env._lazy_compilation || env._specialize_calls != null) {
+ getErr().println("Can not use persistent class caching with lazy compilation or call specialization.");
+ } else {
+ try {
+ final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
+ codeStore = new CodeStore(cacheDir);
+ } catch (IOException e) {
+ throw new RuntimeException("Error initializing code cache", e);
+ }
+ }
+ }
+
// print version info if asked.
if (env._version) {
getErr().println("nashorn " + Version.version());
@@ -933,17 +991,32 @@ public final class Context {
return script;
}
- final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
- if (errors.hasErrors()) {
- return null;
- }
+ CompiledScript compiledScript = null;
+ FunctionNode functionNode = null;
- if (env._print_ast) {
- getErr().println(new ASTWriter(functionNode));
+ if (!env._parse_only && codeStore != null) {
+ try {
+ compiledScript = codeStore.getScript(source);
+ } catch (IOException | ClassNotFoundException e) {
+ Compiler.LOG.warning("Error loading ", source, " from cache: ", e);
+ // Fall back to normal compilation
+ }
}
- if (env._print_parse) {
- getErr().println(new PrintVisitor(functionNode));
+ if (compiledScript == null) {
+ functionNode = new Parser(env, source, errMan, strict).parse();
+
+ if (errors.hasErrors()) {
+ return null;
+ }
+
+ if (env._print_ast) {
+ getErr().println(new ASTWriter(functionNode));
+ }
+
+ if (env._print_parse) {
+ getErr().println(new PrintVisitor(functionNode));
+ }
}
if (env._parse_only) {
@@ -955,12 +1028,15 @@ public final class Context {
final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
- final Compiler compiler = new Compiler(installer, strict);
+ if (functionNode != null) {
+ final Compiler compiler = new Compiler(installer, strict);
+ final FunctionNode newFunctionNode = compiler.compile(functionNode);
+ script = compiler.install(newFunctionNode);
+ } else {
+ script = install(compiledScript, installer);
+ }
- final FunctionNode newFunctionNode = compiler.compile(functionNode);
- script = compiler.install(newFunctionNode);
cacheClass(source, script);
-
return script;
}
@@ -982,6 +1058,42 @@ public final class Context {
return uniqueScriptId.getAndIncrement();
}
+
+ /**
+ * Install a previously compiled class from the code cache.
+ *
+ * @param compiledScript cached script containing class bytes and constants
+ * @return main script class
+ */
+ private Class<?> install(final CompiledScript compiledScript, final CodeInstaller<ScriptEnvironment> installer) {
+
+ final Map<String, Class<?>> installedClasses = new HashMap<>();
+ final Source source = compiledScript.getSource();
+ final Object[] constants = compiledScript.getConstants();
+ final String rootClassName = compiledScript.getMainClassName();
+ final byte[] rootByteCode = compiledScript.getClassBytes().get(rootClassName);
+ final Class<?> rootClass = installer.install(rootClassName, rootByteCode, source, constants);
+
+ installedClasses.put(rootClassName, rootClass);
+
+ for (final Map.Entry<String, byte[]> entry : compiledScript.getClassBytes().entrySet()) {
+ final String className = entry.getKey();
+ if (className.equals(rootClassName)) {
+ continue;
+ }
+ final byte[] code = entry.getValue();
+
+ installedClasses.put(className, installer.install(className, code, source, constants));
+ }
+ for (Object constant : constants) {
+ if (constant instanceof RecompilableScriptFunctionData) {
+ ((RecompilableScriptFunctionData) constant).setCodeAndSource(installedClasses, source);
+ }
+ }
+
+ return rootClass;
+ }
+
/**
* Cache for compiled script classes.
*/
diff --git a/src/jdk/nashorn/internal/runtime/JSType.java b/src/jdk/nashorn/internal/runtime/JSType.java
index c49de9cf..11cecbfe 100644
--- a/src/jdk/nashorn/internal/runtime/JSType.java
+++ b/src/jdk/nashorn/internal/runtime/JSType.java
@@ -438,7 +438,9 @@ public enum JSType {
// encode integer part from least significant digit, then reverse
do {
- sb.append(chars.charAt((int) (intPart % radix)));
+ final double remainder = intPart % radix;
+ sb.append(chars.charAt((int) remainder));
+ intPart -= remainder;
intPart /= radix;
} while (intPart >= 1.0);
diff --git a/src/jdk/nashorn/internal/runtime/Property.java b/src/jdk/nashorn/internal/runtime/Property.java
index 885fa712..b3f1e0af 100644
--- a/src/jdk/nashorn/internal/runtime/Property.java
+++ b/src/jdk/nashorn/internal/runtime/Property.java
@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
+import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.util.Objects;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
@@ -43,7 +44,7 @@ import jdk.nashorn.internal.codegen.types.Type;
* @see AccessorProperty
* @see UserAccessorProperty
*/
-public abstract class Property {
+public abstract class Property implements Serializable {
/*
* ECMA 8.6.1 Property Attributes
*
@@ -100,6 +101,8 @@ public abstract class Property {
/** Property field number or spill slot. */
private final int slot;
+ private static final long serialVersionUID = 2099814273074501176L;
+
/**
* Constructor
*
@@ -358,6 +361,13 @@ public abstract class Property {
public abstract MethodHandle getGetter(final Class<?> type);
/**
+ * Hook to initialize method handles after deserialization.
+ *
+ * @param structure the structure class
+ */
+ abstract void initMethodHandles(final Class<?> structure);
+
+ /**
* Get the key for this property. This key is an ordinary string. The "name".
* @return key for property
*/
diff --git a/src/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk/nashorn/internal/runtime/PropertyMap.java
index 77bcc83f..e2e07fdf 100644
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java
@@ -29,6 +29,10 @@ import static jdk.nashorn.internal.runtime.PropertyHashMap.EMPTY_HASHMAP;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.lang.invoke.SwitchPoint;
import java.lang.ref.SoftReference;
import java.util.Arrays;
@@ -37,6 +41,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;
+import jdk.nashorn.internal.scripts.JO;
/**
* Map of object properties. The PropertyMap is the "template" for JavaScript object
@@ -47,7 +52,7 @@ import java.util.WeakHashMap;
* All property maps are immutable. If a property is added, modified or removed, the mutator
* will return a new map.
*/
-public final class PropertyMap implements Iterable<Object> {
+public final class PropertyMap implements Iterable<Object>, Serializable {
/** Used for non extensible PropertyMaps, negative logic as the normal case is extensible. See {@link ScriptObject#preventExtensions()} */
public static final int NOT_EXTENSIBLE = 0b0000_0001;
/** Does this map contain valid array keys? */
@@ -57,7 +62,7 @@ public final class PropertyMap implements Iterable<Object> {
private int flags;
/** Map of properties. */
- private final PropertyHashMap properties;
+ private transient PropertyHashMap properties;
/** Number of fields in use. */
private int fieldCount;
@@ -68,17 +73,22 @@ public final class PropertyMap implements Iterable<Object> {
/** Length of spill in use. */
private int spillLength;
+ /** Structure class name */
+ private String className;
+
/** {@link SwitchPoint}s for gets on inherited properties. */
- private HashMap<String, SwitchPoint> protoGetSwitches;
+ private transient HashMap<String, SwitchPoint> protoGetSwitches;
/** History of maps, used to limit map duplication. */
- private WeakHashMap<Property, SoftReference<PropertyMap>> history;
+ private transient WeakHashMap<Property, SoftReference<PropertyMap>> history;
/** History of prototypes, used to limit map duplication. */
- private WeakHashMap<PropertyMap, SoftReference<PropertyMap>> protoHistory;
+ private transient WeakHashMap<PropertyMap, SoftReference<PropertyMap>> protoHistory;
/** property listeners */
- private PropertyListeners listeners;
+ private transient PropertyListeners listeners;
+
+ private static final long serialVersionUID = -7041836752008732533L;
/**
* Constructor.
@@ -89,8 +99,10 @@ public final class PropertyMap implements Iterable<Object> {
* @param spillLength Number of spill slots used.
* @param containsArrayKeys True if properties contain numeric keys
*/
- private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) {
+ private PropertyMap(final PropertyHashMap properties, final String className, final int fieldCount,
+ final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) {
this.properties = properties;
+ this.className = className;
this.fieldCount = fieldCount;
this.fieldMaximum = fieldMaximum;
this.spillLength = spillLength;
@@ -145,7 +157,25 @@ public final class PropertyMap implements Iterable<Object> {
if (Context.DEBUG) {
duplicatedCount++;
}
- return new PropertyMap(this.properties, 0, 0, 0, containsArrayKeys());
+ return new PropertyMap(this.properties, this.className, 0, 0, 0, containsArrayKeys());
+ }
+
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeObject(properties.getProperties());
+ }
+
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ final Property[] props = (Property[]) in.readObject();
+ this.properties = EMPTY_HASHMAP.immutableAdd(props);
+
+ assert className != null;
+ final Class<?> structure = Context.forStructureClass(className);
+ for (Property prop : props) {
+ prop.initMethodHandles(structure);
+ }
}
/**
@@ -160,9 +190,9 @@ public final class PropertyMap implements Iterable<Object> {
* @param spillLength Number of used spill slots.
* @return New {@link PropertyMap}.
*/
- public static PropertyMap newMap(final Collection<Property> properties, final int fieldCount, final int fieldMaximum, final int spillLength) {
+ public static PropertyMap newMap(final Collection<Property> properties, final String className, final int fieldCount, final int fieldMaximum, final int spillLength) {
PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties);
- return new PropertyMap(newProperties, fieldCount, fieldMaximum, spillLength, false);
+ return new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength, false);
}
/**
@@ -175,7 +205,7 @@ public final class PropertyMap implements Iterable<Object> {
* @return New {@link PropertyMap}.
*/
public static PropertyMap newMap(final Collection<Property> properties) {
- return (properties == null || properties.isEmpty())? newMap() : newMap(properties, 0, 0, 0);
+ return (properties == null || properties.isEmpty())? newMap() : newMap(properties, JO.class.getName(), 0, 0, 0);
}
/**
@@ -184,7 +214,7 @@ public final class PropertyMap implements Iterable<Object> {
* @return New empty {@link PropertyMap}.
*/
public static PropertyMap newMap() {
- return new PropertyMap(EMPTY_HASHMAP, 0, 0, 0, false);
+ return new PropertyMap(EMPTY_HASHMAP, JO.class.getName(), 0, 0, 0, false);
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
index 7e1a516a..90885444 100644
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
@@ -27,14 +27,15 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
+import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
+import java.util.Map;
import jdk.internal.dynalink.support.NameCodec;
-
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature;
@@ -43,6 +44,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
+import jdk.nashorn.internal.scripts.JS;
/**
* This is a subclass that represents a script function that may be regenerated,
@@ -50,13 +52,19 @@ import jdk.nashorn.internal.parser.TokenType;
* The common denominator is that it can get new invokers during its lifespan,
* unlike {@code FinalScriptFunctionData}
*/
-public final class RecompilableScriptFunctionData extends ScriptFunctionData {
+public final class RecompilableScriptFunctionData extends ScriptFunctionData implements Serializable {
/** FunctionNode with the code for this ScriptFunction */
- private FunctionNode functionNode;
+ private transient FunctionNode functionNode;
/** Source from which FunctionNode was parsed. */
- private final Source source;
+ private transient Source source;
+
+ /** The line number where this function begins. */
+ private final int lineNumber;
+
+ /** Allows us to retrieve the method handle for this function once the code is compiled */
+ private MethodLocator methodLocator;
/** Token of this function within the source. */
private final long token;
@@ -65,13 +73,13 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
private final PropertyMap allocatorMap;
/** Code installer used for all further recompilation/specialization of this ScriptFunction */
- private CodeInstaller<ScriptEnvironment> installer;
+ private transient CodeInstaller<ScriptEnvironment> installer;
/** Name of class where allocator function resides */
private final String allocatorClassName;
/** lazily generated allocator */
- private MethodHandle allocator;
+ private transient MethodHandle allocator;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
@@ -79,7 +87,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
* Used for specialization based on runtime arguments. Whenever we specialize on
* callsite parameter types at runtime, we need to use a parameter type guard to
* ensure that the specialized version of the script function continues to be
- * applicable for a particular callsite *
+ * applicable for a particular callsite.
*/
private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Object[].class);
@@ -88,10 +96,12 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
* (or java.lang.Number instance) to specialize the parameter to an integer, if the
* parameter in question can be represented as one. The double typically only exists
* because the compiler doesn't know any better than "a number type" and conservatively
- * picks doubles when it can't prove that an integer addition wouldn't overflow
+ * picks doubles when it can't prove that an integer addition wouldn't overflow.
*/
private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class);
+ private static final long serialVersionUID = 4914839316174633726L;
+
/**
* Constructor - public as scripts use it
*
@@ -104,13 +114,16 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
super(functionName(functionNode),
functionNode.getParameters().size(),
getFlags(functionNode));
-
this.functionNode = functionNode;
this.source = functionNode.getSource();
+ this.lineNumber = functionNode.getLineNumber();
this.token = tokenFor(functionNode);
this.installer = installer;
this.allocatorClassName = allocatorClassName;
this.allocatorMap = allocatorMap;
+ if (!functionNode.isLazy()) {
+ methodLocator = new MethodLocator(functionNode);
+ }
}
@Override
@@ -122,16 +135,19 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
return "function " + (name == null ? "" : name) + "() { [native code] }";
}
+ public void setCodeAndSource(final Map<String, Class<?>> code, final Source source) {
+ this.source = source;
+ if (methodLocator != null) {
+ methodLocator.setClass(code.get(methodLocator.getClassName()));
+ }
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
if (source != null) {
- sb.append(source.getName());
- if (functionNode != null) {
- sb.append(':').append(functionNode.getLineNumber());
- }
- sb.append(' ');
+ sb.append(source.getName()).append(':').append(lineNumber).append(' ');
}
return sb.toString() + super.toString();
@@ -204,8 +220,13 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
functionNode = compiler.compile(functionNode);
assert !functionNode.isLazy();
compiler.install(functionNode);
+ methodLocator = new MethodLocator(functionNode);
flags = getFlags(functionNode);
}
+
+ if (functionNode != null) {
+ methodLocator.setClass(functionNode.getCompileUnit().getCode());
+ }
}
@Override
@@ -221,12 +242,13 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
* eager compilation or from running a lazy compile on the lines above
*/
- assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
+ assert functionNode == null || functionNode.hasState(CompilationState.EMITTED) :
+ functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
// code exists - look it up and add it into the automatically sorted invoker list
addCode(functionNode);
- if (! functionNode.canSpecialize()) {
+ if (functionNode != null && !functionNode.canSpecialize()) {
// allow GC to claim IR stuff that is not needed anymore
functionNode = null;
installer = null;
@@ -238,13 +260,9 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
}
private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) {
- final MethodType targetType = new FunctionSignature(fn).getMethodType();
- MethodHandle target =
- MH.findStatic(
- LOOKUP,
- fn.getCompileUnit().getCode(),
- fn.getName(),
- targetType);
+ assert methodLocator != null;
+ MethodHandle target = methodLocator.getMethodHandle();
+ final MethodType targetType = methodLocator.getMethodType();
/*
* For any integer argument. a double that is representable as an integer is OK.
@@ -424,7 +442,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args));
- assert snapshot != null;
assert snapshot != functionNode;
final Compiler compiler = new Compiler(installer);
@@ -450,5 +467,45 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types));
}
+ /**
+ * Helper class that allows us to retrieve the method handle for this function once it has been generated.
+ */
+ private static class MethodLocator implements Serializable {
+ private transient Class<?> clazz;
+ private final String className;
+ private final String methodName;
+ private final MethodType methodType;
+
+ private static final long serialVersionUID = -5420835725902966692L;
+
+ MethodLocator(final FunctionNode functionNode) {
+ this.className = functionNode.getCompileUnit().getUnitClassName();
+ this.methodName = functionNode.getName();
+ this.methodType = new FunctionSignature(functionNode).getMethodType();
+
+ assert className != null;
+ assert methodName != null;
+ }
+
+ void setClass(final Class<?> clazz) {
+ if (!JS.class.isAssignableFrom(clazz)) {
+ throw new IllegalArgumentException();
+ }
+ this.clazz = clazz;
+ }
+
+ String getClassName() {
+ return className;
+ }
+
+ MethodType getMethodType() {
+ return methodType;
+ }
+
+ MethodHandle getMethodHandle() {
+ return MH.findStatic(LOOKUP, clazz, methodName, methodType);
+ }
+ }
+
}
diff --git a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
index 53274710..96982011 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
@@ -134,6 +134,9 @@ public final class ScriptEnvironment {
/** Only parse the source code, do not compile */
public final boolean _parse_only;
+ /** Enable disk cache for compiled scripts */
+ public final boolean _persistent_cache;
+
/** Print the AST before lowering */
public final boolean _print_ast;
@@ -218,6 +221,7 @@ public final class ScriptEnvironment {
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
_no_typed_arrays = options.getBoolean("no.typed.arrays");
_parse_only = options.getBoolean("parse.only");
+ _persistent_cache = options.getBoolean("persistent.code.cache");
_print_ast = options.getBoolean("print.ast");
_print_lower_ast = options.getBoolean("print.lower.ast");
_print_code = options.getBoolean("print.code");
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
index aee367d6..ad40d622 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
@@ -29,10 +29,10 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
/**
@@ -40,7 +40,7 @@ import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
* Instances of this class are created during codegen and stored in script classes'
* constants array to reduce function instantiation overhead during runtime.
*/
-public abstract class ScriptFunctionData {
+public abstract class ScriptFunctionData implements Serializable {
/** Name of the function or "" for anonynous functions */
protected final String name;
@@ -74,6 +74,8 @@ public abstract class ScriptFunctionData {
/** Flag for strict constructors */
public static final int IS_STRICT_CONSTRUCTOR = IS_STRICT | IS_CONSTRUCTOR;
+ private static final long serialVersionUID = 4252901245508769114L;
+
/**
* Constructor
*
diff --git a/src/jdk/nashorn/internal/runtime/Source.java b/src/jdk/nashorn/internal/runtime/Source.java
index 0455a86c..f57874bc 100644
--- a/src/jdk/nashorn/internal/runtime/Source.java
+++ b/src/jdk/nashorn/internal/runtime/Source.java
@@ -39,6 +39,8 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;
import jdk.nashorn.internal.parser.Token;
@@ -71,6 +73,9 @@ public final class Source {
/** Cached hash code */
private int hash;
+ /** Message digest */
+ private byte[] digest;
+
/** Source URL if available */
private final URL url;
@@ -417,6 +422,40 @@ public final class Source {
}
/**
+ * Get a message digest for this source.
+ *
+ * @return a message digest for this source
+ */
+ public synchronized byte[] getDigest() {
+ if (digest == null) {
+
+ final byte[] bytes = new byte[content.length * 2];
+
+ for (int i = 0; i < content.length; i++) {
+ bytes[i * 2] = (byte) (content[i] & 0x00ff);
+ bytes[i * 2 + 1] = (byte) ((content[i] & 0xff00) >> 8);
+ }
+
+ try {
+ final MessageDigest md = MessageDigest.getInstance("SHA-1");
+ if (name != null) {
+ md.update(name.getBytes(StandardCharsets.UTF_8));
+ }
+ if (base != null) {
+ md.update(base.getBytes(StandardCharsets.UTF_8));
+ }
+ if (url != null) {
+ md.update(url.toString().getBytes(StandardCharsets.UTF_8));
+ }
+ digest = md.digest(bytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return digest;
+ }
+
+ /**
* Get the base url. This is currently used for testing only
* @param url a URL
* @return base URL for url
diff --git a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
index a69de45c..5b21f6ee 100644
--- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
@@ -187,6 +187,11 @@ public final class UserAccessorProperty extends Property {
}
@Override
+ void initMethodHandles(final Class<?> structure) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public ScriptFunction getGetterFunction(final ScriptObject obj) {
final Object value = obj.getSpill(getterSlot);
return (value instanceof ScriptFunction) ? (ScriptFunction) value : null;
diff --git a/src/jdk/nashorn/internal/runtime/resources/Options.properties b/src/jdk/nashorn/internal/runtime/resources/Options.properties
index 27262111..033fcc25 100644
--- a/src/jdk/nashorn/internal/runtime/resources/Options.properties
+++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties
@@ -230,6 +230,14 @@ nashorn.option.parse.only = { \
desc="Parse without compiling." \
}
+nashorn.option.persistent.code.cache = { \
+ name="--persistent-code-cache", \
+ short_name="-pcc", \
+ desc="Enable disk cache for compiled scripts.", \
+ is_undocumented=true, \
+ default=false \
+}
+
nashorn.option.profile.callsites = { \
name="--profile-callsites", \
short_name="-pcs", \
diff --git a/test/script/basic/JDK-8030199.js b/test/script/basic/JDK-8030199.js
new file mode 100644
index 00000000..b6d32fb1
--- /dev/null
+++ b/test/script/basic/JDK-8030199.js
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8030199: Nashorn: Uint8ClampedArray - Incorrect ToUint8Clamp implementation
+ *
+ * @test
+ * @run
+ */
+
+function testTypedArray(ArrayType) {
+ print(ArrayType.BYTES_PER_ELEMENT);
+ var a = new ArrayType(7);
+ a[0] = 4294967296;
+ a[1] = -4294967295;
+ a[2] = 4294967298;
+ a[3] = -4294967298;
+ a[4] = Infinity;
+ a[5] = -Infinity;
+ a[6] = NaN;
+ print(Array.prototype.join.call(a));
+}
+
+testTypedArray(Uint8ClampedArray);
+testTypedArray(Uint8Array);
+testTypedArray(Int8Array);
+testTypedArray(Uint16Array);
+testTypedArray(Int16Array);
+testTypedArray(Uint32Array);
+testTypedArray(Int32Array);
diff --git a/test/script/basic/JDK-8030199.js.EXPECTED b/test/script/basic/JDK-8030199.js.EXPECTED
new file mode 100644
index 00000000..0b2cf627
--- /dev/null
+++ b/test/script/basic/JDK-8030199.js.EXPECTED
@@ -0,0 +1,14 @@
+1
+255,0,255,0,255,0,0
+1
+0,1,2,254,0,0,0
+1
+0,1,2,-2,0,0,0
+2
+0,1,2,65534,0,0,0
+2
+0,1,2,-2,0,0,0
+4
+0,1,2,4294967294,0,0,0
+4
+0,1,2,-2,0,0,0
diff --git a/test/script/basic/JDK-8030200.js b/test/script/basic/JDK-8030200.js
new file mode 100644
index 00000000..e49eec7d
--- /dev/null
+++ b/test/script/basic/JDK-8030200.js
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8030200: Wrong result for Number.prototype.toString() for certain radix/inputs
+ *
+ * @test
+ * @run
+ */
+
+var n = 0x8000000000000800;
+print(n);
+var s = n.toString(5);
+var m = parseInt(s, 5);
+print(m === n);
+print(n);
diff --git a/test/script/basic/JDK-8030200.js.EXPECTED b/test/script/basic/JDK-8030200.js.EXPECTED
new file mode 100644
index 00000000..38b73bc2
--- /dev/null
+++ b/test/script/basic/JDK-8030200.js.EXPECTED
@@ -0,0 +1,3 @@
+9223372036854778000
+true
+9223372036854778000
diff --git a/test/script/basic/JDK-8039387.js b/test/script/basic/JDK-8039387.js
new file mode 100644
index 00000000..8f903c1a
--- /dev/null
+++ b/test/script/basic/JDK-8039387.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8039387: Nashorn supports indexed access of List elements, but length property is not supported
+ *
+ * @test
+ * @run
+ */
+
+var ArrayList = Java.type("java.util.ArrayList")
+var list = new ArrayList(3)
+list.add("nashorn")
+list.add("js")
+list.add("ecmascript")
+var len = list.length
+print("length = " + len)
+for (var i = 0; i < len; i++)
+ print(list[i])
diff --git a/test/script/basic/JDK-8039387.js.EXPECTED b/test/script/basic/JDK-8039387.js.EXPECTED
new file mode 100644
index 00000000..5b9ce56d
--- /dev/null
+++ b/test/script/basic/JDK-8039387.js.EXPECTED
@@ -0,0 +1,4 @@
+length = 3
+nashorn
+js
+ecmascript
diff --git a/test/script/basic/NASHORN-173.js.EXPECTED b/test/script/basic/NASHORN-173.js.EXPECTED
index a0781a88..5375ecb1 100644
--- a/test/script/basic/NASHORN-173.js.EXPECTED
+++ b/test/script/basic/NASHORN-173.js.EXPECTED
@@ -132,7 +132,7 @@ RangeError
2.3423446609034533e+21
2.3423446609034533e+21
11111101111101010001111111010101101000101011011001001000000000000000000
-2224143002343343220233144213324
+2224143002343343220233044213324
375752177255053311000000
73b92b9962990aa44400
7efa8fead15b240000
diff --git a/test/script/basic/list.js b/test/script/basic/list.js
index 72ae0be7..59136c0b 100644
--- a/test/script/basic/list.js
+++ b/test/script/basic/list.js
@@ -33,7 +33,7 @@ print("l.class.name=" + Java.typeName(l.class)) // Has "class" property like any
l.add("foo")
l.add("bar")
-print("l.length=" + l.length) // doesn't work, returns undefined
+print("l.length=" + l.length) // works, maps to l.size()
print("l.size()=" + l.size()) // this will work
print("l[0]=" + l[0])
diff --git a/test/script/basic/list.js.EXPECTED b/test/script/basic/list.js.EXPECTED
index 18feade2..47f3bd4f 100644
--- a/test/script/basic/list.js.EXPECTED
+++ b/test/script/basic/list.js.EXPECTED
@@ -1,5 +1,5 @@
l.class.name=java.util.ArrayList
-l.length=undefined
+l.length=2
l.size()=2
l[0]=foo
l[1]=bar
diff --git a/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java b/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java
new file mode 100644
index 00000000..a2d72ebf
--- /dev/null
+++ b/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.FileSystems;
+import javax.script.ScriptException;
+import org.testng.annotations.Test;
+import javax.script.ScriptEngine;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @test
+ * @bug 8039185 8039403
+ * @summary Test for persistent code cache and path handling
+ * @run testng jdk.nashorn.internal.runtime.CodeStoreAndPathTest
+ */
+
+public class CodeStoreAndPathTest {
+
+ final String code1 = "var code1; var x = 'Hello Script'; var x1 = 'Hello Script'; "
+ + "var x2 = 'Hello Script'; var x3 = 'Hello Script'; "
+ + "var x4 = 'Hello Script'; var x5 = 'Hello Script';"
+ + "var x6 = 'Hello Script'; var x7 = 'Hello Script'; "
+ + "var x8 = 'Hello Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';"
+ + "function f() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}"
+ + "function g() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}"
+ + "function h() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}"
+ + "function i() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}";
+ final String code2 = "var code2; var x = 'Hello Script'; var x1 = 'Hello Script'; "
+ + "var x2 = 'Hello Script'; var x3 = 'Hello Script'; "
+ + "var x4 = 'Hello Script'; var x5 = 'Hello Script';"
+ + "var x6 = 'Hello Script'; var x7 = 'Hello Script'; "
+ + "var x8 = 'Hello Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';"
+ + "function f() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}"
+ + "function g() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}"
+ + "function h() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}"
+ + "function i() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';"
+ + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';"
+ + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; "
+ + "var x10 = 'Hello Script';}";
+ // Script size < Default minimum size for storing a compiled script class
+ final String code3 = "var code3; var x = 'Hello Script'; var x1 = 'Hello Script'; ";
+ final String codeCache = "build/nashorn_code_cache";
+ final String oldUserDir = System.getProperty("user.dir");
+
+ public void checkCompiledScripts(DirectoryStream<Path> stream, int numberOfScripts) throws IOException {
+ for (Path file : stream) {
+ numberOfScripts--;
+ }
+ stream.close();
+ assertEquals(numberOfScripts,0);
+ }
+
+ @Test
+ public void pathHandlingTest() throws ScriptException, IOException {
+ System.setProperty("nashorn.persistent.code.cache", codeCache);
+ String[] options = new String[]{"--persistent-code-cache"};
+ NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
+ ScriptEngine e = fac.getScriptEngine(options);
+ Path expectedCodeCachePath = FileSystems.getDefault().getPath(oldUserDir + File.separator + codeCache);
+ Path actualCodeCachePath = FileSystems.getDefault().getPath(System.getProperty(
+ "nashorn.persistent.code.cache")).toAbsolutePath();
+ // Check that nashorn code cache is created in current working directory
+ assertEquals(actualCodeCachePath, expectedCodeCachePath);
+ // Check that code cache dir exists and it's not empty
+ File file = new File(actualCodeCachePath.toUri());
+ assertFalse(!file.isDirectory(), "No code cache directory was created!");
+ assertFalse(file.list().length == 0, "Code cache directory is empty!");
+ }
+
+ @Test
+ public void changeUserDirTest() throws ScriptException, IOException {
+ System.setProperty("nashorn.persistent.code.cache", codeCache);
+ String[] options = new String[]{"--persistent-code-cache"};
+ NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
+ ScriptEngine e = fac.getScriptEngine(options);
+ Path codeCachePath = FileSystems.getDefault().getPath(System.getProperty(
+ "nashorn.persistent.code.cache")).toAbsolutePath();
+ String newUserDir = "build/newUserDir";
+ // Now changing current working directory
+ System.setProperty("user.dir", System.getProperty("user.dir") + File.separator + newUserDir);
+ // Check that a new compiled script is stored in exisitng code cache
+ e.eval(code1);
+ DirectoryStream<Path> stream = Files.newDirectoryStream(codeCachePath);
+ // Already one compiled script has been stored in the cache during initialization
+ checkCompiledScripts(stream, 2);
+ // Setting to default current working dir
+ System.setProperty("user.dir", oldUserDir);
+ }
+
+ @Test
+ public void codeCacheTest() throws ScriptException, IOException {
+ System.setProperty("nashorn.persistent.code.cache", codeCache);
+ String[] options = new String[]{"--persistent-code-cache"};
+ NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
+ ScriptEngine e = fac.getScriptEngine(options);
+ Path codeCachePath = FileSystems.getDefault().getPath(System.getProperty(
+ "nashorn.persistent.code.cache")).toAbsolutePath();
+ e.eval(code1);
+ e.eval(code2);
+ e.eval(code3);// less than minimum size for storing
+ // Already one compiled script has been stored in the cache during initialization
+ // adding code1 and code2.
+ DirectoryStream<Path> stream = Files.newDirectoryStream(codeCachePath);
+ checkCompiledScripts(stream, 3);
+ }
+}
diff --git a/test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java b/test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java
new file mode 100644
index 00000000..7b84f5a7
--- /dev/null
+++ b/test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import static org.testng.Assert.fail;
+import org.testng.annotations.Test;
+
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import javax.script.SimpleScriptContext;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+
+/**
+ * @test
+ * @bug 8037378
+ * @summary Sanity tests for no persistence caching
+ * @run testng/othervm jdk.nashorn.internal.runtime.NoPersistenceCachingTest
+ */
+public class NoPersistenceCachingTest {
+
+ private ScriptEngine engine;
+ private ScriptContext context1, context2, context3;
+ private ByteArrayOutputStream stderr;
+ private PrintStream prevStderr;
+
+ @BeforeTest
+ public void setupTest() {
+ stderr = new ByteArrayOutputStream();
+ prevStderr = System.err;
+ System.setErr(new PrintStream(stderr));
+ NashornScriptEngineFactory nashornFactory = null;
+ ScriptEngineManager sm = new ScriptEngineManager();
+ for (ScriptEngineFactory fac : sm.getEngineFactories()) {
+ if (fac instanceof NashornScriptEngineFactory) {
+ nashornFactory = (NashornScriptEngineFactory) fac;
+ break;
+ }
+ }
+ if (nashornFactory == null) {
+ fail("Cannot find nashorn factory!");
+ }
+ String[] options = new String[]{"--log=compiler:finest"};
+ engine = nashornFactory.getScriptEngine(options);
+ context1 = engine.getContext();
+ context2 = new SimpleScriptContext();
+ context2.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
+ context3 = new SimpleScriptContext();
+ context3.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
+ }
+
+ @AfterTest
+ public void setErrTest() {
+ System.setErr(prevStderr);
+ }
+
+ public void runTest(int numberOfContext, String expectedOutputPattern,
+ int expectedPatternOccurrence) {
+
+ try {
+ switch (numberOfContext) {
+ case 2:
+ String scriptTwoContexts = "print('HelloTwoContexts')";
+ engine.eval(scriptTwoContexts, context1);
+ engine.eval(scriptTwoContexts, context2);
+ break;
+ case 3:
+ String scriptThreeContexts = "print('HelloThreeContexts')";
+ engine.eval(scriptThreeContexts, context1);
+ engine.eval(scriptThreeContexts, context2);
+ engine.eval(scriptThreeContexts, context3);
+ break;
+ }
+ } catch (final Exception se) {
+ se.printStackTrace();
+ fail(se.getMessage());
+ }
+ Pattern deoptimizing = Pattern.compile(expectedOutputPattern);
+ Matcher matcher = deoptimizing.matcher(stderr.toString());
+ int matches = 0;
+ while (matcher.find()) {
+ matches++;
+ }
+ if (matches != expectedPatternOccurrence) {
+ fail("Number of cache hit is not correct, expected: "
+ + expectedPatternOccurrence + " and found: " + matches + "\n"
+ + stderr);
+ }
+ stderr.reset();
+ }
+
+ private static String getCodeCachePattern() {
+ return ("\\[compiler\\]\\sCode\\scache\\shit\\sfor\\s<eval>\\savoiding\\srecompile.");
+ }
+
+ @Test
+ public void twoContextTest() {
+ runTest(2, getCodeCachePattern(), 1);
+
+ }
+
+ @Test
+ public void threeContextTest() {
+ runTest(3, getCodeCachePattern(), 2);
+ }
+}