# HG changeset patch # User lana # Date 1397685939 25200 # Node ID c116e9229e096ffe841f2b4f79067378288d0d1d # Parent ef90d0fc9533a8c3315d4e270222e672f51d7a4a# Parent 14f081aae67a8ebc8d75589857f7f5af488c867c Merge diff -r ef90d0fc9533 -r c116e9229e09 src/jdk/internal/dynalink/beans/BeanLinker.java --- a/src/jdk/internal/dynalink/beans/BeanLinker.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/internal/dynalink/beans/BeanLinker.java Wed Apr 16 15:05:39 2014 -0700 @@ -113,6 +113,8 @@ // 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Apr 16 15:05:39 2014 -0700 @@ -3228,7 +3228,7 @@ 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/codegen/Compiler.java --- a/src/jdk/nashorn/internal/codegen/Compiler.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Wed Apr 16 15:05:39 2014 -0700 @@ -405,32 +405,8 @@ 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() { - @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 @@ assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed"; final Map> 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 @@ 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/codegen/MapCreator.java --- a/src/jdk/nashorn/internal/codegen/MapCreator.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/MapCreator.java Wed Apr 16 15:05:39 2014 -0700 @@ -83,7 +83,7 @@ } } - 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 @@ } } - return PropertyMap.newMap(properties, 0, 0, spillIndex); + return PropertyMap.newMap(properties, structure.getName(), 0, 0, spillIndex); } /** diff -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java --- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Wed Apr 16 15:05:39 2014 -0700 @@ -89,6 +89,15 @@ } @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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/AccessorProperty.java --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed Apr 16 15:05:39 2014 -0700 @@ -39,6 +39,9 @@ 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 @@ * 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 @@ }; /** 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 @@ } /** 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 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 @@ 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 @@ } @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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/CodeInstaller.java --- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java Wed Apr 16 15:05:39 2014 -0700 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.runtime; +import java.util.Map; import jdk.nashorn.internal.codegen.ClassEmitter; /** @@ -52,7 +53,7 @@ * @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 @@ * @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 classBytes, Object[] constants); } diff -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/CodeStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/CodeStore.java Wed Apr 16 15:05:39 2014 -0700 @@ -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() { + @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() { + @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 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() { + @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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/CompiledScript.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/CompiledScript.java Wed Apr 16 15:05:39 2014 -0700 @@ -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 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 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 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/Context.java --- a/src/jdk/nashorn/internal/runtime/Context.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/Context.java Wed Apr 16 15:05:39 2014 -0700 @@ -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.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.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 @@ } @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() { + @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 long getUniqueEvalId() { return context.getUniqueEvalId(); } + + @Override + public void storeCompiledScript(final Source source, final String mainClassName, + final Map 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 @@ private static final ThreadLocal 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 @@ 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 @@ 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._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_ast) { - getErr().println(new ASTWriter(functionNode)); - } + if (compiledScript == null) { + functionNode = new Parser(env, source, errMan, strict).parse(); - if (env._print_parse) { - getErr().println(new PrintVisitor(functionNode)); + 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 @@ final CodeSource cs = new CodeSource(url, (CodeSigner[])null); final CodeInstaller 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 @@ 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 installer) { + + final Map> 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 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/JSType.java --- a/src/jdk/nashorn/internal/runtime/JSType.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Wed Apr 16 15:05:39 2014 -0700 @@ -438,7 +438,9 @@ // 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/Property.java --- a/src/jdk/nashorn/internal/runtime/Property.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/Property.java Wed Apr 16 15:05:39 2014 -0700 @@ -29,6 +29,7 @@ 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 @@ * @see AccessorProperty * @see UserAccessorProperty */ -public abstract class Property { +public abstract class Property implements Serializable { /* * ECMA 8.6.1 Property Attributes * @@ -100,6 +101,8 @@ /** Property field number or spill slot. */ private final int slot; + private static final long serialVersionUID = 2099814273074501176L; + /** * Constructor * @@ -358,6 +361,13 @@ 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/PropertyMap.java --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Wed Apr 16 15:05:39 2014 -0700 @@ -29,6 +29,10 @@ 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.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 @@ * 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 { +public final class PropertyMap implements Iterable, 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 @@ 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 @@ /** Length of spill in use. */ private int spillLength; + /** Structure class name */ + private String className; + /** {@link SwitchPoint}s for gets on inherited properties. */ - private HashMap protoGetSwitches; + private transient HashMap protoGetSwitches; /** History of maps, used to limit map duplication. */ - private WeakHashMap> history; + private transient WeakHashMap> history; /** History of prototypes, used to limit map duplication. */ - private WeakHashMap> protoHistory; + private transient WeakHashMap> protoHistory; /** property listeners */ - private PropertyListeners listeners; + private transient PropertyListeners listeners; + + private static final long serialVersionUID = -7041836752008732533L; /** * Constructor. @@ -89,8 +99,10 @@ * @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 @@ 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 @@ * @param spillLength Number of used spill slots. * @return New {@link PropertyMap}. */ - public static PropertyMap newMap(final Collection properties, final int fieldCount, final int fieldMaximum, final int spillLength) { + public static PropertyMap newMap(final Collection 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 @@ * @return New {@link PropertyMap}. */ public static PropertyMap newMap(final Collection 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 @@ * @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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Apr 16 15:05:39 2014 -0700 @@ -27,14 +27,15 @@ 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.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 @@ * 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 @@ private final PropertyMap allocatorMap; /** Code installer used for all further recompilation/specialization of this ScriptFunction */ - private CodeInstaller installer; + private transient CodeInstaller 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 @@ * 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 @@ * (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 @@ 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 @@ return "function " + (name == null ? "" : name) + "() { [native code] }"; } + public void setCodeAndSource(final Map> 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 @@ 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 @@ * 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 @@ } 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 @@ 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 @@ 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/ScriptEnvironment.java --- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed Apr 16 15:05:39 2014 -0700 @@ -134,6 +134,9 @@ /** 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 @@ _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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/ScriptFunctionData.java --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Apr 16 15:05:39 2014 -0700 @@ -29,10 +29,10 @@ 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 @@ * 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 @@ /** Flag for strict constructors */ public static final int IS_STRICT_CONSTRUCTOR = IS_STRICT | IS_CONSTRUCTOR; + private static final long serialVersionUID = 4252901245508769114L; + /** * Constructor * diff -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/Source.java --- a/src/jdk/nashorn/internal/runtime/Source.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/Source.java Wed Apr 16 15:05:39 2014 -0700 @@ -39,6 +39,8 @@ 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 @@ /** Cached hash code */ private int hash; + /** Message digest */ + private byte[] digest; + /** Source URL if available */ private final URL url; @@ -417,6 +422,40 @@ } /** + * 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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/UserAccessorProperty.java --- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Wed Apr 16 15:05:39 2014 -0700 @@ -187,6 +187,11 @@ } @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 -r ef90d0fc9533 -r c116e9229e09 src/jdk/nashorn/internal/runtime/resources/Options.properties --- a/src/jdk/nashorn/internal/runtime/resources/Options.properties Wed Apr 16 12:32:47 2014 -0700 +++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties Wed Apr 16 15:05:39 2014 -0700 @@ -230,6 +230,14 @@ 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 -r ef90d0fc9533 -r c116e9229e09 test/script/basic/JDK-8030199.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8030199.js Wed Apr 16 15:05:39 2014 -0700 @@ -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 -r ef90d0fc9533 -r c116e9229e09 test/script/basic/JDK-8030199.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8030199.js.EXPECTED Wed Apr 16 15:05:39 2014 -0700 @@ -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 -r ef90d0fc9533 -r c116e9229e09 test/script/basic/JDK-8030200.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8030200.js Wed Apr 16 15:05:39 2014 -0700 @@ -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 -r ef90d0fc9533 -r c116e9229e09 test/script/basic/JDK-8030200.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8030200.js.EXPECTED Wed Apr 16 15:05:39 2014 -0700 @@ -0,0 +1,3 @@ +9223372036854778000 +true +9223372036854778000 diff -r ef90d0fc9533 -r c116e9229e09 test/script/basic/JDK-8039387.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8039387.js Wed Apr 16 15:05:39 2014 -0700 @@ -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 -r ef90d0fc9533 -r c116e9229e09 test/script/basic/JDK-8039387.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8039387.js.EXPECTED Wed Apr 16 15:05:39 2014 -0700 @@ -0,0 +1,4 @@ +length = 3 +nashorn +js +ecmascript diff -r ef90d0fc9533 -r c116e9229e09 test/script/basic/NASHORN-173.js.EXPECTED --- a/test/script/basic/NASHORN-173.js.EXPECTED Wed Apr 16 12:32:47 2014 -0700 +++ b/test/script/basic/NASHORN-173.js.EXPECTED Wed Apr 16 15:05:39 2014 -0700 @@ -132,7 +132,7 @@ 2.3423446609034533e+21 2.3423446609034533e+21 11111101111101010001111111010101101000101011011001001000000000000000000 -2224143002343343220233144213324 +2224143002343343220233044213324 375752177255053311000000 73b92b9962990aa44400 7efa8fead15b240000 diff -r ef90d0fc9533 -r c116e9229e09 test/script/basic/list.js --- a/test/script/basic/list.js Wed Apr 16 12:32:47 2014 -0700 +++ b/test/script/basic/list.js Wed Apr 16 15:05:39 2014 -0700 @@ -33,7 +33,7 @@ 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 -r ef90d0fc9533 -r c116e9229e09 test/script/basic/list.js.EXPECTED --- a/test/script/basic/list.js.EXPECTED Wed Apr 16 12:32:47 2014 -0700 +++ b/test/script/basic/list.js.EXPECTED Wed Apr 16 15:05:39 2014 -0700 @@ -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 -r ef90d0fc9533 -r c116e9229e09 test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java Wed Apr 16 15:05:39 2014 -0700 @@ -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 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 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 stream = Files.newDirectoryStream(codeCachePath); + checkCompiledScripts(stream, 3); + } +} diff -r ef90d0fc9533 -r c116e9229e09 test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java Wed Apr 16 15:05:39 2014 -0700 @@ -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\\savoiding\\srecompile."); + } + + @Test + public void twoContextTest() { + runTest(2, getCodeCachePattern(), 1); + + } + + @Test + public void threeContextTest() { + runTest(3, getCodeCachePattern(), 2); + } +}