Mercurial > hg > release > icedtea8-forest-3.0 > nashorn
changeset 1629:a82fc95737d1
Merge
author | asaha |
---|---|
date | Thu, 24 Sep 2015 10:09:56 -0700 |
parents | 1eda0618f55d (current diff) e4a553f79ebd (diff) |
children | 718b32884775 |
files | src/jdk/nashorn/internal/codegen/AstSerializer.java src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java src/jdk/nashorn/internal/objects/PrototypeObject.java src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java |
diffstat | 127 files changed, 2428 insertions(+), 1618 deletions(-) [+] |
line wrap: on
line diff
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java Thu Sep 24 10:09:56 2015 -0700 @@ -54,10 +54,9 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_DESC; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE; @@ -282,9 +281,9 @@ assert specs != null; if (!specs.isEmpty()) { mi.memberInfoArray(className, specs); - mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC); + mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC); } else { - mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC); + mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_DESC); } if (arityFound) {
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Thu Sep 24 10:09:56 2015 -0700 @@ -38,9 +38,8 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC3; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC4; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_INIT_DESC3; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_INIT_DESC4; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE; @@ -55,7 +54,7 @@ import jdk.internal.org.objectweb.asm.Handle; /** - * This class generates constructor class for a @ClassInfo annotated class. + * This class generates constructor class for a @ScriptClass annotated class. * */ public class ConstructorGenerator extends ClassGenerator { @@ -75,8 +74,8 @@ } byte[] getClassBytes() { - // new class extensing from ScriptObject - final String superClass = (constructor != null)? SCRIPTFUNCTIONIMPL_TYPE : SCRIPTOBJECT_TYPE; + // new class extending from ScriptObject + final String superClass = (constructor != null)? SCRIPTFUNCTION_TYPE : SCRIPTOBJECT_TYPE; cw.visit(V1_7, ACC_FINAL, className, null, superClass, null); if (memberCount > 0) { // add fields @@ -182,8 +181,8 @@ loadMap(mi); } else { // call Function.<init> - superClass = SCRIPTFUNCTIONIMPL_TYPE; - superDesc = (memberCount > 0) ? SCRIPTFUNCTIONIMPL_INIT_DESC4 : SCRIPTFUNCTIONIMPL_INIT_DESC3; + superClass = SCRIPTFUNCTION_TYPE; + superDesc = (memberCount > 0) ? SCRIPTFUNCTION_INIT_DESC4 : SCRIPTFUNCTION_INIT_DESC3; mi.loadLiteral(constructor.getName()); mi.visitLdcInsn(new Handle(H_INVOKESTATIC, scriptClassInfo.getJavaName(), constructor.getJavaName(), constructor.getJavaDesc())); loadMap(mi);
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java Thu Sep 24 10:09:56 2015 -0700 @@ -161,7 +161,7 @@ } /** - * Tag something as optimitic builtin or not + * Tag something as optimistic builtin or not * @param isOptimistic boolean, true if builtin constructor */ public void setIsOptimistic(final boolean isOptimistic) { @@ -178,7 +178,7 @@ } /** - * Set thre SpecializedFunction link logic class for specializations, i.e. optimistic + * Set the SpecializedFunction link logic class for specializations, i.e. optimistic * builtins * @param linkLogicClass link logic class */
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Thu Sep 24 10:09:56 2015 -0700 @@ -42,7 +42,7 @@ import java.io.IOException; /** - * This class generates prototype class for a @ClassInfo annotated class. + * This class generates prototype class for a @ScriptClass annotated class. * */ public class PrototypeGenerator extends ClassGenerator { @@ -57,7 +57,7 @@ } byte[] getClassBytes() { - // new class extensing from ScriptObject + // new class extending from ScriptObject cw.visit(V1_7, ACC_FINAL | ACC_SUPER, className, null, PROTOTYPEOBJECT_TYPE, null); if (memberCount > 0) { // add fields @@ -155,7 +155,7 @@ */ public static void main(final String[] args) throws IOException { if (args.length != 1) { - System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>"); + System.err.println("Usage: " + PrototypeGenerator.class.getName() + " <class>"); System.exit(1); }
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java Thu Sep 24 10:09:56 2015 -0700 @@ -48,7 +48,7 @@ * */ public final class ScriptClassInfo { - // descriptots for various annotations + // descriptors for various annotations static final String SCRIPT_CLASS_ANNO_DESC = Type.getDescriptor(ScriptClass.class); static final String CONSTRUCTOR_ANNO_DESC = Type.getDescriptor(Constructor.class); static final String FUNCTION_ANNO_DESC = Type.getDescriptor(Function.class); @@ -140,7 +140,7 @@ } boolean isPrototypeNeeded() { - // Prototype class generation is needed if we have atleast one + // Prototype class generation is needed if we have at least one // prototype property or @Constructor defined in the class. for (final MemberInfo memInfo : members) { if (memInfo.getWhere() == Where.PROTOTYPE || memInfo.isConstructor()) {
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java Thu Sep 24 10:09:56 2015 -0700 @@ -118,7 +118,7 @@ addScriptMember(memInfo); return new AnnotationVisitor(Opcodes.ASM4, delegateAV) { - // These could be "null" if values are not suppiled, + // These could be "null" if values are not supplied, // in which case we have to use the default values. private String name; private Integer attributes; @@ -194,7 +194,7 @@ final MemberInfo memInfo = new MemberInfo(); - //annokind == e.g. GETTER or SPECIALIZED_FUNCTION + // annoKind == GETTER or SPECIALIZED_FUNCTION memInfo.setKind(annoKind); memInfo.setJavaName(methodName); memInfo.setJavaDesc(methodDesc); @@ -203,7 +203,7 @@ addScriptMember(memInfo); return new AnnotationVisitor(Opcodes.ASM4, delegateAV) { - // These could be "null" if values are not suppiled, + // These could be "null" if values are not supplied, // in which case we have to use the default values. private String name; private Integer attributes;
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java Thu Sep 24 10:09:56 2015 -0700 @@ -65,7 +65,6 @@ * 2) add "Map" type static field named "$map". * 3) add static initializer block to initialize map. */ - public class ScriptClassInstrumentor extends ClassVisitor { private final ScriptClassInfo scriptClassInfo; private final int memberCount; @@ -267,7 +266,7 @@ */ public static void main(final String[] args) throws IOException { if (args.length != 1) { - System.err.println("Usage: " + ScriptClassInfoCollector.class.getName() + " <class>"); + System.err.println("Usage: " + ScriptClassInstrumentor.class.getName() + " <class>"); System.exit(1); }
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Thu Sep 24 10:00:42 2015 -0700 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Thu Sep 24 10:09:56 2015 -0700 @@ -31,10 +31,9 @@ import java.util.Collections; import java.util.List; import jdk.internal.org.objectweb.asm.Type; -import jdk.nashorn.internal.objects.PrototypeObject; -import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.runtime.AccessorProperty; import jdk.nashorn.internal.runtime.PropertyMap; +import jdk.nashorn.internal.runtime.PrototypeObject; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.Specialization; @@ -88,7 +87,6 @@ static final Type TYPE_PROPERTYMAP = Type.getType(PropertyMap.class); static final Type TYPE_PROTOTYPEOBJECT = Type.getType(PrototypeObject.class); static final Type TYPE_SCRIPTFUNCTION = Type.getType(ScriptFunction.class); - static final Type TYPE_SCRIPTFUNCTIONIMPL = Type.getType(ScriptFunctionImpl.class); static final Type TYPE_SCRIPTOBJECT = Type.getType(ScriptObject.class); static final String PROTOTYPE_SUFFIX = "$Prototype"; @@ -122,17 +120,14 @@ static final String SCRIPTFUNCTION_SETARITY_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE); static final String SCRIPTFUNCTION_SETPROTOTYPE = "setPrototype"; static final String SCRIPTFUNCTION_SETPROTOTYPE_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT); - - // ScriptFunctionImpl - static final String SCRIPTFUNCTIONIMPL_TYPE = TYPE_SCRIPTFUNCTIONIMPL.getInternalName(); - static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION = "makeFunction"; - static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC = + static final String SCRIPTFUNCTION_CREATEBUILTIN = "createBuiltin"; + static final String SCRIPTFUNCTION_CREATEBUILTIN_DESC = Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE); - static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC = + static final String SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC = Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY); - static final String SCRIPTFUNCTIONIMPL_INIT_DESC3 = + static final String SCRIPTFUNCTION_INIT_DESC3 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY); - static final String SCRIPTFUNCTIONIMPL_INIT_DESC4 = + static final String SCRIPTFUNCTION_INIT_DESC4 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_SPECIALIZATION_ARRAY); // ScriptObject
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/exceptionswallow.js Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,136 @@ +#// Usage: jjs exceptionswallow.js -- <directory> + +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This example demonstrates Java subclassing by Java.extend +// and javac Compiler and Tree API. This example looks for +// empty catch blocks ("exception swallow") and reports those. + +if (arguments.length == 0) { + print("Usage: jjs exceptionswallow.js -- <directory>"); + exit(1); +} + +// Java types used +var File = Java.type("java.io.File"); +var Files = Java.type("java.nio.file.Files"); +var StringArray = Java.type("java.lang.String[]"); +var ToolProvider = Java.type("javax.tools.ToolProvider"); +var Tree = Java.type("com.sun.source.tree.Tree"); +var EmptyStatementTree = Java.type("com.sun.source.tree.EmptyStatementTree"); +var Trees = Java.type("com.sun.source.util.Trees"); +var TreeScanner = Java.type("com.sun.source.util.TreeScanner"); + +// printEmptyCatch + +function printEmptyCatch() { + // get the system compiler tool + var compiler = ToolProvider.systemJavaCompiler; + // get standard file manager + var fileMgr = compiler.getStandardFileManager(null, null, null); + // Using Java.to convert script array (arguments) to a Java String[] + var compUnits = fileMgr.getJavaFileObjects( + Java.to(arguments, StringArray)); + // create a new compilation task + var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); + + // SourcePositions object to get positions of AST nodes + var sourcePositions = Trees.instance(task).sourcePositions; + + // subclass SimpleTreeVisitor - to print empty catch + var EmptyCatchFinder = Java.extend(TreeScanner); + + function hasOnlyEmptyStats(stats) { + var itr = stats.iterator(); + while (itr.hasNext()) { + if (! (itr.next() instanceof EmptyStatementTree)) { + return false; + } + } + + return true; + } + + var visitor = new EmptyCatchFinder() { + // current CompilationUnitTree + compUnit: null, + // current LineMap (pos -> line, column) + lineMap: null, + // current compilation unit's file name + fileName: null, + + // overrides of TreeScanner methods + + visitCompilationUnit: function(node, p) { + // capture info about current Compilation unit + this.compUnit = node; + this.lineMap = node.lineMap; + this.fileName = node.sourceFile.name; + + // Using Java.super API to call super class method here + return Java.super(visitor).visitCompilationUnit(node, p); + }, + + visitCatch: function (node, p) { + var stats = node.block.statements; + if (stats.empty || hasOnlyEmptyStats(stats)) { + // print information on this empty catch + var pos = sourcePositions.getStartPosition(this.compUnit, node); + var line = this.lineMap.getLineNumber(pos); + var col = this.lineMap.getColumnNumber(pos); + print("Exception swallow" + " @ " + this.fileName + ":" + line + ":" + col); + // print(node); + } + } + } + + for each (var cu in task.parse()) { + cu.accept(visitor, null); + } +} + +// for each ".java" file in directory (recursively) and check it! +function main(dir) { + Files.walk(dir.toPath()). + forEach(function(p) { + var name = p.toFile().absolutePath; + if (name.endsWith(".java")) { + try { + printEmptyCatch(p.toFile().getAbsolutePath()); + } catch (e) { + print(e); + } + } + }); +} + +main(new File(arguments[0]));
--- a/samples/find_nonfinals2.js Thu Sep 24 10:00:42 2015 -0700 +++ b/samples/find_nonfinals2.js Thu Sep 24 10:09:56 2015 -0700 @@ -43,7 +43,6 @@ // Java types used var File = Java.type("java.io.File"); var Files = Java.type("java.nio.file.Files"); -var FileVisitOption = Java.type("java.nio.file.FileVisitOption"); var StringArray = Java.type("java.lang.String[]"); var ToolProvider = Java.type("javax.tools.ToolProvider"); var Tree = Java.type("com.sun.source.tree.Tree"); @@ -106,7 +105,7 @@ // for each ".java" file in directory (recursively). function main(dir) { var totalCount = 0; - Files.walk(dir.toPath(), FileVisitOption.FOLLOW_LINKS). + Files.walk(dir.toPath()). forEach(function(p) { var name = p.toFile().absolutePath; if (name.endsWith(".java")) {
--- a/samples/javafoovars.js Thu Sep 24 10:00:42 2015 -0700 +++ b/samples/javafoovars.js Thu Sep 24 10:09:56 2015 -0700 @@ -42,7 +42,6 @@ // Java types used var File = Java.type("java.io.File"); var Files = Java.type("java.nio.file.Files"); -var FileVisitOption = Java.type("java.nio.file.FileVisitOption"); var StringArray = Java.type("java.lang.String[]"); var ToolProvider = Java.type("javax.tools.ToolProvider"); var Tree = Java.type("com.sun.source.tree.Tree"); @@ -81,7 +80,7 @@ // for each ".java" file in directory (recursively) count "foo". function main(dir) { var totalCount = 0; - Files.walk(dir.toPath(), FileVisitOption.FOLLOW_LINKS). + Files.walk(dir.toPath()). forEach(function(p) { var name = p.toFile().absolutePath; if (name.endsWith(".java")) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/resourcetrysuggester.js Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,156 @@ +#// Usage: jjs resourcetrysuggester.js -- <directory> + +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This example demonstrates Java subclassing by Java.extend +// and javac Compiler and Tree API. This example looks for +// finally clauses with "close" call and suggests "resource try"! + +if (arguments.length == 0) { + print("Usage: jjs resourcetrysuggester.js -- <directory>"); + exit(1); +} + +// Java types used +var ExpressionStatementTree = Java.type("com.sun.source.tree.ExpressionStatementTree"); +var File = Java.type("java.io.File"); +var Files = Java.type("java.nio.file.Files"); +var MemberSelectTree = Java.type("com.sun.source.tree.MemberSelectTree"); +var MethodInvocationTree = Java.type("com.sun.source.tree.MethodInvocationTree"); +var StringArray = Java.type("java.lang.String[]"); +var ToolProvider = Java.type("javax.tools.ToolProvider"); +var Tree = Java.type("com.sun.source.tree.Tree"); +var Trees = Java.type("com.sun.source.util.Trees"); +var TreeScanner = Java.type("com.sun.source.util.TreeScanner"); + +// resourceTrySuggestions + +function resourceTrySuggestions() { + // get the system compiler tool + var compiler = ToolProvider.systemJavaCompiler; + // get standard file manager + var fileMgr = compiler.getStandardFileManager(null, null, null); + // Using Java.to convert script array (arguments) to a Java String[] + var compUnits = fileMgr.getJavaFileObjects( + Java.to(arguments, StringArray)); + // create a new compilation task + var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); + + // SourcePositions object to get positions of AST nodes + var sourcePositions = Trees.instance(task).sourcePositions; + + // subclass SimpleTreeVisitor - to print resource try suggestions + var ResourceTrySuggester = Java.extend(TreeScanner); + + function hasOnlyEmptyStats(stats) { + var itr = stats.iterator(); + while (itr.hasNext()) { + if (! (itr.next() instanceof EmptyStatementTree)) { + return false; + } + } + + return true; + } + + // does the given statement list has an expression statement which + // calls "close" method (don't worry about types - just crude one will do) + function hasCloseCall(stats) { + var itr = stats.iterator(); + while (itr.hasNext()) { + var stat = itr.next(); + if (stat instanceof ExpressionStatementTree) { + var expr = stat.expression; + if (expr instanceof MethodInvocationTree) { + var method = expr.methodSelect; + if (method instanceof MemberSelectTree) { + return method.identifier.toString().equals("close"); + } + } + } + } + return false; + } + + var visitor = new ResourceTrySuggester() { + // current CompilationUnitTree + compUnit: null, + // current LineMap (pos -> line, column) + lineMap: null, + // current compilation unit's file name + fileName: null, + + // overrides of TreeScanner methods + + visitCompilationUnit: function(node, p) { + // capture info about current Compilation unit + this.compUnit = node; + this.lineMap = node.lineMap; + this.fileName = node.sourceFile.name; + + // Using Java.super API to call super class method here + return Java.super(visitor).visitCompilationUnit(node, p); + }, + + visitTry: function (node, p) { + var finallyBlk = node.finallyBlock; + if (finallyBlk != null && hasCloseCall(finallyBlk.statements)) { + var pos = sourcePositions.getStartPosition(this.compUnit, node); + var line = this.lineMap.getLineNumber(pos); + var col = this.lineMap.getColumnNumber(pos); + print("Consider resource try statement " + " @ " + this.fileName + ":" + line + ":" + col); + // print(node); + } + } + } + + for each (var cu in task.parse()) { + cu.accept(visitor, null); + } +} + +// for each ".java" file in directory (recursively) and check it! +function main(dir) { + Files.walk(dir.toPath()). + forEach(function(p) { + var name = p.toFile().absolutePath; + if (name.endsWith(".java")) { + try { + resourceTrySuggestions(p.toFile().getAbsolutePath()); + } catch (e) { + print(e); + } + } + }); +} + +main(new File(arguments[0]));
--- a/samples/zipfs.js Thu Sep 24 10:00:42 2015 -0700 +++ b/samples/zipfs.js Thu Sep 24 10:09:56 2015 -0700 @@ -36,13 +36,12 @@ var Files = Java.type("java.nio.file.Files") var FileSystems = Java.type("java.nio.file.FileSystems") -var FileVisitOption = Java.type("java.nio.file.FileVisitOption") var Paths = Java.type("java.nio.file.Paths") var zipfile = Paths.get(arguments[0]) var fs = FileSystems.newFileSystem(zipfile, null) var root = fs.rootDirectories[0] -Files.walk(root, FileVisitOption.FOLLOW_LINKS).forEach( +Files.walk(root).forEach( function(p) (print(p), print(Files.readAttributes(p, "zip:*"))) ) fs.close()
--- a/src/jdk/internal/dynalink/DynamicLinker.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/DynamicLinker.java Thu Sep 24 10:09:56 2015 -0700 @@ -117,7 +117,7 @@ * return factory.createLinker(); * } * - * public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) { + * public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) { * return dynamicLinker.link(new MonomorphicCallSite(CallSiteDescriptorFactory.create(lookup, name, type))); * } * }
--- a/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Thu Sep 24 10:09:56 2015 -0700 @@ -107,7 +107,7 @@ private final AccessibleObject target; private final MethodType type; - public CallerSensitiveDynamicMethod(final AccessibleObject target) { + CallerSensitiveDynamicMethod(final AccessibleObject target) { super(getName(target)); this.target = target; this.type = getMethodType(target); @@ -115,8 +115,9 @@ private static String getName(final AccessibleObject target) { final Member m = (Member)target; - return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(), - m.getName())); + final boolean constructor = m instanceof Constructor; + return getMethodNameWithSignature(getMethodType(target), constructor ? m.getName() : + getClassAndMethodName(m.getDeclaringClass(), m.getName()), !constructor); } @Override
--- a/src/jdk/internal/dynalink/beans/FacetIntrospector.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/beans/FacetIntrospector.java Thu Sep 24 10:09:56 2015 -0700 @@ -152,7 +152,7 @@ boolean isAccessible(final Member m) { final Class<?> declaring = m.getDeclaringClass(); // (declaring == clazz) is just an optimization - we're calling this only from code that operates on a - // non-restriced class, so if the declaring class is identical to the class being inspected, then forego + // non-restricted class, so if the declaring class is identical to the class being inspected, then forego // a potentially expensive restricted-package check. return declaring == clazz || !CheckRestrictedPackage.isRestrictedClass(declaring); }
--- a/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Thu Sep 24 10:09:56 2015 -0700 @@ -86,7 +86,9 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.text.Collator; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -242,6 +244,35 @@ return methods.getFirst().isConstructor(); } + @Override + public String toString() { + // First gather the names and sort them. This makes it consistent and easier to read. + final List<String> names = new ArrayList<>(methods.size()); + int len = 0; + for (final SingleDynamicMethod m: methods) { + final String name = m.getName(); + len += name.length(); + names.add(name); + } + // Case insensitive sorting, so e.g. "Object" doesn't come before "boolean". + final Collator collator = Collator.getInstance(); + collator.setStrength(Collator.SECONDARY); + Collections.sort(names, collator); + + final String className = getClass().getName(); + // Class name length + length of signatures + 2 chars/per signature for indentation and newline + + // 3 for brackets and initial newline + final int totalLength = className.length() + len + 2 * names.size() + 3; + final StringBuilder b = new StringBuilder(totalLength); + b.append('[').append(className).append('\n'); + for(final String name: names) { + b.append(' ').append(name).append('\n'); + } + b.append(']'); + assert b.length() == totalLength; + return b.toString(); + }; + ClassLoader getClassLoader() { return classLoader; }
--- a/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Thu Sep 24 10:09:56 2015 -0700 @@ -122,13 +122,13 @@ * @param constructor does this represent a constructor? */ SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) { - super(getName(target, clazz, name)); + super(getName(target, clazz, name, constructor)); this.target = target; this.constructor = constructor; } - private static String getName(final MethodHandle target, final Class<?> clazz, final String name) { - return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name)); + private static String getName(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) { + return getMethodNameWithSignature(target.type(), constructor ? name : getClassAndMethodName(clazz, name), !constructor); } @Override
--- a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java Thu Sep 24 10:09:56 2015 -0700 @@ -98,7 +98,6 @@ * target method to a call site type (including mapping variable arity methods to a call site signature with different * arity). * @author Attila Szegedi - * @version $Id: $ */ abstract class SingleDynamicMethod extends DynamicMethod { @@ -143,14 +142,18 @@ return getMethodType().parameterList().equals(method.getMethodType().parameterList()); } - static String getMethodNameWithSignature(final MethodType type, final String methodName) { + static String getMethodNameWithSignature(final MethodType type, final String methodName, final boolean withReturnType) { final String typeStr = type.toString(); final int retTypeIndex = typeStr.lastIndexOf(')') + 1; int secondParamIndex = typeStr.indexOf(',') + 1; if(secondParamIndex == 0) { secondParamIndex = retTypeIndex - 1; } - return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex); + final StringBuilder b = new StringBuilder(); + if (withReturnType) { + b.append(typeStr, retTypeIndex, typeStr.length()).append(' '); + } + return b.append(methodName).append('(').append(typeStr, secondParamIndex, retTypeIndex).toString(); } /**
--- a/src/jdk/internal/dynalink/linker/GuardedInvocation.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/linker/GuardedInvocation.java Thu Sep 24 10:09:56 2015 -0700 @@ -353,7 +353,7 @@ /** * Applies argument filters to both the invocation and the guard (if there is one). - * @param pos the position of the first argumen being filtered + * @param pos the position of the first argument being filtered * @param filters the argument filters * @return a filtered invocation */
--- a/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java Thu Sep 24 10:09:56 2015 -0700 @@ -110,7 +110,7 @@ /** * Check if invocation is cacheable - * @return true if cachable, false otherwise + * @return true if cacheable, false otherwise */ public boolean isCacheable() { return cacheable;
--- a/src/jdk/nashorn/api/scripting/AbstractJSObject.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/api/scripting/AbstractJSObject.java Thu Sep 24 10:09:56 2015 -0700 @@ -182,7 +182,7 @@ /** * Checking whether the given object is an instance of 'this' object. * - * @param instance instace to check + * @param instance instance to check * @return true if the given 'instance' is an instance of this 'function' object */ @Override
--- a/src/jdk/nashorn/api/scripting/JSObject.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/api/scripting/JSObject.java Thu Sep 24 10:09:56 2015 -0700 @@ -141,7 +141,7 @@ /** * Checking whether the given object is an instance of 'this' object. * - * @param instance instace to check + * @param instance instance to check * @return true if the given 'instance' is an instance of this 'function' object */ public boolean isInstance(final Object instance);
--- a/src/jdk/nashorn/api/scripting/NashornException.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/api/scripting/NashornException.java Thu Sep 24 10:09:56 2015 -0700 @@ -51,6 +51,8 @@ private String fileName; // script line number private int line; + // are the line and fileName unknown? + private boolean lineAndFileNameUnknown; // script column number private int column; // underlying ECMA error object - lazily initialized @@ -92,27 +94,10 @@ */ protected NashornException(final String msg, final Throwable cause) { super(msg, cause == null ? null : cause); - // This is not so pretty - but it gets the job done. Note that the stack - // trace has been already filled by "fillInStackTrace" call from - // Throwable - // constructor and so we don't pay additional cost for it. - // Hard luck - no column number info this.column = -1; - - // Find the first JavaScript frame by walking and set file, line from it - // Usually, we should be able to find it in just few frames depth. - for (final StackTraceElement ste : getStackTrace()) { - if (ECMAErrors.isScriptFrame(ste)) { - // Whatever here is compiled from JavaScript code - this.fileName = ste.getFileName(); - this.line = ste.getLineNumber(); - return; - } - } - - this.fileName = null; - this.line = 0; + // We can retrieve the line number and file name from the stack trace if needed + this.lineAndFileNameUnknown = true; } /** @@ -121,6 +106,7 @@ * @return the file name */ public final String getFileName() { + ensureLineAndFileName(); return fileName; } @@ -131,6 +117,7 @@ */ public final void setFileName(final String fileName) { this.fileName = fileName; + lineAndFileNameUnknown = false; } /** @@ -139,6 +126,7 @@ * @return the line number */ public final int getLineNumber() { + ensureLineAndFileName(); return line; } @@ -148,6 +136,7 @@ * @param line the line number */ public final void setLineNumber(final int line) { + lineAndFileNameUnknown = false; this.line = line; } @@ -274,4 +263,19 @@ public void setEcmaError(final Object ecmaError) { this.ecmaError = ecmaError; } + + private void ensureLineAndFileName() { + if (lineAndFileNameUnknown) { + for (final StackTraceElement ste : getStackTrace()) { + if (ECMAErrors.isScriptFrame(ste)) { + // Whatever here is compiled from JavaScript code + fileName = ste.getFileName(); + line = ste.getLineNumber(); + return; + } + } + + lineAndFileNameUnknown = false; + } + } }
--- a/src/jdk/nashorn/api/scripting/ScriptUtils.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java Thu Sep 24 10:09:56 2015 -0700 @@ -79,7 +79,7 @@ * @return a synchronizing wrapper function */ public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) { - return func.makeSynchronizedFunction(unwrap(sync)); + return func.createSynchronized(unwrap(sync)); } /**
--- a/src/jdk/nashorn/api/scripting/URLReader.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/api/scripting/URLReader.java Thu Sep 24 10:09:56 2015 -0700 @@ -103,7 +103,7 @@ /** * Charset used by this reader * - * @return the Chartset used to convert bytes to chars + * @return the Charset used to convert bytes to chars */ public Charset getCharset() { return cs;
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu Sep 24 10:09:56 2015 -0700 @@ -40,7 +40,6 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; @@ -298,7 +297,28 @@ @Override public boolean enterFunctionNode(final FunctionNode functionNode) { - if (!USE_APPLY2CALL) { + // Cheap tests first + if (!( + // is the transform globally enabled? + USE_APPLY2CALL + + // Are we compiling lazily? We can't known the number and types of the actual parameters at + // the caller when compiling eagerly, so this only works with on-demand compilation. + && compiler.isOnDemandCompilation() + + // Does the function even reference the "arguments" identifier (without redefining it)? If not, + // it trivially can't have an expression of form "f.apply(self, arguments)" that this transform + // is targeting. + && functionNode.needsArguments() + + // Does the function have eval? If so, it can arbitrarily modify arguments so we can't touch it. + && !functionNode.hasEval() + + // Finally, does the function declare any parameters explicitly? We don't support that. It could + // be done, but has some complications. Therefore only a function with no explicit parameters + // is considered. + && functionNode.getNumOfParams() == 0)) + { return false; } @@ -308,18 +328,6 @@ return false; } - if (!compiler.isOnDemandCompilation()) { - return false; - } - - if (functionNode.getNumOfParams() != 0) { - return false; - } - - if (functionNode.hasEval()) { - return false; - } - if (!hasApplies(functionNode)) { return false; } @@ -375,7 +383,7 @@ callSiteTypes.pop(); explodedArguments.pop(); - return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED); + return newFunctionNode; } private static boolean isApply(final CallNode callNode) {
--- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Sep 24 10:09:56 2015 -0700 @@ -65,7 +65,6 @@ import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LexicalContext; @@ -149,12 +148,14 @@ private final Deque<Set<String>> thisProperties = new ArrayDeque<>(); private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol private final Compiler compiler; + private final boolean isOnDemand; public AssignSymbols(final Compiler compiler) { super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); this.debug = log.isEnabled(); + this.isOnDemand = compiler.isOnDemandCompilation(); } @Override @@ -242,7 +243,7 @@ /** * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically - * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function + * used to create assignment of {@code :callee} to the function name symbol in self-referential function * expressions as well as for assignment of {@code :arguments} to {@code arguments}. * * @param name the ident node identifying the variable to initialize @@ -390,7 +391,7 @@ // Create and add to appropriate block. symbol = createSymbol(name, flags); - symbolBlock.putSymbol(lc, symbol); + symbolBlock.putSymbol(symbol); if ((flags & IS_SCOPE) == 0) { // Initial assumption; symbol can lose its slot later @@ -440,7 +441,7 @@ start(block); if (lc.isFunctionBody()) { - block.clearSymbols(); + assert !block.hasSymbols(); final FunctionNode fn = lc.getCurrentFunction(); if (isUnparsedFunction(fn)) { // It's a skipped nested function. Just mark the symbols being used by it as being in use. @@ -459,7 +460,7 @@ } private boolean isUnparsedFunction(final FunctionNode fn) { - return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); + return isOnDemand && fn != lc.getOutermostFunction(); } @Override @@ -747,28 +748,6 @@ } } - @Override - public Node leaveBlock(final Block block) { - // It's not necessary to guard the marking of symbols as locals with this "if" condition for - // correctness, it's just an optimization -- runtime type calculation is not used when the compilation - // is not an on-demand optimistic compilation, so we can skip locals marking then. - if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { - // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand - // compilation, and we're skipping parsing the function bodies for nested functions, this - // basically only means their parameters. It'd be enough to mistakenly declare to be a local a - // symbol in the outer function named the same as one of the parameters, though. - if (lc.getFunction(block) == lc.getOutermostFunction()) { - for (final Symbol symbol: block.getSymbols()) { - if (!symbol.isScope()) { - assert symbol.isVar() || symbol.isParam(); - compiler.declareLocalSymbol(symbol.getName()); - } - } - } - } - return block; - } - private Node leaveDELETE(final UnaryNode unaryNode) { final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final boolean strictMode = currentFunctionNode.isStrict(); @@ -786,9 +765,9 @@ if (symbol.isThis()) { // Can't delete "this", ignore and return true - return LiteralNode.newInstance(unaryNode, true).accept(this); + return LiteralNode.newInstance(unaryNode, true); } - final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); + final Expression literalNode = LiteralNode.newInstance(unaryNode, name); final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))); if (!failDelete) { @@ -799,7 +778,7 @@ if (failDelete) { request = Request.FAIL_DELETE; - } else if (symbol.isGlobal() && !symbol.isFunctionDeclaration()) { + } else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) { request = Request.SLOW_DELETE; } } else if (rhs instanceof AccessNode) { @@ -807,7 +786,7 @@ final String property = ((AccessNode)rhs).getProperty(); args.add(base); - args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this)); + args.add(LiteralNode.newInstance(unaryNode, property)); args.add(strictFlagNode); } else if (rhs instanceof IndexNode) { @@ -820,15 +799,15 @@ args.add(strictFlagNode); } else { - return LiteralNode.newInstance(unaryNode, true).accept(this); + return LiteralNode.newInstance(unaryNode, true); } - return new RuntimeNode(unaryNode, request, args).accept(this); + return new RuntimeNode(unaryNode, request, args); } @Override public Node leaveForNode(final ForNode forNode) { if (forNode.isForIn()) { - forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 + return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 } return end(forNode); @@ -848,7 +827,7 @@ lc.applyTopFlags(functionNode)))) .setThisProperties(lc, thisProperties.pop().size())); } - return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED); + return finalizedFunction; } @Override @@ -904,19 +883,18 @@ public Node leaveSwitchNode(final SwitchNode switchNode) { // We only need a symbol for the tag if it's not an integer switch node if(!switchNode.isUniqueInteger()) { - switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); + return switchNode.setTag(lc, newObjectInternal(SWITCH_TAG_PREFIX)); } return switchNode; } @Override public Node leaveTryNode(final TryNode tryNode) { - tryNode.setException(exceptionSymbol()); assert tryNode.getFinallyBody() == null; end(tryNode); - return tryNode; + return tryNode.setException(lc, exceptionSymbol()); } private Node leaveTYPEOF(final UnaryNode unaryNode) { @@ -925,13 +903,13 @@ final List<Expression> args = new ArrayList<>(); if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { args.add(compilerConstantIdentifier(SCOPE)); - args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null + args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName())); //null } else { args.add(rhs); - args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' + args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } - final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); + final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); end(unaryNode); @@ -939,7 +917,7 @@ } private FunctionNode markProgramBlock(final FunctionNode functionNode) { - if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) { + if (isOnDemand || !functionNode.isProgram()) { return functionNode; }
--- a/src/jdk/nashorn/internal/codegen/AstSerializer.java Thu Sep 24 10:00:42 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * 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.codegen; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.util.Collections; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; -import jdk.nashorn.internal.ir.Block; -import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.LexicalContext; -import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.Statement; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; -import jdk.nashorn.internal.runtime.options.Options; - -/** - * This static utility class performs serialization of FunctionNode ASTs to a byte array. - * The format is a standard Java serialization stream, deflated. - */ -final class AstSerializer { - // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed - // and size. - private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); - static byte[] serialize(final FunctionNode fn) { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final Deflater deflater = new Deflater(COMPRESSION_LEVEL); - try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { - oout.writeObject(removeInnerFunctionBodies(fn)); - } catch (final IOException e) { - throw new AssertionError("Unexpected exception serializing function", e); - } finally { - deflater.end(); - } - return out.toByteArray(); - } - - private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) { - return (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public Node leaveBlock(final Block block) { - if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { - return block.setStatements(lc, Collections.<Statement>emptyList()); - } - return super.leaveBlock(block); - } - }); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/CacheAst.java Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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.codegen; + +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; + +class CacheAst extends NodeVisitor<LexicalContext> { + private final Deque<RecompilableScriptFunctionData> dataStack = new ArrayDeque<>(); + + private final Compiler compiler; + + CacheAst(final Compiler compiler) { + super(new LexicalContext()); + this.compiler = compiler; + assert !compiler.isOnDemandCompilation(); + } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + final int id = functionNode.getId(); + // It isn't necessary to keep a stack of RecompilableScriptFunctionData, but then we'd need to do a + // potentially transitive lookup with compiler.getScriptFunctionData(id) for deeper functions; this way + // we keep it constant time. + dataStack.push(dataStack.isEmpty() ? compiler.getScriptFunctionData(id) : dataStack.peek().getScriptFunctionData(id)); + return true; + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + final RecompilableScriptFunctionData data = dataStack.pop(); + if (functionNode.isSplit()) { + // NOTE: cache only split function ASTs from eager pass. Caching non-split functions would require + // some additional work, namely creating the concept of "uncacheable" function and reworking + // ApplySpecialization to ensure that functions undergoing apply-to-call transformations are not + // cacheable as well as recomputing Symbol.useCount when caching the eagerly parsed AST. + // Recomputing Symbol.useCount would be needed so it will only reflect uses from within the + // function being cached (and not reflect uses from its own nested functions or functions it is + // nested in). This is consistent with the count an on-demand recompilation of the function would + // produce. This is important as the decision to emit shared scope calls is based on this count, + // and if it is not matched between a previous version of the code and its deoptimizing rest-of + // compilation, it can result in rest-of not emitting a shared scope call where a previous version + // of the code (compiled from a cached eager pre-pass seeing higher (global) useCount) would emit + // it, causing a mismatch in stack shapes between previous code and its rest-of. + data.setCachedAst(functionNode); + } + + if (!dataStack.isEmpty() && ((dataStack.peek().getFunctionFlags() & FunctionNode.IS_SPLIT) != 0)) { + // Return a function node with no body so that caching outer functions doesn't hold on to nested + // functions' bodies. Note we're doing this only for functions directly nested inside split + // functions, since we're only caching the split ones. It is not necessary to limit body removal + // to just these functions, but it's a cheap way to prevent unnecessary AST mutations. + return functionNode.setBody(lc, functionNode.getBody().setStatements(null, Collections.<Statement>emptyList())); + } + return functionNode; + } +}
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Sep 24 10:09:56 2015 -0700 @@ -93,7 +93,6 @@ import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; @@ -132,7 +131,6 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.objects.Global; -import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; @@ -195,9 +193,9 @@ private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class, "ensureNumber", double.class, Object.class, int.class); - private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class, + private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunction.class, "create", ScriptFunction.class, Object[].class, int.class, ScriptObject.class); - private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class, + private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunction.class, "create", ScriptFunction.class, Object[].class, int.class); private static final Call TO_NUMBER_FOR_EQ = CompilerConstants.staticCallNoLookup(JSType.class, @@ -249,7 +247,7 @@ private final Set<String> emittedMethods = new HashSet<>(); // Function Id -> ContinuationInfo. Used by compilation of rest-of function only. - private final Map<Integer, ContinuationInfo> fnIdToContinuationInfo = new HashMap<>(); + private ContinuationInfo continuationInfo; private final Deque<Label> scopeEntryLabels = new ArrayDeque<>(); @@ -349,11 +347,20 @@ final int flags = getScopeCallSiteFlags(symbol); if (isFastScope(symbol)) { // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope. - if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !isOptimisticOrRestOf()) { - method.loadCompilerConstant(SCOPE); - // As shared scope vars are only used in non-optimistic compilation, we switch from using TypeBounds to + if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !identNode.isOptimistic()) { + // As shared scope vars are only used with non-optimistic identifiers, we switch from using TypeBounds to // just a single definitive type, resultBounds.widest. - loadSharedScopeVar(resultBounds.widest, symbol, flags); + new OptimisticOperation(identNode, TypeBounds.OBJECT) { + @Override + void loadStack() { + method.loadCompilerConstant(SCOPE); + } + + @Override + void consumeStack() { + loadSharedScopeVar(resultBounds.widest, symbol, flags); + } + }.emit(); } else { new LoadFastScopeVar(identNode, resultBounds, flags).emit(); } @@ -384,10 +391,6 @@ return continuationEntryPoints != null; } - private boolean isOptimisticOrRestOf() { - return useOptimisticTypes() || isRestOf(); - } - private boolean isCurrentContinuationEntryPoint(final int programPoint) { return isRestOf() && getCurrentContinuationEntryPoint() == programPoint; } @@ -464,12 +467,8 @@ } private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) { - assert !isOptimisticOrRestOf(); - if (isFastScope(symbol)) { - method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol)); - } else { - method.load(-1); - } + assert isFastScope(symbol); + method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol)); return lc.getScopeGet(unit, symbol, valueType, flags).generateInvoke(method); } @@ -1494,7 +1493,7 @@ int argsCount; @Override void loadStack() { - /** + /* * We want to load 'eval' to check if it is indeed global builtin eval. * If this eval call is inside a 'with' statement, dyn:getMethod|getProp|getElem * would be generated if ident is a "isFunction". But, that would result in a @@ -1573,7 +1572,7 @@ } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD || !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD || CodeGenerator.this.lc.inDynamicScope() - || isOptimisticOrRestOf()) { + || callNode.isOptimistic()) { scopeCall(node, flags); } else { sharedScopeCall(node, flags); @@ -2070,8 +2069,6 @@ @Override public boolean enterFunctionNode(final FunctionNode functionNode) { - final int fnId = functionNode.getId(); - if (skipFunction(functionNode)) { // In case we are not generating code for the function, we must create or retrieve the function object and // load it on the stack here. @@ -2109,9 +2106,9 @@ method.begin(); if (isRestOf()) { - final ContinuationInfo ci = new ContinuationInfo(); - fnIdToContinuationInfo.put(fnId, ci); - method.gotoLoopStart(ci.getHandlerLabel()); + assert continuationInfo == null; + continuationInfo = new ContinuationInfo(); + method.gotoLoopStart(continuationInfo.getHandlerLabel()); } } @@ -2144,7 +2141,7 @@ markOptimistic = false; } - FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.BYTECODE_GENERATED); + FunctionNode newFunctionNode = functionNode; if (markOptimistic) { newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_DEOPTIMIZABLE); } @@ -4331,7 +4328,7 @@ } private void prologue() { - /** + /* * This loads the parts of the target, e.g base and index. they are kept * on the stack throughout the store and used at the end to execute it */ @@ -4799,7 +4796,7 @@ * conversion has no side effects. * @param name the name of the property being get * @param flags call site flags - * @param isMethod whether we're preferrably retrieving a function + * @param isMethod whether we're preferably retrieving a function * @return the current method emitter */ MethodEmitter dynamicGet(final String name, final int flags, final boolean isMethod, final boolean isIndex) { @@ -5231,7 +5228,7 @@ private Type returnValueType; // If we are in the middle of an object literal initialization, we need to update the map private PropertyMap objectLiteralMap; - // Object literal stack depth for object literal - not necessarly top if property is a tree + // Object literal stack depth for object literal - not necessarily top if property is a tree private int objectLiteralStackDepth = -1; // The line number at the continuation point private int lineNumber; @@ -5308,7 +5305,7 @@ } private ContinuationInfo getContinuationInfo() { - return fnIdToContinuationInfo.get(lc.getCurrentFunction().getId()); + return continuationInfo; } private void generateContinuationHandler() { @@ -5396,7 +5393,7 @@ method.load(lvarTypes.get(slot), slot); method.convert(stackTypes[i]); // stack: s0=object literal being initialized - // change map of s0 so that the property we are initilizing when we failed + // change map of s0 so that the property we are initializing when we failed // is now ci.returnValueType if (i == objectLiteralStackDepth) { method.dup();
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Sep 24 10:09:56 2015 -0700 @@ -25,34 +25,21 @@ package jdk.nashorn.internal.codegen; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.OPTIMISTIC_TYPES_ASSIGNED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; import java.io.PrintWriter; -import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; +import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -65,15 +52,9 @@ * A compilation phase is a step in the processes of turning a JavaScript * FunctionNode into bytecode. It has an optional return value. */ -enum CompilationPhase { - /** - * Constant folding pass Simple constant folding that will make elementary - * constructs go away - */ - CONSTANT_FOLDING_PHASE( - EnumSet.of( - INITIALIZED, - PARSED)) { +abstract class CompilationPhase { + + private static final class ConstantFoldingPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new FoldConstants(compiler)); @@ -83,20 +64,15 @@ public String toString() { return "'Constant Folding'"; } - }, + } /** - * Lower (Control flow pass) Finalizes the control flow. Clones blocks for - * finally constructs and similar things. Establishes termination criteria - * for nodes Guarantee return instructions to method making sure control - * flow cannot fall off the end. Replacing high level nodes with lower such - * as runtime nodes where applicable. + * Constant folding pass Simple constant folding that will make elementary + * constructs go away */ - LOWERING_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED)) { + static final CompilationPhase CONSTANT_FOLDING_PHASE = new ConstantFoldingPhase(); + + private static final class LoweringPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new Lower(compiler)); @@ -106,42 +82,35 @@ public String toString() { return "'Control Flow Lowering'"; } - }, + } /** - * Phase used only when doing optimistic code generation. It assigns all potentially - * optimistic ops a program point so that an UnwarrantedException knows from where - * a guess went wrong when creating the continuation to roll back this execution + * Lower (Control flow pass) Finalizes the control flow. Clones blocks for + * finally constructs and similar things. Establishes termination criteria + * for nodes Guarantee return instructions to method making sure control + * flow cannot fall off the end. Replacing high level nodes with lower such + * as runtime nodes where applicable. */ - TRANSFORM_BUILTINS_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED)) { - //we only do this if we have a param type map, otherwise this is not a specialized recompile + static final CompilationPhase LOWERING_PHASE = new LoweringPhase(); + + private static final class ApplySpecializationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return setStates(transformFunction(fn, new ApplySpecialization(compiler)), BUILTINS_TRANSFORMED); + return transformFunction(fn, new ApplySpecialization(compiler)); } @Override public String toString() { return "'Builtin Replacement'"; } - }, + }; /** - * Splitter Split the AST into several compile units based on a heuristic size calculation. - * Split IR can lead to scope information being changed. + * Phase used to transform Function.prototype.apply. */ - SPLITTING_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED)) { + static final CompilationPhase APPLY_SPECIALIZATION_PHASE = new ApplySpecializationPhase(); + + private static final class SplittingPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L); @@ -168,16 +137,15 @@ public String toString() { return "'Code Splitting'"; } - }, + }; - PROGRAM_POINT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + /** + * Splitter Split the AST into several compile units based on a heuristic size calculation. + * Split IR can lead to scope information being changed. + */ + static final CompilationPhase SPLITTING_PHASE = new SplittingPhase(); + + private static final class ProgramPointPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new ProgramPoints()); @@ -187,43 +155,34 @@ public String toString() { return "'Program Point Calculation'"; } - }, + }; - SERIALIZE_SPLIT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + static final CompilationPhase PROGRAM_POINT_PHASE = new ProgramPointPhase(); + + private static final class CacheAstPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public boolean enterFunctionNode(final FunctionNode functionNode) { - if (functionNode.isSplit()) { - compiler.serializeAst(functionNode); - } - return true; - } - }); + if (!compiler.isOnDemandCompilation()) { + // Only do this on initial preprocessing of the source code. For on-demand compilations from + // source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function + // being compiled. + transformFunction(fn, new CacheAst(compiler)); + } + // NOTE: we're returning the original fn as we have destructively modified the cached functions by + // removing their bodies. This step is associating FunctionNode objects with + // RecompilableScriptFunctionData; it's not really modifying the AST. + return fn; } @Override public String toString() { - return "'Serialize Split Functions'"; + return "'Cache ASTs'"; } - }, + }; - SYMBOL_ASSIGNMENT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + static final CompilationPhase CACHE_AST_PHASE = new CacheAstPhase(); + + private static final class SymbolAssignmentPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new AssignSymbols(compiler)); @@ -233,17 +192,11 @@ public String toString() { return "'Symbol Assignment'"; } - }, + }; - SCOPE_DEPTH_COMPUTATION_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED)) { + static final CompilationPhase SYMBOL_ASSIGNMENT_PHASE = new SymbolAssignmentPhase(); + + private static final class ScopeDepthComputationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new FindScopeDepths(compiler)); @@ -253,43 +206,66 @@ public String toString() { return "'Scope Depth Computation'"; } - }, + }; + + static final CompilationPhase SCOPE_DEPTH_COMPUTATION_PHASE = new ScopeDepthComputationPhase(); - OPTIMISTIC_TYPE_ASSIGNMENT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED)) { + private static final class DeclareLocalSymbolsPhase extends CompilationPhase { + @Override + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { + // It's not necessary to guard the marking of symbols as locals with this "if" condition for + // correctness, it's just an optimization -- runtime type calculation is not used when the compilation + // is not an on-demand optimistic compilation, so we can skip locals marking then. + if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { + fn.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand + // compilation, and we're skipping parsing the function bodies for nested functions, this + // basically only means their parameters. It'd be enough to mistakenly declare to be a local a + // symbol in the outer function named the same as one of the parameters, though. + return false; + }; + @Override + public boolean enterBlock(final Block block) { + for (final Symbol symbol: block.getSymbols()) { + if (!symbol.isScope()) { + compiler.declareLocalSymbol(symbol.getName()); + } + } + return true; + }; + }); + } + return fn; + } + + @Override + public String toString() { + return "'Local Symbols Declaration'"; + } + }; + + static final CompilationPhase DECLARE_LOCAL_SYMBOLS_PHASE = new DeclareLocalSymbolsPhase(); + + private static final class OptimisticTypeAssignmentPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { if (compiler.useOptimisticTypes()) { return transformFunction(fn, new OptimisticTypesCalculator(compiler)); } - return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED); + return fn; } @Override public String toString() { return "'Optimistic Type Assignment'"; } - }, + } - LOCAL_VARIABLE_TYPE_CALCULATION_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED)) { + static final CompilationPhase OPTIMISTIC_TYPE_ASSIGNMENT_PHASE = new OptimisticTypeAssignmentPhase(); + + private static final class LocalVariableTypeCalculationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler)); @@ -314,25 +290,11 @@ public String toString() { return "'Local Variable Type Calculation'"; } - }, - + }; - /** - * Reuse compile units, if they are already present. We are using the same compiler - * to recompile stuff - */ - REUSE_COMPILE_UNITS_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED, - LOCAL_VARIABLE_TYPES_CALCULATED)) { + static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase(); + + private static final class ReuseCompileUnitsPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods"; @@ -380,16 +342,15 @@ public String toString() { return "'Reuse Compile Units'"; } - }, + } - REINITIALIZE_SERIALIZED( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + /** + * Reuse compile units, if they are already present. We are using the same compiler + * to recompile stuff + */ + static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase(); + + private static final class ReinitializeCachedPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet(); @@ -430,28 +391,13 @@ @Override public String toString() { - return "'Deserialize'"; + return "'Reinitialize cached'"; } - }, + } - /** - * Bytecode generation: - * - * Generate the byte code class(es) resulting from the compiled FunctionNode - */ - BYTECODE_GENERATION_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED, - LOCAL_VARIABLE_TYPES_CALCULATED)) { + static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase(); + private static final class BytecodeGenerationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final ScriptEnvironment senv = compiler.getScriptEnvironment(); @@ -469,7 +415,7 @@ try { // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program // in the lazy + optimistic world. See CodeGenerator.skipFunction(). - newFunctionNode = transformFunction(newFunctionNode, codegen).setState(null, BYTECODE_GENERATED); + newFunctionNode = transformFunction(newFunctionNode, codegen); codegen.generateScopeCalls(); } catch (final VerifyError e) { if (senv._verify_code || senv._print_code) { @@ -517,22 +463,16 @@ public String toString() { return "'Bytecode Generation'"; } - }, + } - INSTALL_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED, - LOCAL_VARIABLE_TYPES_CALCULATED, - BYTECODE_GENERATED)) { + /** + * Bytecode generation: + * + * Generate the byte code class(es) resulting from the compiled FunctionNode + */ + static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase(); + private static final class InstallPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final DebugLogger log = compiler.getLogger(); @@ -600,18 +540,16 @@ log.fine(sb.toString()); } - return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED); + return fn.setRootClass(null, rootClass); } @Override public String toString() { return "'Class Installation'"; } - - }; + } - /** pre conditions required for function node to which this transform is to be applied */ - private final EnumSet<CompilationState> pre; + static final CompilationPhase INSTALL_PHASE = new InstallPhase(); /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ private long startTime; @@ -622,21 +560,7 @@ /** boolean that is true upon transform completion */ private boolean isFinished; - private CompilationPhase(final EnumSet<CompilationState> pre) { - this.pre = pre; - } - - private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) { - if (!AssertsEnabled.assertsEnabled()) { - return functionNode; - } - return transformFunction(functionNode, new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public Node leaveFunctionNode(final FunctionNode fn) { - return fn.setState(lc, state); - } - }); - } + private CompilationPhase() {} /** * Start a compilation phase @@ -646,23 +570,7 @@ */ protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) { compiler.getLogger().indent(); - - assert pre != null; - - if (!functionNode.hasState(pre)) { - final StringBuilder sb = new StringBuilder("Compilation phase "); - sb.append(this). - append(" is not applicable to "). - append(quote(functionNode.getName())). - append("\n\tFunctionNode state = "). - append(functionNode.getState()). - append("\n\tRequired state = "). - append(this.pre); - - throw new CompilationException(sb.toString()); - } - - startTime = System.nanoTime(); + startTime = System.nanoTime(); return functionNode; } @@ -697,7 +605,7 @@ abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException; /** - * Apply a transform to a function node, returning the transfored function node. If the transform is not + * Apply a transform to a function node, returning the transformed function node. If the transform is not * applicable, an exception is thrown. Every transform requires the function to have a certain number of * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states.
--- a/src/jdk/nashorn/internal/codegen/Compiler.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Thu Sep 24 10:09:56 2015 -0700 @@ -160,42 +160,40 @@ */ private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; - private final Map<Integer, byte[]> serializedAsts = new HashMap<>(); - /** * Compilation phases that a compilation goes through */ public static class CompilationPhases implements Iterable<CompilationPhase> { /** - * Singleton that describes compilation up to the phase where a function can be serialized. + * Singleton that describes compilation up to the phase where a function can be cached. */ - private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases( + private final static CompilationPhases COMPILE_UPTO_CACHED = new CompilationPhases( "Common initial phases", CompilationPhase.CONSTANT_FOLDING_PHASE, CompilationPhase.LOWERING_PHASE, - CompilationPhase.TRANSFORM_BUILTINS_PHASE, + CompilationPhase.APPLY_SPECIALIZATION_PHASE, CompilationPhase.SPLITTING_PHASE, CompilationPhase.PROGRAM_POINT_PHASE, - CompilationPhase.SERIALIZE_SPLIT_PHASE + CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, + CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, + CompilationPhase.CACHE_AST_PHASE ); - private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases( + private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( "After common phases, before bytecode generator", - CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, - CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE ); /** - * Singleton that describes additional steps to be taken after deserializing, all the way up to (but not - * including) generating and installing code. + * Singleton that describes additional steps to be taken after retrieving a cached function, all the + * way up to (but not including) generating and installing code. */ - public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases( - "Recompile serialized function up to bytecode", - CompilationPhase.REINITIALIZE_SERIALIZED, - COMPILE_SERIALIZABLE_UPTO_BYTECODE + public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( + "Recompile cached function up to bytecode", + CompilationPhase.REINITIALIZE_CACHED, + COMPILE_CACHED_UPTO_BYTECODE ); /** @@ -211,8 +209,8 @@ /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( "Compile upto bytecode", - COMPILE_UPTO_SERIALIZABLE, - COMPILE_SERIALIZABLE_UPTO_BYTECODE); + COMPILE_UPTO_CACHED, + COMPILE_CACHED_UPTO_BYTECODE); /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( @@ -227,9 +225,9 @@ GENERATE_BYTECODE_AND_INSTALL); /** Singleton that describes a full compilation - this includes code installation - from serialized state*/ - public final static CompilationPhases COMPILE_ALL_SERIALIZED = new CompilationPhases( + public final static CompilationPhases COMPILE_ALL_CACHED = new CompilationPhases( "Eager compilation from serializaed state", - RECOMPILE_SERIALIZED_UPTO_BYTECODE, + RECOMPILE_CACHED_UPTO_BYTECODE, GENERATE_BYTECODE_AND_INSTALL); /** @@ -248,9 +246,9 @@ GENERATE_BYTECODE_AND_INSTALL_RESTOF); /** Compile from serialized for a rest of method */ - public final static CompilationPhases COMPILE_SERIALIZED_RESTOF = new CompilationPhases( + public final static CompilationPhases COMPILE_CACHED_RESTOF = new CompilationPhases( "Compile serialized, rest of", - RECOMPILE_SERIALIZED_UPTO_BYTECODE, + RECOMPILE_CACHED_UPTO_BYTECODE, GENERATE_BYTECODE_AND_INSTALL_RESTOF); private final List<CompilationPhase> phases; @@ -313,7 +311,7 @@ } boolean isRestOfCompilation() { - return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_SERIALIZED_RESTOF; + return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_CACHED_RESTOF; } String getDesc() { @@ -766,14 +764,6 @@ compileUnits.addAll(newUnits); } - void serializeAst(final FunctionNode fn) { - serializedAsts.put(fn.getId(), AstSerializer.serialize(fn)); - } - - byte[] removeSerializedAst(final int fnId) { - return serializedAsts.remove(fnId); - } - CompileUnit findUnit(final long weight) { for (final CompileUnit unit : compileUnits) { if (unit.canHold(weight)) {
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java Thu Sep 24 10:09:56 2015 -0700 @@ -192,7 +192,7 @@ private static Set<String> symbolNames; /** - * Prefix used for internal methods generated in script clases. + * Prefix used for internal methods generated in script classes. */ private static final String INTERNAL_METHOD_PREFIX = ":"; @@ -225,7 +225,7 @@ } /** - * Check whether a name is that of a reserved compiler constnat + * Check whether a name is that of a reserved compiler constant * @param name name * @return true if compiler constant name */
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Sep 24 10:09:56 2015 -0700 @@ -34,7 +34,6 @@ import java.util.Set; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; @@ -182,14 +181,16 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { final String name = functionNode.getName(); - FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.SCOPE_DEPTHS_COMPUTED); - + FunctionNode newFunctionNode = functionNode; if (compiler.isOnDemandCompilation()) { final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId()); if (data.inDynamicContext()) { log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope."); newFunctionNode = newFunctionNode.setInDynamicContext(lc); } + if (newFunctionNode == lc.getOutermostFunction() && !newFunctionNode.hasApplyToCallSpecialization()) { + data.setCachedAst(newFunctionNode); + } return newFunctionNode; } @@ -210,8 +211,7 @@ ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()), nestedFunctions, externalSymbolDepths.get(fnId), - internalSymbols.get(fnId), - compiler.removeSerializedAst(fnId)); + internalSymbols.get(fnId)); if (lc.getOutermostFunction() != newFunctionNode) { final FunctionNode parentFn = lc.getParentFunction(newFunctionNode);
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java Thu Sep 24 10:09:56 2015 -0700 @@ -37,7 +37,6 @@ import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; @@ -101,7 +100,7 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { - return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED); + return functionNode; } @Override
--- a/src/jdk/nashorn/internal/codegen/Label.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/Label.java Thu Sep 24 10:09:56 2015 -0700 @@ -447,7 +447,7 @@ undefineLocalVariables(liveLocalCount, true); // Temporaries are promoted firstTemp = liveLocalCount; - // No trailing undefineds + // No trailing undefined values localVariableTypes.subList(firstTemp, localVariableTypes.size()).clear(); assert symbolBoundary.length() == firstTemp; // Generalize all reference types to Object, and promote boolean to int @@ -497,7 +497,7 @@ private transient Label.Stack stack; /** ASM representation of this label */ - private jdk.internal.org.objectweb.asm.Label label; + private transient jdk.internal.org.objectweb.asm.Label label; /** Id for debugging purposes, remove if footprint becomes unmanageable */ private final int id;
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Thu Sep 24 10:09:56 2015 -0700 @@ -54,7 +54,6 @@ import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; @@ -1478,7 +1477,6 @@ newFunction = newFunction.setReturnType(lc, returnType); - newFunction = newFunction.setState(lc, CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED); newFunction = newFunction.setParameters(lc, newFunction.visitParameters(applyChangesVisitor)); return newFunction; }
--- a/src/jdk/nashorn/internal/codegen/Lower.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Thu Sep 24 10:09:56 2015 -0700 @@ -51,7 +51,6 @@ import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; @@ -266,7 +265,7 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { log.info("END FunctionNode: ", functionNode.getName()); - return functionNode.setState(lc, CompilationState.LOWERED); + return functionNode; } @Override @@ -511,7 +510,7 @@ } /* - * create a new trynode + * create a new try node * if we have catches: * * try try @@ -522,7 +521,7 @@ * catchall * rethrow * - * otheriwse + * otherwise * * try try * x x
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Sep 24 10:09:56 2015 -0700 @@ -1159,7 +1159,7 @@ /** * Pop a value from the stack and store it in a variable denoted by the given symbol. The variable should be either * a local variable, or a function parameter (and not a scoped variable). For local variables, this method will also - * do the bookeeping of the local variable table as well as mark values in all alternative slots for the symbol as + * do the bookkeeping of the local variable table as well as mark values in all alternative slots for the symbol as * dead. In this regard it differs from {@link #storeHidden(Type, int)}. * * @param symbol the symbol to store into.
--- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Thu Sep 24 10:09:56 2015 -0700 @@ -786,7 +786,7 @@ * @param primitiveSetter primitive setter for the current type with an element of the current type * @param objectSetter the object setter * - * @return method handle that checks if the element to be set is of the currenttype, even though it's boxed + * @return method handle that checks if the element to be set is of the current type, even though it's boxed * and instead of using the generic object setter, that would blow up the type and invalidate the map, * unbox it and call the primitive setter instead */
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Thu Sep 24 10:09:56 2015 -0700 @@ -38,7 +38,6 @@ import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; @@ -208,7 +207,7 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { neverOptimistic.pop(); - return functionNode.setState(lc, CompilationState.OPTIMISTIC_TYPES_ASSIGNED); + return functionNode; } @Override
--- a/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Thu Sep 24 10:09:56 2015 -0700 @@ -29,7 +29,6 @@ import java.util.List; import jdk.nashorn.internal.ir.CompileUnitHolder; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; @@ -64,7 +63,7 @@ @Override public Node leaveFunctionNode(final FunctionNode node) { - return node.setCompileUnit(lc, getExistingReplacement(node)).setState(lc, CompilationState.COMPILE_UNITS_REUSED); + return node.setCompileUnit(lc, getExistingReplacement(node)); } @Override
--- a/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Thu Sep 24 10:09:56 2015 -0700 @@ -175,8 +175,7 @@ FunctionNode.IS_ANONYMOUS | FunctionNode.USES_ANCESTOR_SCOPE | FunctionNode.IS_SPLIT ) .setBody(lc, body) - .setCompileUnit(lc, splitNode.getCompileUnit()) - .copyCompilationState(lc, originalFn); + .setCompileUnit(lc, splitNode.getCompileUnit()); // Call the function: // either "(function () { ... }).call(this)"
--- a/src/jdk/nashorn/internal/codegen/Splitter.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/Splitter.java Thu Sep 24 10:09:56 2015 -0700 @@ -33,7 +33,6 @@ import java.util.Map; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; @@ -158,7 +157,7 @@ assert functionNode.getCompileUnit() != null; - return functionNode.setState(null, CompilationState.SPLIT); + return functionNode; } private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
--- a/src/jdk/nashorn/internal/codegen/types/BytecodeOps.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/codegen/types/BytecodeOps.java Thu Sep 24 10:09:56 2015 -0700 @@ -36,7 +36,7 @@ * The bytecode ops are coupled to a MethodVisitor from ASM for * byte code generation. They know nothing about our MethodGenerator, * which is the abstraction for working with Nashorn JS types - * For exmaple, anything like "two or one slots" for a type, which + * For example, anything like "two or one slots" for a type, which * is represented in bytecode and ASM, is abstracted away in the * MethodGenerator. There you just say "dup" or "store". *
--- a/src/jdk/nashorn/internal/ir/Block.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/Block.java Thu Sep 24 10:09:56 2015 -0700 @@ -130,11 +130,42 @@ } /** - * Clear the symbols in the block. - * TODO: make this immutable. + * Returns true if this block defines any symbols. + * @return true if this block defines any symbols. + */ + public boolean hasSymbols() { + return !symbols.isEmpty(); + } + + /** + * Replaces symbols defined in this block with different symbols. Used to ensure symbol tables are + * immutable upon construction and have copy-on-write semantics. Note that this method only replaces the + * symbols in the symbol table, it does not act on any contained AST nodes that might reference the symbols. + * Those should be updated separately as this method is meant to be used as part of such an update pass. + * @param lc the current lexical context + * @param replacements the map of symbol replacements + * @return a new block with replaced symbols, or this block if none of the replacements modified the symbol + * table. */ - public void clearSymbols() { - symbols.clear(); + public Block replaceSymbols(final LexicalContext lc, final Map<Symbol, Symbol> replacements) { + if (symbols.isEmpty()) { + return this; + } + final LinkedHashMap<String, Symbol> newSymbols = new LinkedHashMap<>(symbols); + for (final Map.Entry<String, Symbol> entry: newSymbols.entrySet()) { + final Symbol newSymbol = replacements.get(entry.getValue()); + assert newSymbol != null : "Missing replacement for " + entry.getKey(); + entry.setValue(newSymbol); + } + return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, newSymbols, conversion)); + } + + /** + * Returns a copy of this block with a shallow copy of the symbol table. + * @return a copy of this block with a shallow copy of the symbol table. + */ + public Block copyWithNewSymbols() { + return new Block(this, finish, statements, flags, new LinkedHashMap<>(symbols), conversion); } @Override @@ -162,7 +193,7 @@ * @return symbol iterator */ public List<Symbol> getSymbols() { - return Collections.unmodifiableList(new ArrayList<>(symbols.values())); + return symbols.isEmpty() ? Collections.<Symbol>emptyList() : Collections.unmodifiableList(new ArrayList<>(symbols.values())); } /** @@ -326,10 +357,9 @@ /** * Add or overwrite an existing symbol in the block * - * @param lc get lexical context * @param symbol symbol */ - public void putSymbol(final LexicalContext lc, final Symbol symbol) { + public void putSymbol(final Symbol symbol) { symbols.put(symbol.getName(), symbol); }
--- a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Thu Sep 24 10:09:56 2015 -0700 @@ -34,7 +34,7 @@ * This is a subclass of lexical context used for filling * blocks (and function nodes) with statements. When popping * a block from the lexical context, any statements that have - * been generated in it are commited to the block. This saves + * been generated in it are committed to the block. This saves * unnecessary object mutations and lexical context replacement */ public class BlockLexicalContext extends LexicalContext {
--- a/src/jdk/nashorn/internal/ir/ForNode.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/ForNode.java Thu Sep 24 10:09:56 2015 -0700 @@ -43,7 +43,7 @@ private final JoinPredecessorExpression modify; /** Iterator symbol. */ - private Symbol iterator; + private final Symbol iterator; /** Is this a normal for in loop? */ public static final int IS_FOR_IN = 1 << 0; @@ -70,22 +70,22 @@ this.flags = flags; this.init = null; this.modify = null; + this.iterator = null; } private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, - final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion) { + final Block body, final JoinPredecessorExpression modify, final int flags, + final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) { super(forNode, test, body, controlFlowEscapes, conversion); this.init = init; this.modify = modify; this.flags = flags; - // Even if the for node gets cloned in try/finally, the symbol can be shared as only one branch of the finally - // is executed. - this.iterator = forNode.iterator; + this.iterator = iterator; } @Override public Node ensureUniqueLabels(final LexicalContext lc) { - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -158,7 +158,7 @@ if (this.init == init) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } /** @@ -206,10 +206,15 @@ /** * Assign an iterator symbol to this ForNode. Used for for in and for each constructs + * @param lc the current lexical context * @param iterator the iterator symbol + * @return a ForNode with the iterator set */ - public void setIterator(final Symbol iterator) { - this.iterator = iterator; + public ForNode setIterator(final LexicalContext lc, final Symbol iterator) { + if (this.iterator == iterator) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } /** @@ -230,7 +235,7 @@ if (this.modify == modify) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -238,7 +243,7 @@ if (this.test == test) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -251,7 +256,7 @@ if (this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -259,19 +264,19 @@ if (this.controlFlowEscapes == controlFlowEscapes) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } private ForNode setFlags(final LexicalContext lc, final int flags) { if (this.flags == flags) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Sep 24 10:09:56 2015 -0700 @@ -33,10 +33,8 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES; import java.util.Collections; -import java.util.EnumSet; import java.util.Iterator; import java.util.List; -import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.CompilerConstants; @@ -73,40 +71,6 @@ SETTER } - /** Compilation states available */ - public enum CompilationState { - /** compiler is ready */ - INITIALIZED, - /** method has been parsed */ - PARSED, - /** method has been parsed */ - PARSE_ERROR, - /** constant folding pass */ - CONSTANT_FOLDED, - /** method has been lowered */ - LOWERED, - /** program points have been assigned to unique locations */ - PROGRAM_POINTS_ASSIGNED, - /** any transformations of builtins have taken place, e.g. apply=>call */ - BUILTINS_TRANSFORMED, - /** method has been split */ - SPLIT, - /** method has had symbols assigned */ - SYMBOLS_ASSIGNED, - /** computed scope depths for symbols */ - SCOPE_DEPTHS_COMPUTED, - /** method has had types calculated*/ - OPTIMISTIC_TYPES_ASSIGNED, - /** method has had types calculated */ - LOCAL_VARIABLE_TYPES_CALCULATED, - /** compile units reused (optional) */ - COMPILE_UNITS_REUSED, - /** method has been emitted to bytecode */ - BYTECODE_GENERATED, - /** method has been installed */ - BYTECODE_INSTALLED - } - /** Source of entity. */ private transient final Source source; @@ -144,10 +108,6 @@ /** Method's namespace. */ private transient final Namespace namespace; - /** Current compilation state */ - @Ignore - private final EnumSet<CompilationState> compilationState; - /** Number of properties of "this" object assigned in this function */ @Ignore private final int thisProperties; @@ -263,6 +223,11 @@ */ public static final int NEEDS_CALLEE = 1 << 26; + /** + * Is the function node cached? + */ + public static final int IS_CACHED = 1 << 27; + /** extension callsite flags mask */ public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST | @@ -322,7 +287,6 @@ this.firstToken = firstToken; this.lastToken = token; this.namespace = namespace; - this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.flags = flags; this.compileUnit = null; this.body = null; @@ -339,12 +303,11 @@ final String name, final Type returnType, final CompileUnit compileUnit, - final EnumSet<CompilationState> compilationState, final Block body, final List<IdentNode> parameters, final int thisProperties, final Class<?> rootClass, - final Source source, Namespace namespace) { + final Source source, final Namespace namespace) { super(functionNode); this.endParserState = endParserState; @@ -354,7 +317,6 @@ this.returnType = returnType; this.compileUnit = compileUnit; this.lastToken = lastToken; - this.compilationState = compilationState; this.body = body; this.parameters = parameters; this.thisProperties = thisProperties; @@ -454,7 +416,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -530,80 +491,6 @@ } /** - * Get the compilation state of this function - * @return the compilation state - */ - public EnumSet<CompilationState> getState() { - return compilationState; - } - - /** - * Check whether this FunctionNode has reached a give CompilationState. - * - * @param state the state to check for - * @return true of the node is in the given state - */ - public boolean hasState(final EnumSet<CompilationState> state) { - return !AssertsEnabled.assertsEnabled() || compilationState.containsAll(state); - } - - /** - * Add a state to the total CompilationState of this node, e.g. if - * FunctionNode has been lowered, the compiler will add - * {@code CompilationState#LOWERED} to the state vector - * - * @param lc lexical context - * @param state {@link CompilationState} to add - * @return function node or a new one if state was changed - */ - public FunctionNode setState(final LexicalContext lc, final CompilationState state) { - if (!AssertsEnabled.assertsEnabled() || this.compilationState.contains(state)) { - return this; - } - final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); - newState.add(state); - return setCompilationState(lc, newState); - } - - /** - * Copy a compilation state from an original function to this function. Used when creating synthetic - * function nodes by the splitter. - * - * @param lc lexical context - * @param original the original function node to copy compilation state from - * @return function node or a new one if state was changed - */ - public FunctionNode copyCompilationState(final LexicalContext lc, final FunctionNode original) { - final EnumSet<CompilationState> origState = original.compilationState; - if (!AssertsEnabled.assertsEnabled() || this.compilationState.containsAll(origState)) { - return this; - } - final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); - newState.addAll(origState); - return setCompilationState(lc, newState); - } - - private FunctionNode setCompilationState(final LexicalContext lc, final EnumSet<CompilationState> compilationState) { - return Node.replaceInLexicalContext( - lc, - this, - new FunctionNode( - this, - lastToken, - endParserState, - flags, - name, - returnType, - compileUnit, - compilationState, - body, - parameters, - thisProperties, - rootClass, source, namespace)); - } - - - /** * Create a unique name in the namespace of this FunctionNode * @param base prefix for name * @return base if no collision exists, otherwise a name prefix with base @@ -668,7 +555,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -748,7 +634,7 @@ */ public boolean needsCallee() { // NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units. - return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall(); + return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasApplyToCallSpecialization(); } /** @@ -765,7 +651,7 @@ * Return true if function contains an apply to call transform * @return true if this function has transformed apply to call */ - public boolean hasOptimisticApplyToCall() { + public boolean hasApplyToCallSpecialization() { return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION); } @@ -809,7 +695,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -905,7 +790,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -966,7 +850,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1002,7 +885,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1040,7 +922,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1116,7 +997,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1204,7 +1084,6 @@ name, type, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1221,6 +1100,24 @@ } /** + * Returns true if this function node has been cached. + * @return true if this function node has been cached. + */ + public boolean isCached() { + return getFlag(IS_CACHED); + } + + /** + * Mark this function node as having been cached. + * @param lc the current lexical context + * @return a function node equivalent to this one, with the flag set. + */ + public FunctionNode setCached(final LexicalContext lc) { + return setFlag(lc, IS_CACHED); + } + + + /** * Get the compile unit used to compile this function * @see Compiler * @return the compile unit @@ -1252,7 +1149,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1308,7 +1204,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties,
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Thu Sep 24 10:09:56 2015 -0700 @@ -430,7 +430,7 @@ * * @param token token * @param finish finish - * @param value undefined value, passed only for polymorphisism discrimination + * @param value undefined value, passed only for polymorphism discrimination * * @return the new literal node */
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java Thu Sep 24 10:09:56 2015 -0700 @@ -276,7 +276,7 @@ * * @param request a request * - * @return the inverted rquest, or null if not applicable + * @return the inverted request, or null if not applicable */ public static Request invert(final Request request) { switch (request) {
--- a/src/jdk/nashorn/internal/ir/SwitchNode.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/SwitchNode.java Thu Sep 24 10:09:56 2015 -0700 @@ -53,7 +53,7 @@ private final boolean uniqueInteger; /** Tag symbol. */ - private Symbol tag; + private final Symbol tag; /** * Constructor @@ -71,15 +71,16 @@ this.cases = cases; this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase); this.uniqueInteger = false; + this.tag = null; } private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases, - final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger) { + final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger, final Symbol tag) { super(switchNode, conversion); this.expression = expression; this.cases = cases; this.defaultCaseIndex = defaultCaseIndex; - this.tag = switchNode.getTag(); //TODO are symbols inherited as references? + this.tag = tag; this.uniqueInteger = uniqueInteger; } @@ -89,7 +90,7 @@ for (final CaseNode caseNode : cases) { newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion())); } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger, tag)); } @Override @@ -157,7 +158,7 @@ if (this.cases == cases) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -189,7 +190,7 @@ if (this.expression == expression) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -204,10 +205,15 @@ /** * Set the tag symbol for this switch. The tag symbol is where * the switch expression result is stored + * @param lc lexical context * @param tag a symbol + * @return a switch node with the symbol set */ - public void setTag(final Symbol tag) { - this.tag = tag; + public SwitchNode setTag(final LexicalContext lc, final Symbol tag) { + if (this.tag == tag) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -229,12 +235,12 @@ if(this.uniqueInteger == uniqueInteger) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } }
--- a/src/jdk/nashorn/internal/ir/Symbol.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Thu Sep 24 10:09:56 2015 -0700 @@ -25,7 +25,10 @@ package jdk.nashorn.internal.ir; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.PrintWriter; +import java.io.Serializable; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; @@ -47,7 +50,9 @@ * refer to their location. */ -public final class Symbol implements Comparable<Symbol> { +public final class Symbol implements Comparable<Symbol>, Cloneable, Serializable { + private static final long serialVersionUID = 1L; + /** Is this Global */ public static final int IS_GLOBAL = 1; /** Is this a variable */ @@ -94,10 +99,10 @@ /** First bytecode method local variable slot for storing the value(s) of this variable. -1 indicates the variable * is not stored in local variable slots or it is not yet known. */ - private int firstSlot = -1; + private transient int firstSlot = -1; /** Field number in scope or property; array index in varargs when not using arguments object. */ - private int fieldIndex = -1; + private transient int fieldIndex = -1; /** Number of times this symbol is used in code */ private int useCount; @@ -144,6 +149,15 @@ } } + @Override + public Symbol clone() { + try { + return (Symbol)super.clone(); + } catch (final CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + private static String align(final String string, final int max) { final StringBuilder sb = new StringBuilder(); sb.append(string.substring(0, Math.min(string.length(), max))); @@ -337,7 +351,7 @@ * Flag this symbol as scope as described in {@link Symbol#isScope()} * @return the symbol */ - public Symbol setIsScope() { + public Symbol setIsScope() { if (!isScope()) { if(shouldTrace()) { trace("SET IS SCOPE"); @@ -609,11 +623,11 @@ /** * Increase the symbol's use count by one. - * @return the symbol */ - public Symbol increaseUseCount() { - useCount++; - return this; + public void increaseUseCount() { + if (isScope()) { // Avoid dirtying a cache line; we only need the use count for scoped symbols + useCount++; + } } /** @@ -669,4 +683,10 @@ new Throwable().printStackTrace(Context.getCurrentErr()); } } + + private void readObject(final ObjectInputStream in) throws ClassNotFoundException, IOException { + in.defaultReadObject(); + firstSlot = -1; + fieldIndex = -1; + } }
--- a/src/jdk/nashorn/internal/ir/TryNode.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/TryNode.java Thu Sep 24 10:09:56 2015 -0700 @@ -65,7 +65,7 @@ private final List<Block> inlinedFinallies; /** Exception symbol. */ - private Symbol exception; + private final Symbol exception; private final LocalVariableConversion conversion; @@ -86,22 +86,23 @@ this.finallyBody = finallyBody; this.conversion = null; this.inlinedFinallies = Collections.emptyList(); + this.exception = null; } - private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies) { + private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies, final Symbol exception) { super(tryNode); this.body = body; this.catchBlocks = catchBlocks; this.finallyBody = finallyBody; this.conversion = conversion; this.inlinedFinallies = inlinedFinallies; - this.exception = tryNode.exception; + this.exception = exception; } @Override public Node ensureUniqueLabels(final LexicalContext lc) { //try nodes are never in lex context - return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); } @Override @@ -160,7 +161,7 @@ if (this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -197,7 +198,7 @@ if (this.catchBlocks == catchBlocks) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -209,12 +210,15 @@ } /** * Set the exception symbol for this try block + * @param lc lexical context * @param exception a symbol for the compiler to store the exception in * @return new TryNode or same if unchanged */ - public TryNode setException(final Symbol exception) { - this.exception = exception; - return this; + public TryNode setException(final LexicalContext lc, final Symbol exception) { + if (this.exception == exception) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -277,7 +281,7 @@ if (this.finallyBody == finallyBody) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -293,7 +297,7 @@ return this; } assert checkInlinedFinallies(inlinedFinallies); - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) { @@ -314,7 +318,7 @@ if(this.conversion == conversion) { return this; } - return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); } @Override
--- a/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java Thu Sep 24 10:09:56 2015 -0700 @@ -36,7 +36,7 @@ import jdk.nashorn.internal.ir.debug.NashornTextifier.NashornLabel; /** - * Subclass of the ASM classs reader that retains more info, such + * Subclass of the ASM class reader that retains more info, such * as bytecode offsets */ public class NashornClassReader extends ClassReader {
--- a/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java Thu Sep 24 10:09:56 2015 -0700 @@ -193,7 +193,7 @@ } /** - * Get the class histograpm + * Get the class histogram * @return class histogram element list */ public List<ClassHistogramElement> getClassHistogram() {
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java Thu Sep 24 10:09:56 2015 -0700 @@ -192,7 +192,7 @@ /** * Factory method for array data * - * @param nb underlying nativebuffer + * @param nb underlying native buffer * @param start start element * @param end end element *
--- a/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java Thu Sep 24 10:00:42 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2010, 2013, 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.objects; - -import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptFunctionData; -import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.ScriptRuntime; - -/** - * A {@code ScriptFunctionImpl} subclass for functions created using {@code Function.prototype.bind}. Such functions - * must track their {@code [[TargetFunction]]} property for purposes of correctly implementing {@code [[HasInstance]]}; - * see {@link ScriptFunction#isInstance(ScriptObject)}. - */ -final class BoundScriptFunctionImpl extends ScriptFunctionImpl { - private final ScriptFunction targetFunction; - - BoundScriptFunctionImpl(final ScriptFunctionData data, final ScriptFunction targetFunction) { - super(data, Global.instance()); - setPrototype(ScriptRuntime.UNDEFINED); - this.targetFunction = targetFunction; - } - - @Override - protected ScriptFunction getTargetFunction() { - return targetFunction; - } -}
--- a/src/jdk/nashorn/internal/objects/Global.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/Global.java Thu Sep 24 10:09:56 2015 -0700 @@ -1583,7 +1583,11 @@ return ScriptFunction.getPrototype(builtinObject); } - ScriptObject getFunctionPrototype() { + /** + * Get the builtin Function prototype. + * @return the Function.prototype. + */ + public ScriptObject getFunctionPrototype() { return ScriptFunction.getPrototype(builtinFunction); } @@ -1768,7 +1772,12 @@ return ScriptFunction.getPrototype(getBuiltinFloat64Array()); } - ScriptFunction getTypeErrorThrower() { + /** + * Return the function that throws TypeError unconditionally. Used as "poison" methods for certain Function properties. + * + * @return the TypeError throwing function + */ + public ScriptFunction getTypeErrorThrower() { return typeErrorThrower; } @@ -2157,7 +2166,7 @@ // We want to avoid adding our generic lexical scope switchpoint to global constant invocations, // because those are invalidated per-key in the addBoundProperties method above. - // We therefor check if the invocation does already have a switchpoint and the property is non-inherited, + // We therefore check if the invocation does already have a switchpoint and the property is non-inherited, // assuming this only applies to global constants. If other non-inherited properties will // start using switchpoints some time in the future we'll have to revisit this. if (isScope && context.getEnv()._es6 && (invocation.getSwitchPoints() == null || !hasOwnProperty(name))) { @@ -2202,10 +2211,10 @@ * Adds jjs shell interactive mode builtin functions to global scope. */ public void addShellBuiltins() { - Object value = ScriptFunctionImpl.makeFunction("input", ShellFunctions.INPUT); + Object value = ScriptFunction.createBuiltin("input", ShellFunctions.INPUT); addOwnProperty("input", Attribute.NOT_ENUMERABLE, value); - value = ScriptFunctionImpl.makeFunction("evalinput", ShellFunctions.EVALINPUT); + value = ScriptFunction.createBuiltin("evalinput", ShellFunctions.EVALINPUT); addOwnProperty("evalinput", Attribute.NOT_ENUMERABLE, value); } @@ -2252,35 +2261,35 @@ this.setInitialProto(getObjectPrototype()); // initialize global function properties - this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL); - - this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT, + this.eval = this.builtinEval = ScriptFunction.createBuiltin("eval", EVAL); + + this.parseInt = ScriptFunction.createBuiltin("parseInt", GlobalFunctions.PARSEINT, new Specialization[] { new Specialization(GlobalFunctions.PARSEINT_Z), new Specialization(GlobalFunctions.PARSEINT_I), new Specialization(GlobalFunctions.PARSEINT_J), new Specialization(GlobalFunctions.PARSEINT_OI), new Specialization(GlobalFunctions.PARSEINT_O) }); - this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT); - this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN, + this.parseFloat = ScriptFunction.createBuiltin("parseFloat", GlobalFunctions.PARSEFLOAT); + this.isNaN = ScriptFunction.createBuiltin("isNaN", GlobalFunctions.IS_NAN, new Specialization[] { new Specialization(GlobalFunctions.IS_NAN_I), new Specialization(GlobalFunctions.IS_NAN_J), new Specialization(GlobalFunctions.IS_NAN_D) }); - this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT); - this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN); - this.isFinite = ScriptFunctionImpl.makeFunction("isFinite", GlobalFunctions.IS_FINITE); - this.encodeURI = ScriptFunctionImpl.makeFunction("encodeURI", GlobalFunctions.ENCODE_URI); - this.encodeURIComponent = ScriptFunctionImpl.makeFunction("encodeURIComponent", GlobalFunctions.ENCODE_URICOMPONENT); - this.decodeURI = ScriptFunctionImpl.makeFunction("decodeURI", GlobalFunctions.DECODE_URI); - this.decodeURIComponent = ScriptFunctionImpl.makeFunction("decodeURIComponent", GlobalFunctions.DECODE_URICOMPONENT); - this.escape = ScriptFunctionImpl.makeFunction("escape", GlobalFunctions.ESCAPE); - this.unescape = ScriptFunctionImpl.makeFunction("unescape", GlobalFunctions.UNESCAPE); - this.print = ScriptFunctionImpl.makeFunction("print", env._print_no_newline ? PRINT : PRINTLN); - this.load = ScriptFunctionImpl.makeFunction("load", LOAD); - this.loadWithNewGlobal = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOAD_WITH_NEW_GLOBAL); - this.exit = ScriptFunctionImpl.makeFunction("exit", EXIT); - this.quit = ScriptFunctionImpl.makeFunction("quit", EXIT); + this.parseFloat = ScriptFunction.createBuiltin("parseFloat", GlobalFunctions.PARSEFLOAT); + this.isNaN = ScriptFunction.createBuiltin("isNaN", GlobalFunctions.IS_NAN); + this.isFinite = ScriptFunction.createBuiltin("isFinite", GlobalFunctions.IS_FINITE); + this.encodeURI = ScriptFunction.createBuiltin("encodeURI", GlobalFunctions.ENCODE_URI); + this.encodeURIComponent = ScriptFunction.createBuiltin("encodeURIComponent", GlobalFunctions.ENCODE_URICOMPONENT); + this.decodeURI = ScriptFunction.createBuiltin("decodeURI", GlobalFunctions.DECODE_URI); + this.decodeURIComponent = ScriptFunction.createBuiltin("decodeURIComponent", GlobalFunctions.DECODE_URICOMPONENT); + this.escape = ScriptFunction.createBuiltin("escape", GlobalFunctions.ESCAPE); + this.unescape = ScriptFunction.createBuiltin("unescape", GlobalFunctions.UNESCAPE); + this.print = ScriptFunction.createBuiltin("print", env._print_no_newline ? PRINT : PRINTLN); + this.load = ScriptFunction.createBuiltin("load", LOAD); + this.loadWithNewGlobal = ScriptFunction.createBuiltin("loadWithNewGlobal", LOAD_WITH_NEW_GLOBAL); + this.exit = ScriptFunction.createBuiltin("exit", EXIT); + this.quit = ScriptFunction.createBuiltin("quit", EXIT); // built-in constructors this.builtinArray = initConstructorAndSwitchPoint("Array", ScriptFunction.class); @@ -2360,7 +2369,7 @@ // default file name addOwnProperty(ScriptEngine.FILENAME, Attribute.NOT_ENUMERABLE, null); // __noSuchProperty__ hook for ScriptContext search of missing variables - final ScriptFunction noSuchProp = ScriptFunctionImpl.makeStrictFunction(NO_SUCH_PROPERTY_NAME, NO_SUCH_PROPERTY); + final ScriptFunction noSuchProp = ScriptFunction.createStrictBuiltin(NO_SUCH_PROPERTY_NAME, NO_SUCH_PROPERTY); addOwnProperty(NO_SUCH_PROPERTY_NAME, Attribute.NOT_ENUMERABLE, noSuchProp); } } @@ -2371,17 +2380,17 @@ final ScriptObject errorProto = getErrorPrototype(); // Nashorn specific accessors on Error.prototype - stack, lineNumber, columnNumber and fileName - final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", NativeError.GET_STACK); - final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", NativeError.SET_STACK); + final ScriptFunction getStack = ScriptFunction.createBuiltin("getStack", NativeError.GET_STACK); + final ScriptFunction setStack = ScriptFunction.createBuiltin("setStack", NativeError.SET_STACK); errorProto.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); - final ScriptFunction getLineNumber = ScriptFunctionImpl.makeFunction("getLineNumber", NativeError.GET_LINENUMBER); - final ScriptFunction setLineNumber = ScriptFunctionImpl.makeFunction("setLineNumber", NativeError.SET_LINENUMBER); + final ScriptFunction getLineNumber = ScriptFunction.createBuiltin("getLineNumber", NativeError.GET_LINENUMBER); + final ScriptFunction setLineNumber = ScriptFunction.createBuiltin("setLineNumber", NativeError.SET_LINENUMBER); errorProto.addOwnProperty("lineNumber", Attribute.NOT_ENUMERABLE, getLineNumber, setLineNumber); - final ScriptFunction getColumnNumber = ScriptFunctionImpl.makeFunction("getColumnNumber", NativeError.GET_COLUMNNUMBER); - final ScriptFunction setColumnNumber = ScriptFunctionImpl.makeFunction("setColumnNumber", NativeError.SET_COLUMNNUMBER); + final ScriptFunction getColumnNumber = ScriptFunction.createBuiltin("getColumnNumber", NativeError.GET_COLUMNNUMBER); + final ScriptFunction setColumnNumber = ScriptFunction.createBuiltin("setColumnNumber", NativeError.SET_COLUMNNUMBER); errorProto.addOwnProperty("columnNumber", Attribute.NOT_ENUMERABLE, getColumnNumber, setColumnNumber); - final ScriptFunction getFileName = ScriptFunctionImpl.makeFunction("getFileName", NativeError.GET_FILENAME); - final ScriptFunction setFileName = ScriptFunctionImpl.makeFunction("setFileName", NativeError.SET_FILENAME); + final ScriptFunction getFileName = ScriptFunction.createBuiltin("getFileName", NativeError.GET_FILENAME); + final ScriptFunction setFileName = ScriptFunction.createBuiltin("setFileName", NativeError.SET_FILENAME); errorProto.addOwnProperty("fileName", Attribute.NOT_ENUMERABLE, getFileName, setFileName); // ECMA 15.11.4.2 Error.prototype.name @@ -2420,20 +2429,22 @@ } private void initScripting(final ScriptEnvironment scriptEnv) { - Object value; - value = ScriptFunctionImpl.makeFunction("readLine", ScriptingFunctions.READLINE); + ScriptObject value; + value = ScriptFunction.createBuiltin("readLine", ScriptingFunctions.READLINE); addOwnProperty("readLine", Attribute.NOT_ENUMERABLE, value); - value = ScriptFunctionImpl.makeFunction("readFully", ScriptingFunctions.READFULLY); + value = ScriptFunction.createBuiltin("readFully", ScriptingFunctions.READFULLY); addOwnProperty("readFully", Attribute.NOT_ENUMERABLE, value); final String execName = ScriptingFunctions.EXEC_NAME; - value = ScriptFunctionImpl.makeFunction(execName, ScriptingFunctions.EXEC); + value = ScriptFunction.createBuiltin(execName, ScriptingFunctions.EXEC); + value.addOwnProperty(ScriptingFunctions.THROW_ON_ERROR_NAME, Attribute.NOT_ENUMERABLE, false); + addOwnProperty(execName, Attribute.NOT_ENUMERABLE, value); // Nashorn extension: global.echo (scripting-mode-only) // alias for "print" - value = get("print"); + value = (ScriptObject)get("print"); addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value); // Nashorn extension: global.$OPTIONS (scripting-mode-only) @@ -2609,7 +2620,7 @@ this.builtinFunction = initConstructor("Function", ScriptFunction.class); // create global anonymous function - final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction(); + final ScriptFunction anon = ScriptFunction.createAnonymous(); // need to copy over members of Function.prototype to anon function anon.addBoundProperties(getFunctionPrototype()); @@ -2621,10 +2632,7 @@ anon.deleteOwnProperty(anon.getMap().findProperty("prototype")); // use "getter" so that [[ThrowTypeError]] function's arity is 0 - as specified in step 10 of section 13.2.3 - this.typeErrorThrower = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER, null, null, 0); - typeErrorThrower.setPrototype(UNDEFINED); - // Non-constructor built-in functions do not have "prototype" property - typeErrorThrower.deleteOwnProperty(typeErrorThrower.getMap().findProperty("prototype")); + this.typeErrorThrower = ScriptFunction.createBuiltin("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER); typeErrorThrower.preventExtensions(); // now initialize Object @@ -2635,8 +2643,8 @@ // ES6 draft compliant __proto__ property of Object.prototype // accessors on Object.prototype for "__proto__" - final ScriptFunction getProto = ScriptFunctionImpl.makeFunction("getProto", NativeObject.GET__PROTO__); - final ScriptFunction setProto = ScriptFunctionImpl.makeFunction("setProto", NativeObject.SET__PROTO__); + final ScriptFunction getProto = ScriptFunction.createBuiltin("getProto", NativeObject.GET__PROTO__); + final ScriptFunction setProto = ScriptFunction.createBuiltin("setProto", NativeObject.SET__PROTO__); ObjectPrototype.addOwnProperty("__proto__", Attribute.NOT_ENUMERABLE, getProto, setProto); // Function valued properties of Function.prototype were not properly
--- a/src/jdk/nashorn/internal/objects/NativeDataView.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeDataView.java Thu Sep 24 10:09:56 2015 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
--- a/src/jdk/nashorn/internal/objects/NativeDebug.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeDebug.java Thu Sep 24 10:09:56 2015 -0700 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; + import java.io.PrintWriter; import java.util.LinkedList; import java.util.Objects; @@ -246,7 +247,7 @@ final PrintWriter out = Context.getCurrentErr(); out.println("ScriptObject count " + ScriptObject.getCount()); - out.println("Scope count " + Scope.getCount()); + out.println("Scope count " + Scope.getScopeCount()); out.println("ScriptObject listeners added " + PropertyListeners.getListenersAdded()); out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved()); out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount());
--- a/src/jdk/nashorn/internal/objects/NativeError.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeError.java Thu Sep 24 10:09:56 2015 -0700 @@ -150,8 +150,8 @@ initException(sobj); sobj.delete(STACK, false); if (! sobj.has("stack")) { - final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", GET_STACK); - final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", SET_STACK); + final ScriptFunction getStack = ScriptFunction.createBuiltin("getStack", GET_STACK); + final ScriptFunction setStack = ScriptFunction.createBuiltin("setStack", SET_STACK); sobj.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); } return UNDEFINED;
--- a/src/jdk/nashorn/internal/objects/NativeFunction.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeFunction.java Thu Sep 24 10:09:56 2015 -0700 @@ -108,7 +108,7 @@ throw new AssertionError("Should not reach here"); } - /** + /** * Given an array-like object, converts it into a Java object array suitable for invocation of ScriptRuntime.apply * or for direct invocation of the applied function. * @param array the array-like object. Can be null in which case a zero-length array is created.
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Thu Sep 24 10:09:56 2015 -0700 @@ -621,11 +621,11 @@ if (find != null) { final Object value = find.getObjectValue(); if (value instanceof ScriptFunction) { - final ScriptFunctionImpl func = (ScriptFunctionImpl)value; + final ScriptFunction func = (ScriptFunction)value; // TODO: It's a shame we need to produce a function bound to this and name, when we'd only need it bound // to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice. return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class, - func.makeBoundFunction(this, new Object[] { name })), 0, Object.class), + func.createBound(this, new Object[] { name })), 0, Object.class), testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoint(__call__, find.getOwner())); }
--- a/src/jdk/nashorn/internal/objects/NativeJava.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeJava.java Thu Sep 24 10:09:56 2015 -0700 @@ -93,7 +93,7 @@ @Function(name="synchronized", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object synchronizedFunc(final Object self, final Object func, final Object obj) { if (func instanceof ScriptFunction) { - return ((ScriptFunction)func).makeSynchronizedFunction(obj); + return ((ScriptFunction)func).createSynchronized(obj); } throw typeError("not.a.function", ScriptRuntime.safeToString(func)); @@ -342,7 +342,8 @@ /** * Given a script object and a Java type, converts the script object into the desired Java type. Currently it * performs shallow creation of Java arrays, as well as wrapping of objects in Lists, Dequeues, Queues, - * and Collections. Example: + * and Collections. If conversion is not possible or fails for some reason, TypeError is thrown. + * Example: * <pre> * var anArray = [1, "13", false] * var javaIntArray = Java.to(anArray, "int[]") @@ -386,7 +387,11 @@ } if(targetClass.isArray()) { - return JSType.toJavaArray(obj, targetClass.getComponentType()); + try { + return JSType.toJavaArray(obj, targetClass.getComponentType()); + } catch (final Exception exp) { + throw typeError(exp, "java.array.conversion.failed", targetClass.getName()); + } } if (targetClass == List.class || targetClass == Deque.class || targetClass == Queue.class || targetClass == Collection.class) {
--- a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Thu Sep 24 10:09:56 2015 -0700 @@ -134,7 +134,7 @@ } @Override - protected Object invokeNoSuchProperty(final String name, final int programPoint) { + protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) { final Object retval = createProperty(name); if (isValid(programPoint)) { throw new UnwarrantedOptimismException(retval, programPoint);
--- a/src/jdk/nashorn/internal/objects/NativeRegExp.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java Thu Sep 24 10:09:56 2015 -0700 @@ -728,7 +728,7 @@ * * $$ -> $ * $& -> the matched substring - * $` -> the portion of string that preceeds matched substring + * $` -> the portion of string that precedes matched substring * $' -> the portion of string that follows the matched substring * $n -> the nth capture, where n is [1-9] and $n is NOT followed by a decimal digit * $nn -> the nnth capture, where nn is a two digit decimal number [01-99].
--- a/src/jdk/nashorn/internal/objects/PrototypeObject.java Thu Sep 24 10:00:42 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2010, 2013, 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.objects; - -import static jdk.nashorn.internal.lookup.Lookup.MH; -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import jdk.nashorn.internal.runtime.AccessorProperty; -import jdk.nashorn.internal.runtime.Property; -import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptObject; - -/** - * Instances of this class serve as "prototype" object for script functions. - * The purpose is to expose "constructor" property from "prototype". Also, nasgen - * generated prototype classes extend from this class. - * - */ -public class PrototypeObject extends ScriptObject { - private static final PropertyMap map$; - - private Object constructor; - - private static final MethodHandle GET_CONSTRUCTOR = findOwnMH("getConstructor", Object.class, Object.class); - private static final MethodHandle SET_CONSTRUCTOR = findOwnMH("setConstructor", void.class, Object.class, Object.class); - - static { - final ArrayList<Property> properties = new ArrayList<>(1); - properties.add(AccessorProperty.create("constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR)); - map$ = PropertyMap.newMap(properties); - } - - private PrototypeObject(final Global global, final PropertyMap map) { - super(global.getObjectPrototype(), map != map$? map.addAll(map$) : map$); - } - - PrototypeObject() { - this(Global.instance(), map$); - } - - /** - * PropertyObject constructor - * - * @param map property map - */ - PrototypeObject(final PropertyMap map) { - this(Global.instance(), map); - } - - PrototypeObject(final ScriptFunction func) { - this(Global.instance(), map$); - this.constructor = func; - } - - /** - * Get the constructor for this {@code PrototypeObject} - * @param self self reference - * @return constructor, probably, but not necessarily, a {@link ScriptFunction} - */ - static Object getConstructor(final Object self) { - return (self instanceof PrototypeObject) ? - ((PrototypeObject)self).getConstructor() : - UNDEFINED; - } - - /** - * Reset the constructor for this {@code PrototypeObject} - * @param self self reference - * @param constructor constructor, probably, but not necessarily, a {@link ScriptFunction} - */ - static void setConstructor(final Object self, final Object constructor) { - if (self instanceof PrototypeObject) { - ((PrototypeObject)self).setConstructor(constructor); - } - } - - private Object getConstructor() { - return constructor; - } - - private void setConstructor(final Object constructor) { - this.constructor = constructor; - } - - private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { - return MH.findStatic(MethodHandles.lookup(), PrototypeObject.class, name, MH.type(rtype, types)); - } -}
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Thu Sep 24 10:00:42 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2010, 2013, 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.objects; - -import static jdk.nashorn.internal.lookup.Lookup.MH; -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - -import java.lang.invoke.MethodHandle; -import java.util.ArrayList; -import jdk.nashorn.internal.runtime.AccessorProperty; -import jdk.nashorn.internal.runtime.GlobalFunctions; -import jdk.nashorn.internal.runtime.Property; -import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; -import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptFunctionData; -import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.Specialization; - -/** - * Concrete implementation of ScriptFunction. This sets correct map for the - * function objects -- to expose properties like "prototype", "length" etc. - */ -public class ScriptFunctionImpl extends ScriptFunction { - - /** Reference to constructor prototype. */ - private Object prototype; - - // property map for strict mode functions - private static final PropertyMap strictmodemap$; - // property map for bound functions - private static final PropertyMap boundfunctionmap$; - // property map for non-strict, non-bound functions. - private static final PropertyMap map$; - - // Marker object for lazily initialized prototype object - private static final Object LAZY_PROTOTYPE = new Object(); - - private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs, final Global global) { - super(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); - init(global); - } - - /** - * Constructor called by Nasgen generated code, no membercount, use the default map. - * Creates builtin functions only. - * - * @param name name of function - * @param invokeHandle handle for invocation - * @param specs specialized versions of this method, if available, null otherwise - */ - ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs) { - this(name, invokeHandle, specs, Global.instance()); - } - - private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs, final Global global) { - super(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); - init(global); - } - - /** - * Constructor called by Nasgen generated code, no membercount, use the map passed as argument. - * Creates builtin functions only. - * - * @param name name of function - * @param invokeHandle handle for invocation - * @param map initial property map - * @param specs specialized versions of this method, if available, null otherwise - */ - ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) { - this(name, invokeHandle, map, specs, Global.instance()); - } - - private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags, final Global global) { - super(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags); - init(global); - } - - /** - * Constructor called by Global.newScriptFunction (runtime). - * - * @param name name of function - * @param methodHandle handle for invocation - * @param scope scope object - * @param specs specialized versions of this method, if available, null otherwise - * @param flags {@link ScriptFunctionData} flags - */ - ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags) { - this(name, methodHandle, scope, specs, flags, Global.instance()); - } - - private ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope, final Global global) { - super(data, getMap(data.isStrict()), scope); - init(global); - } - - /** - * Factory method called by compiler generated code for functions that need parent scope. - * - * @param constants the generated class' constant array - * @param index the index of the {@code RecompilableScriptFunctionData} object in the constants array. - * @param scope the parent scope object - * @return a newly created function object - */ - public static ScriptFunction create(final Object[] constants, final int index, final ScriptObject scope) { - return new ScriptFunctionImpl((RecompilableScriptFunctionData)constants[index], scope, Global.instance()); - } - - /** - * Factory method called by compiler generated code for functions that don't need parent scope. - * - * @param constants the generated class' constant array - * @param index the index of the {@code RecompilableScriptFunctionData} object in the constants array. - * @return a newly created function object - */ - public static ScriptFunction create(final Object[] constants, final int index) { - return create(constants, index, null); - } - - /** - * Only invoked internally from {@link BoundScriptFunctionImpl} constructor. - * @param data the script function data for the bound function. - * @param global the global object - */ - ScriptFunctionImpl(final ScriptFunctionData data, final Global global) { - super(data, boundfunctionmap$, null); - init(global); - } - - static { - final ArrayList<Property> properties = new ArrayList<>(3); - properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE)); - properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null)); - properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null)); - map$ = PropertyMap.newMap(properties); - strictmodemap$ = createStrictModeMap(map$); - boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); - } - - private static PropertyMap createStrictModeMap(final PropertyMap map) { - final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; - PropertyMap newMap = map; - // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. - newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags)); - newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags)); - return newMap; - } - - private static boolean isStrict(final int flags) { - return (flags & ScriptFunctionData.IS_STRICT) != 0; - } - - // Choose the map based on strict mode! - private static PropertyMap getMap(final boolean strict) { - return strict ? strictmodemap$ : map$; - } - - private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { - // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see - // ECMAScript 5.1 section 15.3.4.5 - return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype")); - } - - // Instance of this class is used as global anonymous function which - // serves as Function.prototype object. - private static class AnonymousFunction extends ScriptFunctionImpl { - private static final PropertyMap anonmap$ = PropertyMap.newMap(); - - AnonymousFunction() { - super("", GlobalFunctions.ANONYMOUS, anonmap$, null); - } - } - - static ScriptFunctionImpl newAnonymousFunction() { - return new AnonymousFunction(); - } - - private static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) { - final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, flags); - func.setPrototype(UNDEFINED); - // Non-constructor built-in functions do not have "prototype" property - func.deleteOwnProperty(func.getMap().findProperty("prototype")); - - return func; - } - - /** - * Factory method for non-constructor built-in functions - * - * @param name function name - * @param methodHandle handle for invocation - * @param specs specialized versions of function if available, null otherwise - * @return new ScriptFunction - */ - static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs) { - return makeFunction(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN); - } - - /** - * Factory method for non-constructor built-in, strict functions - * - * @param name function name - * @param methodHandle handle for invocation - * @return new ScriptFunction - */ - static ScriptFunction makeStrictFunction(final String name, final MethodHandle methodHandle) { - return makeFunction(name, methodHandle, null, ScriptFunctionData.IS_BUILTIN | ScriptFunctionData.IS_STRICT ); - } - - /** - * Factory method for non-constructor built-in functions - * - * @param name function name - * @param methodHandle handle for invocation - * @return new ScriptFunction - */ - static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) { - return makeFunction(name, methodHandle, null); - } - - @Override - public ScriptFunction makeSynchronizedFunction(final Object sync) { - final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync); - return makeFunction(getName(), mh); - } - - /** - * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we - * can expose it. - * @param self the self to bind to this function. Can be null (in which case, null is bound as this). - * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. - * @return a function with the specified self and parameters bound. - */ - @Override - public ScriptFunction makeBoundFunction(final Object self, final Object[] args) { - return super.makeBoundFunction(self, args); - } - - /** - * This method is used to create a bound function based on this function. - * - * @param data the {@code ScriptFunctionData} specifying the functions immutable portion. - * @return a function initialized from the specified data. Its parent scope will be set to null, therefore the - * passed in data should not expect a callee. - */ - @Override - protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) { - return new BoundScriptFunctionImpl(data, getTargetFunction()); - } - - // return Object.prototype - used by "allocate" - @Override - protected final ScriptObject getObjectPrototype() { - return Global.objectPrototype(); - } - - @Override - public final Object getPrototype() { - if (prototype == LAZY_PROTOTYPE) { - prototype = new PrototypeObject(this); - } - return prototype; - } - - @Override - public final void setPrototype(final Object newProto) { - if (newProto instanceof ScriptObject && newProto != this.prototype && allocatorMap != null) { - // Replace our current allocator map with one that is associated with the new prototype. - allocatorMap = allocatorMap.changeProto((ScriptObject)newProto); - } - this.prototype = newProto; - } - - // Internals below.. - private void init(final Global global) { - this.setInitialProto(global.getFunctionPrototype()); - this.prototype = LAZY_PROTOTYPE; - - // We have to fill user accessor functions late as these are stored - // in this object rather than in the PropertyMap of this object. - assert objectSpill == null; - final ScriptFunction typeErrorThrower = global.getTypeErrorThrower(); - if (findProperty("arguments", true) != null) { - initUserAccessors("arguments", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); - } - if (findProperty("caller", true) != null) { - initUserAccessors("caller", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); - } - } -}
--- a/src/jdk/nashorn/internal/parser/Parser.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/parser/Parser.java Thu Sep 24 10:09:56 2015 -0700 @@ -84,7 +84,6 @@ import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; @@ -513,8 +512,7 @@ return lc.pop(functionNode). setBody(lc, newBody). - setLastToken(lc, lastToken). - setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED); + setLastToken(lc, lastToken); } /** @@ -805,7 +803,7 @@ if (!oldStrictMode && directiveStmts != null) { // check that directives preceding this one do not violate strictness for (final Node statement : directiveStmts) { - // the get value will force unescape of preceeding + // the get value will force unescape of preceding // escaped string directives getValue(statement.getToken()); } @@ -2469,7 +2467,7 @@ // run: function() { println("run"); } // }; // - // The object literal following the "new Constructor()" expresssion + // The object literal following the "new Constructor()" expression // is passed as an additional (last) argument to the constructor. if (!env._no_syntax_extensions && type == LBRACE) { arguments.add(objectLiteral());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/AstSerializer.java Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,55 @@ +/* + * 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.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.runtime.options.Options; + +/** + * This static utility class performs serialization of FunctionNode ASTs to a byte array. + * The format is a standard Java serialization stream, deflated. + */ +final class AstSerializer { + // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed + // and size. + private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); + static byte[] serialize(final FunctionNode fn) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Deflater deflater = new Deflater(COMPRESSION_LEVEL); + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { + oout.writeObject(fn); + } catch (final IOException e) { + throw new AssertionError("Unexpected exception serializing function", e); + } finally { + deflater.end(); + } + return out.toByteArray(); + } +}
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Sep 24 10:09:56 2015 -0700 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; + import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -101,7 +102,7 @@ /* * An optimistic builtin with isOptimistic=true works like any optimistic generated function, i.e. it * can throw unwarranted optimism exceptions. As native functions trivially can't have parts of them - * regenerated as restof methods, this only works if the methods are atomic/functional in their behavior + * regenerated as "restOf" methods, this only works if the methods are atomic/functional in their behavior * and doesn't modify state before an UOE can be thrown. If they aren't, we can reexecute a wider version * of the same builtin in a recompilation handler for FinalScriptFunctionData. There are several * candidate methods in Native* that would benefit from this, but I haven't had time to implement any @@ -566,7 +567,7 @@ return handle; } - // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itslef + // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itself // to the compiled function's changed target whenever the optimistic assumptions are invalidated. final CallSite cs = new MutableCallSite(handle.type()); relinkComposableInvoker(cs, this, isConstructor); @@ -820,7 +821,7 @@ // isn't available, we'll use the old one bound into the call site. final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo; FunctionNode fn = effectiveOptInfo.reparse(); - final boolean serialized = effectiveOptInfo.isSerialized(); + final boolean cached = fn.isCached(); final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of if (!shouldRecompile) { @@ -828,11 +829,11 @@ // recompiled a deoptimized version for an inner invocation. // We still need to do the rest of from the beginning logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); - return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); + return restOfHandle(effectiveOptInfo, compiler.compile(fn, cached ? CompilationPhases.COMPILE_CACHED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); } logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); - fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); + fn = compiler.compile(fn, cached ? CompilationPhases.RECOMPILE_CACHED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); log.fine("Reusable IR generated"); // compile the rest of the function, and install it @@ -956,10 +957,6 @@ FunctionNode reparse() { return data.reparse(); } - - boolean isSerialized() { - return data.isSerialized(); - } } @SuppressWarnings("unused")
--- a/src/jdk/nashorn/internal/runtime/Context.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/Context.java Thu Sep 24 10:09:56 2015 -0700 @@ -153,7 +153,7 @@ * Currently we are conservative and associate the name of a builtin class with all * its properties, so it's enough to invalidate a property to break all assumptions * about a prototype. This can be changed to a more fine grained approach, but no one - * ever needs this, given the very rare occurance of swapping out only parts of + * ever needs this, given the very rare occurrence of swapping out only parts of * a builtin v.s. the entire builtin object */ private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>(); @@ -1456,7 +1456,7 @@ * @param level log level * @param mh method handle * @param paramStart first parameter to print - * @param printReturnValue should we print the return vaulue? + * @param printReturnValue should we print the return value? * @param text debug printout to add * * @return instrumented method handle, or null if logger not enabled
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java Thu Sep 24 10:09:56 2015 -0700 @@ -297,4 +297,3 @@ } } -
--- a/src/jdk/nashorn/internal/runtime/GlobalConstants.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/GlobalConstants.java Thu Sep 24 10:09:56 2015 -0700 @@ -67,7 +67,7 @@ * * Thus everything registered as a global constant gets an extra chance. Set once, * reregister the switchpoint. Set twice or more - don't try again forever, or we'd - * just end up relinking our way into megamorphisism. + * just end up relinking our way into megamorphism. * * Also it has to be noted that this kind of linking creates a coupling between a Global * and the call sites in compiled code belonging to the Context. For this reason, the
--- a/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Thu Sep 24 10:09:56 2015 -0700 @@ -187,14 +187,14 @@ double result = 0.0; int digit; - // we should see atleast one valid digit + // we should see at least one valid digit boolean entered = false; while (idx < length) { digit = fastDigit(str.charAt(idx++), radix); if (digit < 0) { break; } - // we have seen atleast one valid digit in the specified radix + // we have seen at least one valid digit in the specified radix entered = true; result *= radix; result += digit;
--- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java Thu Sep 24 10:09:56 2015 -0700 @@ -26,7 +26,6 @@ package jdk.nashorn.internal.runtime; import java.lang.invoke.MethodHandle; -import java.util.Iterator; import java.util.concurrent.Callable; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.JSONParser;
--- a/src/jdk/nashorn/internal/runtime/JSType.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Thu Sep 24 10:09:56 2015 -0700 @@ -1968,7 +1968,7 @@ /** * Get the unboxed (primitive) type for an object * @param o object - * @return primive type or Object.class if not primitive + * @return primitive type or Object.class if not primitive */ public static Class<?> unboxedFieldType(final Object o) { if (o == null) {
--- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Thu Sep 24 10:09:56 2015 -0700 @@ -206,7 +206,7 @@ } @Override - protected Object invokeNoSuchProperty(final String key, final int programPoint) { + protected Object invokeNoSuchProperty(final String key, final boolean isScope, final int programPoint) { final Object retval = createProperty(key); if (isValid(programPoint)) { throw new UnwarrantedOptimismException(retval, programPoint);
--- a/src/jdk/nashorn/internal/runtime/ParserException.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ParserException.java Thu Sep 24 10:09:56 2015 -0700 @@ -38,7 +38,7 @@ private final Source source; // token responsible for this exception private final long token; - // if this is traslated as ECMA error, which type should be used? + // if this is translated as ECMA error, which type should be used? private final JSErrorType errorType; /**
--- a/src/jdk/nashorn/internal/runtime/PropertyListeners.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/PropertyListeners.java Thu Sep 24 10:09:56 2015 -0700 @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.atomic.LongAdder; /** * Helper class to manage property listeners and notification. @@ -37,8 +38,15 @@ private Map<String, WeakPropertyMapSet> listeners; // These counters are updated in debug mode - private static int listenersAdded; - private static int listenersRemoved; + private static LongAdder listenersAdded; + private static LongAdder listenersRemoved; + + static { + if (Context.DEBUG) { + listenersAdded = new LongAdder(); + listenersRemoved = new LongAdder(); + } + } /** * Copy constructor @@ -54,16 +62,16 @@ * Return aggregate listeners added to all PropertyListenerManagers * @return the listenersAdded */ - public static int getListenersAdded() { - return listenersAdded; + public static long getListenersAdded() { + return listenersAdded.longValue(); } /** * Return aggregate listeners removed from all PropertyListenerManagers * @return the listenersRemoved */ - public static int getListenersRemoved() { - return listenersRemoved; + public static long getListenersRemoved() { + return listenersRemoved.longValue(); } /** @@ -122,7 +130,7 @@ */ synchronized final void addListener(final String key, final PropertyMap propertyMap) { if (Context.DEBUG) { - listenersAdded++; + listenersAdded.increment(); } if (listeners == null) { listeners = new WeakHashMap<>(); @@ -151,6 +159,9 @@ propertyMap.propertyAdded(prop); } listeners.remove(prop.getKey()); + if (Context.DEBUG) { + listenersRemoved.increment(); + } } } } @@ -168,6 +179,9 @@ propertyMap.propertyDeleted(prop); } listeners.remove(prop.getKey()); + if (Context.DEBUG) { + listenersRemoved.increment(); + } } } } @@ -187,6 +201,9 @@ propertyMap.propertyModified(oldProp, newProp); } listeners.remove(oldProp.getKey()); + if (Context.DEBUG) { + listenersRemoved.increment(); + } } } }
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Thu Sep 24 10:09:56 2015 -0700 @@ -42,6 +42,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.WeakHashMap; +import java.util.concurrent.atomic.LongAdder; import jdk.nashorn.internal.scripts.JO; /** @@ -114,7 +115,7 @@ } if (Context.DEBUG) { - count++; + count.increment(); } } @@ -135,8 +136,8 @@ this.freeSlots = propertyMap.freeSlots; if (Context.DEBUG) { - count++; - clonedCount++; + count.increment(); + clonedCount.increment(); } } @@ -328,7 +329,7 @@ if (sp != null) { protoGetSwitches.remove(key); if (Context.DEBUG) { - protoInvalidations++; + protoInvalidations.increment(); } SwitchPoint.invalidateAll(new SwitchPoint[] { sp }); } @@ -343,7 +344,7 @@ final int size = protoGetSwitches.size(); if (size > 0) { if (Context.DEBUG) { - protoInvalidations += size; + protoInvalidations.add(size); } SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[size])); protoGetSwitches.clear(); @@ -704,7 +705,7 @@ } if (Context.DEBUG && cachedMap != null) { - protoHistoryHit++; + protoHistoryHit.increment(); } return cachedMap; @@ -753,7 +754,7 @@ if (historicMap != null) { if (Context.DEBUG) { - historyHit++; + historyHit.increment(); } return historicMap; @@ -901,7 +902,7 @@ } if (Context.DEBUG) { - setProtoNewMapCount++; + setProtoNewMapCount.increment(); } final PropertyMap newMap = new PropertyMap(this); @@ -1021,52 +1022,62 @@ } // counters updated only in debug mode - private static int count; - private static int clonedCount; - private static int historyHit; - private static int protoInvalidations; - private static int protoHistoryHit; - private static int setProtoNewMapCount; + private static LongAdder count; + private static LongAdder clonedCount; + private static LongAdder historyHit; + private static LongAdder protoInvalidations; + private static LongAdder protoHistoryHit; + private static LongAdder setProtoNewMapCount; + static { + if (Context.DEBUG) { + count = new LongAdder(); + clonedCount = new LongAdder(); + historyHit = new LongAdder(); + protoInvalidations = new LongAdder(); + protoHistoryHit = new LongAdder(); + setProtoNewMapCount = new LongAdder(); + } + } /** * @return Total number of maps. */ - public static int getCount() { - return count; + public static long getCount() { + return count.longValue(); } /** * @return The number of maps that were cloned. */ - public static int getClonedCount() { - return clonedCount; + public static long getClonedCount() { + return clonedCount.longValue(); } /** * @return The number of times history was successfully used. */ - public static int getHistoryHit() { - return historyHit; + public static long getHistoryHit() { + return historyHit.longValue(); } /** * @return The number of times prototype changes caused invalidation. */ - public static int getProtoInvalidations() { - return protoInvalidations; + public static long getProtoInvalidations() { + return protoInvalidations.longValue(); } /** * @return The number of times proto history was successfully used. */ - public static int getProtoHistoryHit() { - return protoHistoryHit; + public static long getProtoHistoryHit() { + return protoHistoryHit.longValue(); } /** * @return The number of times prototypes were modified. */ - public static int getSetProtoNewMapCount() { - return setProtoNewMapCount; + public static long getSetProtoNewMapCount() { + return setProtoNewMapCount.longValue(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/PrototypeObject.java Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010, 2013, 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 static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import jdk.nashorn.internal.objects.Global; + +/** + * Instances of this class serve as "prototype" object for script functions. + * The purpose is to expose "constructor" property from "prototype". Also, nasgen + * generated prototype classes extend from this class. + */ +public class PrototypeObject extends ScriptObject { + private static final PropertyMap map$; + + private Object constructor; + + private static final MethodHandle GET_CONSTRUCTOR = findOwnMH("getConstructor", Object.class, Object.class); + private static final MethodHandle SET_CONSTRUCTOR = findOwnMH("setConstructor", void.class, Object.class, Object.class); + + static { + final ArrayList<Property> properties = new ArrayList<>(1); + properties.add(AccessorProperty.create("constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR)); + map$ = PropertyMap.newMap(properties); + } + + private PrototypeObject(final Global global, final PropertyMap map) { + super(global.getObjectPrototype(), map != map$? map.addAll(map$) : map$); + } + + /** + * Prototype constructor + */ + protected PrototypeObject() { + this(Global.instance(), map$); + } + + /** + * PropertyObject constructor + * + * @param map property map + */ + protected PrototypeObject(final PropertyMap map) { + this(Global.instance(), map); + } + + /** + * PropertyObject constructor + * + * @param func constructor function + */ + protected PrototypeObject(final ScriptFunction func) { + this(Global.instance(), map$); + this.constructor = func; + } + + /** + * Get the constructor for this {@code PrototypeObject} + * @param self self reference + * @return constructor, probably, but not necessarily, a {@link ScriptFunction} + */ + public static Object getConstructor(final Object self) { + return (self instanceof PrototypeObject) ? + ((PrototypeObject)self).getConstructor() : + UNDEFINED; + } + + /** + * Reset the constructor for this {@code PrototypeObject} + * @param self self reference + * @param constructor constructor, probably, but not necessarily, a {@link ScriptFunction} + */ + public static void setConstructor(final Object self, final Object constructor) { + if (self instanceof PrototypeObject) { + ((PrototypeObject)self).setConstructor(constructor); + } + } + + private Object getConstructor() { + return constructor; + } + + private void setConstructor(final Object constructor) { + this.constructor = constructor; + } + + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { + return MH.findStatic(MethodHandles.lookup(), PrototypeObject.class, name, MH.type(rtype, types)); + } +}
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Sep 24 10:09:56 2015 -0700 @@ -26,16 +26,25 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; + import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; @@ -45,8 +54,15 @@ import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; import jdk.nashorn.internal.codegen.TypeMap; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.SwitchNode; +import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.ir.TryNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Parser; @@ -55,6 +71,7 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; +import jdk.nashorn.internal.runtime.options.Options; /** * This is a subclass that represents a script function that may be regenerated, * for example with specialization based on call site types, or lazily generated. @@ -66,6 +83,8 @@ /** Prefix used for all recompiled script classes */ public static final String RECOMPILATION_PREFIX = "Recompilation$"; + private static final ExecutorService astSerializerExecutorService = createAstSerializerExecutorService(); + /** Unique function node id for this function node */ private final int functionNodeId; @@ -77,8 +96,12 @@ /** Source from which FunctionNode was parsed. */ private transient Source source; - /** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */ - private final byte[] serializedAst; + /** + * Cached form of the AST. Either a {@code SerializedAst} object used by split functions as they can't be + * reparsed from source, or a soft reference to a {@code FunctionNode} for other functions (it is safe + * to be cleared as they can be reparsed). + */ + private volatile Object cachedAst; /** Token of this function within the source. */ private final long token; @@ -128,7 +151,6 @@ * @param nestedFunctions nested function map * @param externalScopeDepths external scope depths * @param internalSymbols internal symbols to method, defined in its scope - * @param serializedAst a serialized AST representation. Normally only used for split functions. */ public RecompilableScriptFunctionData( final FunctionNode functionNode, @@ -136,8 +158,7 @@ final AllocationStrategy allocationStrategy, final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, final Map<String, Integer> externalScopeDepths, - final Set<String> internalSymbols, - final byte[] serializedAst) { + final Set<String> internalSymbols) { super(functionName(functionNode), Math.min(functionNode.getParameters().size(), MAX_ARITY), @@ -161,7 +182,6 @@ nfn.setParent(this); } - this.serializedAst = serializedAst; createLogger(); } @@ -244,7 +264,7 @@ * @return parent data, or null if non exists and also null IF UNKNOWN. */ public RecompilableScriptFunctionData getParent() { - return parent; + return parent; } void setParent(final RecompilableScriptFunctionData parent) { @@ -358,13 +378,11 @@ return allocationStrategy.allocate(map); } - boolean isSerialized() { - return serializedAst != null; - } - FunctionNode reparse() { - if (isSerialized()) { - return deserialize(); + final FunctionNode cachedFunction = getCachedAst(); + if (cachedFunction != null) { + assert cachedFunction.isCached(); + return cachedFunction; } final int descPosition = Token.descPosition(token); @@ -391,7 +409,104 @@ return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); } - private FunctionNode deserialize() { + private FunctionNode getCachedAst() { + final Object lCachedAst = cachedAst; + // Are we softly caching the AST? + if (lCachedAst instanceof Reference<?>) { + final FunctionNode fn = (FunctionNode)((Reference<?>)lCachedAst).get(); + if (fn != null) { + // Yes we are - this is fast + return cloneSymbols(fn); + } + // Are we strongly caching a serialized AST (for split functions only)? + } else if (lCachedAst instanceof SerializedAst) { + final SerializedAst serializedAst = (SerializedAst)lCachedAst; + // Even so, are we also softly caching the AST? + final FunctionNode cachedFn = serializedAst.cachedAst.get(); + if (cachedFn != null) { + // Yes we are - this is fast + return cloneSymbols(cachedFn); + } + final FunctionNode deserializedFn = deserialize(serializedAst.serializedAst); + // Softly cache after deserialization, maybe next time we won't need to deserialize + serializedAst.cachedAst = new SoftReference<>(deserializedFn); + return deserializedFn; + } + // No cached representation; return null for reparsing + return null; + } + + /** + * Sets the AST to cache in this function + * @param astToCache the new AST to cache + */ + public void setCachedAst(final FunctionNode astToCache) { + assert astToCache.getId() == functionNodeId; // same function + assert !(cachedAst instanceof SerializedAst); // Can't overwrite serialized AST + + final boolean isSplit = astToCache.isSplit(); + // If we're caching a split function, we're doing it in the eager pass, hence there can be no other + // cached representation already. In other words, isSplit implies cachedAst == null. + assert !isSplit || cachedAst == null; // + + final FunctionNode symbolClonedAst = cloneSymbols(astToCache); + final Reference<FunctionNode> ref = new SoftReference<>(symbolClonedAst); + cachedAst = ref; + + // Asynchronously serialize split functions. + if (isSplit) { + astSerializerExecutorService.execute(new Runnable() { + @Override + public void run() { + cachedAst = new SerializedAst(symbolClonedAst, ref); + } + }); + } + } + + /** + * Creates the AST serializer executor service used for in-memory serialization of split functions' ASTs. + * It is created with an unbounded queue (so it can queue any number of pending tasks). Its core and max + * threads is the same, but they are all allowed to time out so when there's no work, they can all go + * away. The threads will be daemons, and they will time out if idle for a minute. Their priority is also + * slightly lower than normal priority as we'd prefer the CPU to keep running the program; serializing + * split function is a memory conservation measure (it allows us to release the AST), it can wait a bit. + * @return an executor service with above described characteristics. + */ + private static ExecutorService createAstSerializerExecutorService() { + final int threads = Math.max(1, Options.getIntProperty("nashorn.serialize.threads", Runtime.getRuntime().availableProcessors() / 2)); + final ThreadPoolExecutor service = new ThreadPoolExecutor(threads, threads, 1L, TimeUnit.MINUTES, new LinkedBlockingDeque<Runnable>(), + new ThreadFactory() { + @Override + public Thread newThread(final Runnable r) { + final Thread t = new Thread(r, "Nashorn AST Serializer"); + t.setDaemon(true); + t.setPriority(Thread.NORM_PRIORITY - 1); + return t; + } + }); + service.allowCoreThreadTimeOut(true); + return service; + } + + /** + * A tuple of a serialized AST and a soft reference to a deserialized AST. This is used to cache split + * functions. Since split functions are altered from their source form, they can't be reparsed from + * source. While we could just use the {@code byte[]} representation in {@link RecompilableScriptFunctionData#cachedAst} + * we're using this tuple instead to also keep a deserialized AST around in memory to cut down on + * deserialization costs. + */ + private static class SerializedAst { + private final byte[] serializedAst; + private volatile Reference<FunctionNode> cachedAst; + + SerializedAst(final FunctionNode fn, final Reference<FunctionNode> cachedAst) { + this.serializedAst = AstSerializer.serialize(fn); + this.cachedAst = cachedAst; + } + } + + private FunctionNode deserialize(final byte[] serializedAst) { final ScriptEnvironment env = installer.getOwner(); final Timing timing = env._timing; final long t1 = System.nanoTime(); @@ -402,6 +517,107 @@ } } + private FunctionNode cloneSymbols(final FunctionNode fn) { + final IdentityHashMap<Symbol, Symbol> symbolReplacements = new IdentityHashMap<>(); + final boolean cached = fn.isCached(); + // blockDefinedSymbols is used to re-mark symbols defined outside the function as global. We only + // need to do this when we cache an eagerly parsed function (which currently means a split one, as we + // don't cache non-split functions from the eager pass); those already cached, or those not split + // don't need this step. + final Set<Symbol> blockDefinedSymbols = fn.isSplit() && !cached ? Collections.newSetFromMap(new IdentityHashMap<Symbol, Boolean>()) : null; + FunctionNode newFn = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + + private Symbol getReplacement(final Symbol original) { + if (original == null) { + return null; + } + final Symbol existingReplacement = symbolReplacements.get(original); + if (existingReplacement != null) { + return existingReplacement; + } + final Symbol newReplacement = original.clone(); + symbolReplacements.put(original, newReplacement); + return newReplacement; + } + + @Override + public Node leaveIdentNode(final IdentNode identNode) { + final Symbol oldSymbol = identNode.getSymbol(); + if (oldSymbol != null) { + final Symbol replacement = getReplacement(oldSymbol); + return identNode.setSymbol(replacement); + } + return identNode; + } + + @Override + public Node leaveForNode(final ForNode forNode) { + return ensureUniqueLabels(forNode.setIterator(lc, getReplacement(forNode.getIterator()))); + } + + @Override + public Node leaveSwitchNode(final SwitchNode switchNode) { + return ensureUniqueLabels(switchNode.setTag(lc, getReplacement(switchNode.getTag()))); + } + + @Override + public Node leaveTryNode(final TryNode tryNode) { + return ensureUniqueLabels(tryNode.setException(lc, getReplacement(tryNode.getException()))); + } + + @Override + public boolean enterBlock(final Block block) { + for(final Symbol symbol: block.getSymbols()) { + final Symbol replacement = getReplacement(symbol); + if (blockDefinedSymbols != null) { + blockDefinedSymbols.add(replacement); + } + } + return true; + } + + @Override + public Node leaveBlock(final Block block) { + return ensureUniqueLabels(block.replaceSymbols(lc, symbolReplacements)); + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + return functionNode.setParameters(lc, functionNode.visitParameters(this)); + } + + @Override + protected Node leaveDefault(final Node node) { + return ensureUniqueLabels(node); + }; + + private Node ensureUniqueLabels(final Node node) { + // If we're returning a cached AST, we must also ensure unique labels + return cached ? node.ensureUniqueLabels(lc) : node; + } + }); + + if (blockDefinedSymbols != null) { + // Mark all symbols not defined in blocks as globals + Block newBody = null; + for(final Symbol symbol: symbolReplacements.values()) { + if(!blockDefinedSymbols.contains(symbol)) { + assert symbol.isScope(); // must be scope + assert externalScopeDepths.containsKey(symbol.getName()); // must be known to us as an external + // Register it in the function body symbol table as a new global symbol + symbol.setFlags((symbol.getFlags() & ~Symbol.KINDMASK) | Symbol.IS_GLOBAL); + if (newBody == null) { + newBody = newFn.getBody().copyWithNewSymbols(); + newFn = newFn.setBody(null, newBody); + } + assert newBody.getExistingSymbol(symbol.getName()) == null; // must not be defined in the body already + newBody.putSymbol(symbol); + } + } + } + return newFn.setCached(null); + } + private boolean getFunctionFlag(final int flag) { return (functionFlags & flag) != 0; } @@ -512,9 +728,9 @@ final FunctionNode fn = reparse(); final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope); final FunctionNode compiledFn = compiler.compile(fn, - isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL); + fn.isCached() ? CompilationPhases.COMPILE_ALL_CACHED : CompilationPhases.COMPILE_ALL); - if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) { + if (persist && !compiledFn.hasApplyToCallSpecialization()) { compiler.persistClassInfo(cacheKey, compiledFn); } return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
--- a/src/jdk/nashorn/internal/runtime/Scope.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/Scope.java Thu Sep 24 10:09:56 2015 -0700 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; +import java.util.concurrent.atomic.LongAdder; import jdk.nashorn.internal.codegen.CompilerConstants; /** @@ -38,7 +39,7 @@ private int splitState = -1; /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */ - private static int count; + private static final LongAdder count = Context.DEBUG ? new LongAdder() : null; /** Method handle that points to {@link Scope#getSplitState}. */ public static final CompilerConstants.Call GET_SPLIT_STATE = virtualCallNoLookup(Scope.class, "getSplitState", int.class); @@ -52,9 +53,7 @@ */ public Scope(final PropertyMap map) { super(map); - if (Context.DEBUG) { - count++; - } + incrementCount(); } /** @@ -65,9 +64,7 @@ */ public Scope(final ScriptObject proto, final PropertyMap map) { super(proto, map); - if (Context.DEBUG) { - count++; - } + incrementCount(); } /** @@ -79,9 +76,7 @@ */ public Scope(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) { super(map, primitiveSpill, objectSpill); - if (Context.DEBUG) { - count++; - } + incrementCount(); } @Override @@ -123,7 +118,13 @@ * * @return number of scope ScriptObjects created */ - public static int getScopeCount() { - return count; + public static long getScopeCount() { + return count != null ? count.sum() : 0; + } + + private static void incrementCount() { + if (Context.DEBUG) { + count.increment(); + } } }
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Thu Sep 24 10:09:56 2015 -0700 @@ -46,6 +46,11 @@ * and output and error writers, top level Namespace etc. */ public final class ScriptEnvironment { + // Primarily intended to be used in test environments so that eager compilation tests work without an + // error when tested with optimistic compilation. + private static final boolean ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE = Options.getBooleanProperty( + "nashorn.options.allowEagerCompilationSilentOverride", false); + /** Output writer for this environment */ private final PrintWriter out; @@ -237,8 +242,20 @@ } _fx = options.getBoolean("fx"); _global_per_engine = options.getBoolean("global.per.engine"); - _lazy_compilation = options.getBoolean("lazy.compilation"); _optimistic_types = options.getBoolean("optimistic.types"); + final boolean lazy_compilation = options.getBoolean("lazy.compilation"); + if (!lazy_compilation && _optimistic_types) { + if (!ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE) { + throw new IllegalStateException( + ECMAErrors.getMessage( + "config.error.eagerCompilationConflictsWithOptimisticTypes", + options.getOptionTemplateByKey("lazy.compilation").getName(), + options.getOptionTemplateByKey("optimistic.types").getName())); + } + _lazy_compilation = true; + } else { + _lazy_compilation = lazy_compilation; + } _loader_per_compile = options.getBoolean("loader.per.compile"); _no_java = options.getBoolean("no.java"); _no_syntax_extensions = options.getBoolean("no.syntax.extensions");
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Sep 24 10:09:56 2015 -0700 @@ -22,7 +22,6 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; @@ -40,6 +39,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.concurrent.atomic.LongAdder; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -55,38 +55,54 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; /** - * Runtime representation of a JavaScript function. + * Runtime representation of a JavaScript function. This class has only private + * and protected constructors. There are no *public* constructors - but only + * factory methods that follow the naming pattern "createXYZ". */ -public abstract class ScriptFunction extends ScriptObject { +public class ScriptFunction extends ScriptObject { - /** Method handle for prototype getter for this ScriptFunction */ + /** + * Method handle for prototype getter for this ScriptFunction + */ public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class); - /** Method handle for prototype setter for this ScriptFunction */ + /** + * Method handle for prototype setter for this ScriptFunction + */ public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class); - /** Method handle for length getter for this ScriptFunction */ + /** + * Method handle for length getter for this ScriptFunction + */ public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class); - /** Method handle for name getter for this ScriptFunction */ + /** + * Method handle for name getter for this ScriptFunction + */ public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class); - /** Method handle used for implementing sync() in mozilla_compat */ + /** + * Method handle used for implementing sync() in mozilla_compat + */ public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class); - /** Method handle for allocate function for this ScriptFunction */ + /** + * Method handle for allocate function for this ScriptFunction + */ static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class); private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class); private static final MethodHandle SCRIPTFUNCTION_GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); - /** method handle to scope getter for this ScriptFunction */ + /** + * method handle to scope getter for this ScriptFunction + */ public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); - private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); + private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); - private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class); + private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class); private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class); @@ -94,55 +110,298 @@ private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class)); - /** The parent scope. */ + // various property maps used for different kinds of functions + // property map for anonymous function that serves as Function.prototype + private static final PropertyMap anonmap$; + // property map for strict mode functions + private static final PropertyMap strictmodemap$; + // property map for bound functions + private static final PropertyMap boundfunctionmap$; + // property map for non-strict, non-bound functions. + private static final PropertyMap map$; + + // Marker object for lazily initialized prototype object + private static final Object LAZY_PROTOTYPE = new Object(); + + private static PropertyMap createStrictModeMap(final PropertyMap map) { + final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; + PropertyMap newMap = map; + // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. + newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags)); + newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags)); + return newMap; + } + + private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { + // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see + // ECMAScript 5.1 section 15.3.4.5 + return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype")); + } + + static { + anonmap$ = PropertyMap.newMap(); + final ArrayList<Property> properties = new ArrayList<>(3); + properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE)); + properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null)); + properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null)); + map$ = PropertyMap.newMap(properties); + strictmodemap$ = createStrictModeMap(map$); + boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); + } + + private static boolean isStrict(final int flags) { + return (flags & ScriptFunctionData.IS_STRICT) != 0; + } + + // Choose the map based on strict mode! + private static PropertyMap getMap(final boolean strict) { + return strict ? strictmodemap$ : map$; + } + + /** + * The parent scope. + */ private final ScriptObject scope; private final ScriptFunctionData data; - /** The property map used for newly allocated object when function is used as constructor. */ + /** + * The property map used for newly allocated object when function is used as + * constructor. + */ protected PropertyMap allocatorMap; /** + * Reference to constructor prototype. + */ + protected Object prototype; + + /** * Constructor * - * @param name function name - * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) - * @param map property map - * @param scope scope - * @param specs specialized version of this function - other method handles - * @param flags {@link ScriptFunctionData} flags + * @param data static function data + * @param map property map + * @param scope scope */ - protected ScriptFunction( + private ScriptFunction( + final ScriptFunctionData data, + final PropertyMap map, + final ScriptObject scope, + final Global global) { + + super(map); + + if (Context.DEBUG) { + constructorCount.increment(); + } + + this.data = data; + this.scope = scope; + this.setInitialProto(global.getFunctionPrototype()); + this.prototype = LAZY_PROTOTYPE; + + // We have to fill user accessor functions late as these are stored + // in this object rather than in the PropertyMap of this object. + assert objectSpill == null; + if (isStrict() || isBoundFunction()) { + final ScriptFunction typeErrorThrower = global.getTypeErrorThrower(); + initUserAccessors("arguments", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); + initUserAccessors("caller", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); + } + } + + /** + * Constructor + * + * @param name function name + * @param methodHandle method handle to function (if specializations are + * present, assumed to be most generic) + * @param map property map + * @param scope scope + * @param specs specialized version of this function - other method handles + * @param flags {@link ScriptFunctionData} flags + */ + private ScriptFunction( final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, final Specialization[] specs, - final int flags) { - - this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope); + final int flags, + final Global global) { + this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope, global); } /** * Constructor * - * @param data static function data - * @param map property map - * @param scope scope + * @param name name of function + * @param methodHandle handle for invocation + * @param scope scope object + * @param specs specialized versions of this method, if available, null + * otherwise + * @param flags {@link ScriptFunctionData} flags + */ + private ScriptFunction( + final String name, + final MethodHandle methodHandle, + final ScriptObject scope, + final Specialization[] specs, + final int flags) { + this(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags, Global.instance()); + } + + /** + * Constructor called by Nasgen generated code, zero added members, use the + * default map. Creates builtin functions only. + * + * @param name name of function + * @param invokeHandle handle for invocation + * @param specs specialized versions of this method, if available, null + * otherwise + */ + protected ScriptFunction(final String name, final MethodHandle invokeHandle, final Specialization[] specs) { + this(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance()); + } + + /** + * Constructor called by Nasgen generated code, non zero member count, use + * the map passed as argument. Creates builtin functions only. + * + * @param name name of function + * @param invokeHandle handle for invocation + * @param map initial property map + * @param specs specialized versions of this method, if available, null + * otherwise + */ + protected ScriptFunction(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) { + this(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance()); + } + + // Factory methods to create various functions + /** + * Factory method called by compiler generated code for functions that need + * parent scope. + * + * @param constants the generated class' constant array + * @param index the index of the {@code RecompilableScriptFunctionData} + * object in the constants array. + * @param scope the parent scope object + * @return a newly created function object */ - protected ScriptFunction( - final ScriptFunctionData data, - final PropertyMap map, - final ScriptObject scope) { + public static ScriptFunction create(final Object[] constants, final int index, final ScriptObject scope) { + final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constants[index]; + return new ScriptFunction(data, getMap(data.isStrict()), scope, Global.instance()); + } + + /** + * Factory method called by compiler generated code for functions that don't + * need parent scope. + * + * @param constants the generated class' constant array + * @param index the index of the {@code RecompilableScriptFunctionData} + * object in the constants array. + * @return a newly created function object + */ + public static ScriptFunction create(final Object[] constants, final int index) { + return create(constants, index, null); + } + + /** + * Create anonymous function that serves as Function.prototype + * + * @return anonymous function object + */ + public static ScriptFunction createAnonymous() { + return new ScriptFunction("", GlobalFunctions.ANONYMOUS, anonmap$, null); + } + + // builtin function create helper factory + private static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) { + final ScriptFunction func = new ScriptFunction(name, methodHandle, null, specs, flags); + func.setPrototype(UNDEFINED); + // Non-constructor built-in functions do not have "prototype" property + func.deleteOwnProperty(func.getMap().findProperty("prototype")); + + return func; + } - super(map); + /** + * Factory method for non-constructor built-in functions + * + * @param name function name + * @param methodHandle handle for invocation + * @param specs specialized versions of function if available, null + * otherwise + * @return new ScriptFunction + */ + public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs) { + return ScriptFunction.createBuiltin(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN); + } - if (Context.DEBUG) { - constructorCount++; + /** + * Factory method for non-constructor built-in functions + * + * @param name function name + * @param methodHandle handle for invocation + * @return new ScriptFunction + */ + public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle) { + return ScriptFunction.createBuiltin(name, methodHandle, null); + } + + /** + * Factory method for non-constructor built-in, strict functions + * + * @param name function name + * @param methodHandle handle for invocation + * @return new ScriptFunction + */ + public static ScriptFunction createStrictBuiltin(final String name, final MethodHandle methodHandle) { + return ScriptFunction.createBuiltin(name, methodHandle, null, ScriptFunctionData.IS_BUILTIN | ScriptFunctionData.IS_STRICT); + } + + // Subclass to represent bound functions + private static class Bound extends ScriptFunction { + private final ScriptFunction target; + + Bound(final ScriptFunctionData boundData, final ScriptFunction target) { + super(boundData, boundfunctionmap$, null, Global.instance()); + setPrototype(ScriptRuntime.UNDEFINED); + this.target = target; } - this.data = data; - this.scope = scope; + @Override + protected ScriptFunction getTargetFunction() { + return target; + } + } + + /** + * Creates a version of this function bound to a specific "self" and other + * arguments, as per {@code Function.prototype.bind} functionality in + * ECMAScript 5.1 section 15.3.4.5. + * + * @param self the self to bind to this function. Can be null (in which + * case, null is bound as this). + * @param args additional arguments to bind to this function. Can be null or + * empty to not bind additional arguments. + * @return a function with the specified self and parameters bound. + */ + public final ScriptFunction createBound(final Object self, final Object[] args) { + return new Bound(data.makeBoundFunctionData(this, self, args), getTargetFunction()); + } + + /** + * Create a function that invokes this function synchronized on {@code sync} + * or the self object of the invocation. + * + * @param sync the Object to synchronize on, or undefined + * @return synchronized function + */ + public final ScriptFunction createSynchronized(final Object sync) { + final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync); + return createBuiltin(getName(), mh); } @Override @@ -151,8 +410,8 @@ } /** - * ECMA 15.3.5.3 [[HasInstance]] (V) - * Step 3 if "prototype" value is not an Object, throw TypeError + * ECMA 15.3.5.3 [[HasInstance]] (V) Step 3 if "prototype" value is not an + * Object, throw TypeError */ @Override public boolean isInstance(final ScriptObject instance) { @@ -171,22 +430,25 @@ } /** - * Returns the target function for this function. If the function was not created using - * {@link #makeBoundFunction(Object, Object[])}, its target function is itself. If it is bound, its target function - * is the target function of the function it was made from (therefore, the target function is always the final, - * unbound recipient of the calls). + * Returns the target function for this function. If the function was not + * created using {@link #createBound(Object, Object[])}, its target + * function is itself. If it is bound, its target function is the target + * function of the function it was made from (therefore, the target function + * is always the final, unbound recipient of the calls). + * * @return the target function for this function. */ protected ScriptFunction getTargetFunction() { return this; } - boolean isBoundFunction() { + final boolean isBoundFunction() { return getTargetFunction() != this; } /** * Set the arity of this ScriptFunction + * * @param arity arity */ public final void setArity(final int arity) { @@ -195,59 +457,66 @@ /** * Is this a ECMAScript 'use strict' function? + * * @return true if function is in strict mode */ - public boolean isStrict() { + public final boolean isStrict() { return data.isStrict(); } /** - * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument - * according to ECMA 10.4.3. + * Returns true if this is a non-strict, non-built-in function that requires + * non-primitive this argument according to ECMA 10.4.3. + * * @return true if this argument must be an object */ - public boolean needsWrappedThis() { + public final boolean needsWrappedThis() { return data.needsWrappedThis(); } private static boolean needsWrappedThis(final Object fn) { - return fn instanceof ScriptFunction ? ((ScriptFunction)fn).needsWrappedThis() : false; + return fn instanceof ScriptFunction ? ((ScriptFunction) fn).needsWrappedThis() : false; } /** * Execute this script function. - * @param self Target object. - * @param arguments Call arguments. + * + * @param self Target object. + * @param arguments Call arguments. * @return ScriptFunction result. - * @throws Throwable if there is an exception/error with the invocation or thrown from it + * @throws Throwable if there is an exception/error with the invocation or + * thrown from it */ - Object invoke(final Object self, final Object... arguments) throws Throwable { + final Object invoke(final Object self, final Object... arguments) throws Throwable { if (Context.DEBUG) { - invokes++; + invokes.increment(); } return data.invoke(this, self, arguments); } /** * Execute this script function as a constructor. - * @param arguments Call arguments. + * + * @param arguments Call arguments. * @return Newly constructed result. - * @throws Throwable if there is an exception/error with the invocation or thrown from it + * @throws Throwable if there is an exception/error with the invocation or + * thrown from it */ - Object construct(final Object... arguments) throws Throwable { + final Object construct(final Object... arguments) throws Throwable { return data.construct(this, arguments); } /** - * Allocate function. Called from generated {@link ScriptObject} code - * for allocation as a factory method + * Allocate function. Called from generated {@link ScriptObject} code for + * allocation as a factory method * - * @return a new instance of the {@link ScriptObject} whose allocator this is + * @return a new instance of the {@link ScriptObject} whose allocator this + * is */ @SuppressWarnings("unused") private Object allocate() { if (Context.DEBUG) { - allocations++; + allocations.increment(); } assert !isBoundFunction(); // allocate never invoked on bound functions @@ -257,7 +526,7 @@ if (object != null) { final Object prototype = getPrototype(); if (prototype instanceof ScriptObject) { - object.setInitialProto((ScriptObject)prototype); + object.setInitialProto((ScriptObject) prototype); } if (object.getProto() == null) { @@ -277,43 +546,28 @@ /** * Return Object.prototype - used by "allocate" + * * @return Object.prototype */ - protected abstract ScriptObject getObjectPrototype(); - - /** - * Creates a version of this function bound to a specific "self" and other arguments, as per - * {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5. - * @param self the self to bind to this function. Can be null (in which case, null is bound as this). - * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. - * @return a function with the specified self and parameters bound. - */ - protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) { - return makeBoundFunction(data.makeBoundFunctionData(this, self, args)); + protected final ScriptObject getObjectPrototype() { + return Global.objectPrototype(); } - /** - * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])}, - * but using a {@link ScriptFunctionData} for the bound data. - * - * @param boundData ScriptFuntionData for the bound function - * @return a function with the bindings performed according to the given data - */ - protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData); - @Override public final String safeToString() { return toSource(); } @Override - public String toString() { + public final String toString() { return data.toString(); } /** - * Get this function as a String containing its source code. If no source code - * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} + * Get this function as a String containing its source code. If no source + * code exists in this ScriptFunction, its contents will be displayed as + * {@code [native code]} + * * @return string representation of this function's source */ public final String toSource() { @@ -322,27 +576,32 @@ /** * Get the prototype object for this function + * * @return prototype */ - public abstract Object getPrototype(); + public final Object getPrototype() { + if (prototype == LAZY_PROTOTYPE) { + prototype = new PrototypeObject(this); + } + return prototype; + } /** * Set the prototype object for this function - * @param prototype new prototype object + * + * @param newPrototype new prototype object */ - public abstract void setPrototype(Object prototype); + public final void setPrototype(final Object newPrototype) { + if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) { + // Replace our current allocator map with one that is associated with the new prototype. + allocatorMap = allocatorMap.changeProto((ScriptObject) newPrototype); + } + this.prototype = newPrototype; + } /** - * Create a function that invokes this function synchronized on {@code sync} or the self object - * of the invocation. - * @param sync the Object to synchronize on, or undefined - * @return synchronized function - */ - public abstract ScriptFunction makeSynchronizedFunction(Object sync); - - /** - * Return the invoke handle bound to a given ScriptObject self reference. - * If callee parameter is required result is rebound to this. + * Return the invoke handle bound to a given ScriptObject self reference. If + * callee parameter is required result is rebound to this. * * @param self self reference * @return bound invoke handle @@ -352,9 +611,12 @@ } /** - * Bind the method handle to this {@code ScriptFunction} instance if it needs a callee parameter. If this function's - * method handles don't have a callee parameter, the handle is returned unchanged. - * @param methodHandle the method handle to potentially bind to this function instance. + * Bind the method handle to this {@code ScriptFunction} instance if it + * needs a callee parameter. If this function's method handles don't have a + * callee parameter, the handle is returned unchanged. + * + * @param methodHandle the method handle to potentially bind to this + * function instance. * @return the potentially bound method handle */ private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) { @@ -364,15 +626,16 @@ /** * Get the name for this function + * * @return the name */ public final String getName() { return data.getName(); } - /** * Get the scope for this function + * * @return the scope */ public final ScriptObject getScope() { @@ -383,36 +646,37 @@ * Prototype getter for this ScriptFunction - follows the naming convention * used by Nasgen and the code generator * - * @param self self reference + * @param self self reference * @return self's prototype */ public static Object G$prototype(final Object self) { - return self instanceof ScriptFunction ? - ((ScriptFunction)self).getPrototype() : - UNDEFINED; + return self instanceof ScriptFunction + ? ((ScriptFunction) self).getPrototype() + : UNDEFINED; } /** * Prototype setter for this ScriptFunction - follows the naming convention * used by Nasgen and the code generator * - * @param self self reference + * @param self self reference * @param prototype prototype to set */ public static void S$prototype(final Object self, final Object prototype) { if (self instanceof ScriptFunction) { - ((ScriptFunction)self).setPrototype(prototype); + ((ScriptFunction) self).setPrototype(prototype); } } /** * Length getter - ECMA 15.3.3.2: Function.length + * * @param self self reference * @return length */ public static int G$length(final Object self) { if (self instanceof ScriptFunction) { - return ((ScriptFunction)self).data.getArity(); + return ((ScriptFunction) self).data.getArity(); } return 0; @@ -420,12 +684,13 @@ /** * Name getter - ECMA Function.name + * * @param self self refence * @return the name, or undefined if none */ public static Object G$name(final Object self) { if (self instanceof ScriptFunction) { - return ((ScriptFunction)self).getName(); + return ((ScriptFunction) self).getName(); } return UNDEFINED; @@ -433,6 +698,7 @@ /** * Get the prototype for this ScriptFunction + * * @param constructor constructor * @return prototype, or null if given constructor is not a ScriptFunction */ @@ -440,7 +706,7 @@ if (constructor != null) { final Object proto = constructor.getPrototype(); if (proto instanceof ScriptObject) { - return (ScriptObject)proto; + return (ScriptObject) proto; } } @@ -448,29 +714,37 @@ } // These counters are updated only in debug mode. - private static int constructorCount; - private static int invokes; - private static int allocations; + private static LongAdder constructorCount; + private static LongAdder invokes; + private static LongAdder allocations; + + static { + if (Context.DEBUG) { + constructorCount = new LongAdder(); + invokes = new LongAdder(); + allocations = new LongAdder(); + } + } /** * @return the constructorCount */ - public static int getConstructorCount() { - return constructorCount; + public static long getConstructorCount() { + return constructorCount.longValue(); } /** * @return the invokes */ - public static int getInvokes() { - return invokes; + public static long getInvokes() { + return invokes.longValue(); } /** * @return the allocations */ - public static int getAllocations() { - return allocations; + public static long getAllocations() { + return allocations.longValue(); } @Override @@ -490,7 +764,6 @@ return Context.getGlobal().wrapAsObject(obj); } - @SuppressWarnings("unused") private static Object globalFilter(final Object object) { // replace whatever we get with the current global object @@ -498,14 +771,16 @@ } /** - * Some receivers are primitive, in that case, according to the Spec we create a new - * native object per callsite with the wrap filter. We can only apply optimistic builtins - * if there is no per instance state saved for these wrapped objects (e.g. currently NativeStrings), - * otherwise we can't create optimistic versions + * Some receivers are primitive, in that case, according to the Spec we + * create a new native object per callsite with the wrap filter. We can only + * apply optimistic builtins if there is no per instance state saved for + * these wrapped objects (e.g. currently NativeStrings), otherwise we can't + * create optimistic versions * - * @param self receiver - * @param linkLogicClass linkLogicClass, or null if no link logic exists - * @return link logic instance, or null if one could not be constructed for this receiver + * @param self receiver + * @param linkLogicClass linkLogicClass, or null if no link logic exists + * @return link logic instance, or null if one could not be constructed for + * this receiver */ private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) { if (linkLogicClass == null) { @@ -518,25 +793,25 @@ final Object wrappedSelf = wrapFilter(self); if (wrappedSelf instanceof OptimisticBuiltins) { - if (wrappedSelf != self && ((OptimisticBuiltins)wrappedSelf).hasPerInstanceAssumptions()) { + if (wrappedSelf != self && ((OptimisticBuiltins) wrappedSelf).hasPerInstanceAssumptions()) { return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state } - return ((OptimisticBuiltins)wrappedSelf).getLinkLogic(linkLogicClass); + return ((OptimisticBuiltins) wrappedSelf).getLinkLogic(linkLogicClass); } return null; } /** - * dyn:call call site signature: (callee, thiz, [args...]) - * generated method signature: (callee, thiz, [args...]) + * dyn:call call site signature: (callee, thiz, [args...]) generated method + * signature: (callee, thiz, [args...]) * * cases: * (a) method has callee parameter - * (1) for local/scope calls, we just bind thiz and drop the second argument. - * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. + * (1) for local/scope calls, we just bind thiz and drop the second argument. + * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. * (b) method doesn't have callee parameter (builtin functions) - * (3) for local/scope calls, bind thiz and drop both callee and thiz. - * (4) for normal this-calls, drop callee. + * (3) for local/scope calls, bind thiz and drop both callee and thiz. + * (4) for normal this-calls, drop callee. * * @return guarded invocation for call */ @@ -544,11 +819,11 @@ protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { final MethodType type = desc.getMethodType(); - final String name = getName(); + final String name = getName(); final boolean isUnstable = request.isCallSiteUnstable(); - final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); - final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name); - final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name); + final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); + final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name); + final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name); final boolean isApplyOrCall = isCall | isApply; @@ -569,7 +844,7 @@ return new GuardedInvocation( handle, null, - (SwitchPoint)null, + (SwitchPoint) null, ClassCastException.class); } @@ -672,14 +947,14 @@ this, cf.getFlags()) : guard, - spsArray, + spsArray, exceptionGuard); } private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) { final MethodType descType = desc.getMethodType(); final int paramCount = descType.parameterCount(); - if(descType.parameterType(paramCount - 1).isArray()) { + if (descType.parameterType(paramCount - 1).isArray()) { // This is vararg invocation of apply or call. This can normally only happen when we do a recursive // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader. @@ -786,7 +1061,7 @@ inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS); } else { // If the original call site doesn't pass argArray, pass in an empty array - inv = MH.insertArguments(inv, 2, (Object)ScriptRuntime.EMPTY_ARRAY); + inv = MH.insertArguments(inv, 2, (Object) ScriptRuntime.EMPTY_ARRAY); } } @@ -851,7 +1126,7 @@ final LinkRequest request, final Object[] args) { final MethodType descType = desc.getMethodType(); final int paramCount = descType.parameterCount(); - final Object[] varArgs = (Object[])args[paramCount - 1]; + final Object[] varArgs = (Object[]) args[paramCount - 1]; // -1 'cause we're not passing the vararg array itself final int copiedArgCount = args.length - 1; final int varArgCount = varArgs.length; @@ -893,7 +1168,7 @@ // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail // with ClassCastException of NativeArray to Object[]. - if(guardType.parameterType(guardParamCount - 1).isArray()) { + if (guardType.parameterType(guardParamCount - 1).isArray()) { arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS); } else { arrayConvertingGuard = guard; @@ -903,19 +1178,20 @@ } private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) { - final MethodHandle bound; - if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) { - bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER); - } else { - bound = mh; - } - return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED); - } + final MethodHandle bound; + if (fn instanceof ScriptFunction && ((ScriptFunction) fn).needsWrappedThis()) { + bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER); + } else { + bound = mh; + } + return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED); + } /** * Used for noSuchMethod/noSuchProperty and JSAdapter hooks. * - * These don't want a callee parameter, so bind that. Name binding is optional. + * These don't want a callee parameter, so bind that. Name binding is + * optional. */ MethodHandle getCallMethodHandle(final MethodType type, final String bindName) { return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type); @@ -939,10 +1215,11 @@ } /** - * Get the guard that checks if a {@link ScriptFunction} is equal to - * a known ScriptFunction, using reference comparison + * Get the guard that checks if a {@link ScriptFunction} is equal to a known + * ScriptFunction, using reference comparison * - * @param function The ScriptFunction to check against. This will be bound to the guard method handle + * @param function The ScriptFunction to check against. This will be bound + * to the guard method handle * * @return method handle for guard */ @@ -957,11 +1234,12 @@ } /** - * Get a guard that checks if a {@link ScriptFunction} is equal to - * a known ScriptFunction using reference comparison, and whether the type of - * the second argument (this-object) is not a JavaScript primitive type. + * Get a guard that checks if a {@link ScriptFunction} is equal to a known + * ScriptFunction using reference comparison, and whether the type of the + * second argument (this-object) is not a JavaScript primitive type. * - * @param function The ScriptFunction to check against. This will be bound to the guard method handle + * @param function The ScriptFunction to check against. This will be bound + * to the guard method handle * * @return method handle for guard */ @@ -972,12 +1250,12 @@ @SuppressWarnings("unused") private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) { - return self instanceof ScriptFunction && ((ScriptFunction)self).data == data; + return self instanceof ScriptFunction && ((ScriptFunction) self).data == data; } @SuppressWarnings("unused") private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) { - return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject; + return self instanceof ScriptFunction && ((ScriptFunction) self).data == data && arg instanceof ScriptObject; } //TODO this can probably be removed given that we have builtin switchpoints in the context @@ -990,7 +1268,7 @@ @SuppressWarnings("unused") private static Object[] addZerothElement(final Object[] args, final Object value) { // extends input array with by adding new zeroth element - final Object[] src = args == null? ScriptRuntime.EMPTY_ARRAY : args; + final Object[] src = args == null ? ScriptRuntime.EMPTY_ARRAY : args; final Object[] result = new Object[src.length + 1]; System.arraycopy(src, 0, result, 1, src.length); result[0] = value; @@ -1014,4 +1292,3 @@ return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types)); } } -
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Thu Sep 24 10:09:56 2015 -0700 @@ -151,7 +151,7 @@ * Is this a ScriptFunction generated with strict semantics? * @return true if strict, false otherwise */ - public boolean isStrict() { + public final boolean isStrict() { return (flags & IS_STRICT) != 0; } @@ -164,11 +164,11 @@ return getName(); } - boolean isBuiltin() { + final boolean isBuiltin() { return (flags & IS_BUILTIN) != 0; } - boolean isConstructor() { + final boolean isConstructor() { return (flags & IS_CONSTRUCTOR) != 0; } @@ -179,7 +179,7 @@ * according to ECMA 10.4.3. * @return true if this argument must be an object */ - boolean needsWrappedThis() { + final boolean needsWrappedThis() { return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0; } @@ -318,7 +318,7 @@ * Used to find an apply to call version that fits this callsite. * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int) * for (Object, Object, int, int, int) or we will destroy the semantics and get - * a function that, when padded with undefineds, behaves differently + * a function that, when padded with undefined values, behaves differently * @param type actual call site type * @return apply to call that perfectly fits this callsite or null if none found */ @@ -397,7 +397,7 @@ /** * This method is used to create the immutable portion of a bound function. - * See {@link ScriptFunction#makeBoundFunction(Object, Object[])} + * See {@link ScriptFunction#createBound(Object, Object[])} * * @param fn the original function being bound * @param self this reference to bind. Can be null.
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu Sep 24 10:09:56 2015 -0700 @@ -64,6 +64,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.LongAdder; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -148,7 +149,7 @@ /** Method handle to retrieve prototype of this object */ public static final MethodHandle GETPROTO = findOwnMH_V("getProto", ScriptObject.class); - static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class); + static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class, boolean.class); static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class); @@ -211,7 +212,7 @@ */ public ScriptObject(final PropertyMap map) { if (Context.DEBUG) { - ScriptObject.count++; + ScriptObject.count.increment(); } this.arrayData = ArrayData.EMPTY_ARRAY; this.setMap(map == null ? PropertyMap.newMap() : map); @@ -224,7 +225,7 @@ * same combination of prototype and property map. * * @param proto the prototype object - * @param map intial {@link PropertyMap} + * @param map initial {@link PropertyMap} */ protected ScriptObject(final ScriptObject proto, final PropertyMap map) { this(map); @@ -1257,7 +1258,7 @@ if (oldProto != newProto) { proto = newProto; - // Let current listeners know that the protototype has changed and set our map + // Let current listeners know that the prototype has changed and set our map final PropertyListeners listeners = getMap().getListeners(); if (listeners != null) { listeners.protoChanged(); @@ -1452,7 +1453,7 @@ * in {@link ScriptFunction} for hasInstance implementation, walks * the proto chain * - * @param instance instace to check + * @param instance instance to check * @return true if 'instance' is an instance of this object */ public boolean isInstance(final ScriptObject instance) { @@ -1869,7 +1870,7 @@ * @param desc the call site descriptor. * @param request the link request * - * @return GuardedInvocation to be invoed at call site. + * @return GuardedInvocation to be invoked at call site. */ protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { return notAFunction(desc); @@ -2019,19 +2020,19 @@ private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) { Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: " + desc + " " + name + " " +isMethod); - final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod); + final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, NashornCallSiteDescriptor.isScope(desc)); final MethodHandle guard = getScriptObjectGuard(desc.getMethodType(), true); return new GuardedInvocation(invoker, guard); } @SuppressWarnings("unused") - private Object megamorphicGet(final String key, final boolean isMethod) { + private Object megamorphicGet(final String key, final boolean isMethod, final boolean isScope) { final FindProperty find = findProperty(key, true); if (find != null) { return find.getObjectValue(); } - return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); + return isMethod ? getNoSuchMethod(key, isScope, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, isScope, INVALID_PROGRAM_POINT); } // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST @@ -2301,7 +2302,7 @@ MH.dropArguments( MH.constant( ScriptFunction.class, - func.makeBoundFunction(thiz, new Object[] { name })), + func.createBound(thiz, new Object[] { name })), 0, Object.class), NashornGuards.combineGuards( @@ -2366,20 +2367,21 @@ /** * Invoke fall back if a property is not found. * @param name Name of property. + * @param isScope is this a scope access? * @param programPoint program point * @return Result from call. */ - protected Object invokeNoSuchProperty(final String name, final int programPoint) { + protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) { final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); + final Object func = (find != null)? find.getObjectValue() : null; Object ret = UNDEFINED; - - if (find != null) { - final Object func = find.getObjectValue(); - - if (func instanceof ScriptFunction) { - ret = ScriptRuntime.apply((ScriptFunction)func, this, name); - } + if (func instanceof ScriptFunction) { + final ScriptFunction sfunc = (ScriptFunction)func; + final Object self = isScope && sfunc.isStrict()? UNDEFINED : this; + ret = ScriptRuntime.apply(sfunc, self, name); + } else if (isScope) { + throw referenceError("not.defined", name); } if (isValid(programPoint)) { @@ -2393,21 +2395,27 @@ /** * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined. * @param name the method name + * @param isScope is this a scope access? * @return the bound function, or undefined */ - private Object getNoSuchMethod(final String name, final int programPoint) { + private Object getNoSuchMethod(final String name, final boolean isScope, final int programPoint) { final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); if (find == null) { - return invokeNoSuchProperty(name, programPoint); + return invokeNoSuchProperty(name, isScope, programPoint); } final Object value = find.getObjectValue(); if (!(value instanceof ScriptFunction)) { + if (isScope) { + throw referenceError("not.defined", name); + } return UNDEFINED; } - return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name}); + final ScriptFunction func = (ScriptFunction)value; + final Object self = isScope && func.isStrict()? UNDEFINED : this; + return func.createBound(self, new Object[] {name}); } private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) { @@ -2722,7 +2730,7 @@ } } - return JSType.toInt32(invokeNoSuchProperty(key, programPoint)); + return JSType.toInt32(invokeNoSuchProperty(key, false, programPoint)); } @Override @@ -2804,7 +2812,7 @@ } } - return JSType.toLong(invokeNoSuchProperty(key, programPoint)); + return JSType.toLong(invokeNoSuchProperty(key, false, programPoint)); } @Override @@ -2886,7 +2894,7 @@ } } - return JSType.toNumber(invokeNoSuchProperty(key, INVALID_PROGRAM_POINT)); + return JSType.toNumber(invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT)); } @Override @@ -2967,7 +2975,7 @@ } } - return invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); + return invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT); } @Override @@ -3796,15 +3804,20 @@ } /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */ - private static int count; - + private static LongAdder count; + + static { + if (Context.DEBUG) { + count = new LongAdder(); + } + } /** * Get number of {@code ScriptObject} instances created. If not running in debug * mode this is always 0 * * @return number of ScriptObjects created */ - public static int getCount() { - return count; + public static long getCount() { + return count.longValue(); } }
--- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Sep 24 10:09:56 2015 -0700 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; @@ -70,6 +71,9 @@ /** EXIT name - special property used by $EXEC API. */ public static final String EXIT_NAME = "$EXIT"; + /** THROW_ON_ERROR name - special property of the $EXEC function used by $EXEC API. */ + public static final String THROW_ON_ERROR_NAME = "throwOnError"; + /** Names of special properties used by $ENV API. */ public static final String ENV_NAME = "$ENV"; @@ -244,6 +248,19 @@ } } + // if we got a non-zero exit code ("failure"), then we have to decide to throw error or not + if (exit != 0) { + // get the $EXEC function object from the global object + final Object exec = global.get(EXEC_NAME); + assert exec instanceof ScriptObject : EXEC_NAME + " is not a script object!"; + + // Check if the user has set $EXEC.throwOnError property to true. If so, throw RangeError + // If that property is not set or set to false, then silently proceed with the rest. + if (JSType.toBoolean(((ScriptObject)exec).get(THROW_ON_ERROR_NAME))) { + throw rangeError("exec.returned.non.zero", ScriptRuntime.safeToString(exit)); + } + } + // Return the result from stdout. return out; }
--- a/src/jdk/nashorn/internal/runtime/Timing.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/Timing.java Thu Sep 24 10:09:56 2015 -0700 @@ -28,12 +28,14 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Function; import java.util.function.Supplier; - import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; @@ -156,11 +158,15 @@ } final class TimeSupplier implements Supplier<String> { - private final Map<String, Long> timings; - - TimeSupplier() { - timings = new LinkedHashMap<>(); - } + private final Map<String, LongAdder> timings = new ConcurrentHashMap<>(); + private final LinkedBlockingQueue<String> orderedTimingNames = new LinkedBlockingQueue<>(); + private final Function<String, LongAdder> newTimingCreator = new Function<String, LongAdder>() { + @Override + public LongAdder apply(final String s) { + orderedTimingNames.add(s); + return new LongAdder(); + } + }; String[] getStrings() { final List<String> strs = new ArrayList<>(); @@ -184,26 +190,26 @@ int maxKeyLength = 0; int maxValueLength = 0; - for (final Map.Entry<String, Long> entry : timings.entrySet()) { + for (final Map.Entry<String, LongAdder> entry : timings.entrySet()) { maxKeyLength = Math.max(maxKeyLength, entry.getKey().length()); - maxValueLength = Math.max(maxValueLength, toMillisPrint(entry.getValue()).length()); + maxValueLength = Math.max(maxValueLength, toMillisPrint(entry.getValue().longValue()).length()); } maxKeyLength++; final StringBuilder sb = new StringBuilder(); sb.append("Accumulated compilation phase timings:\n\n"); - for (final Map.Entry<String, Long> entry : timings.entrySet()) { + for (final String timingName: orderedTimingNames) { int len; len = sb.length(); - sb.append(entry.getKey()); + sb.append(timingName); len = sb.length() - len; while (len++ < maxKeyLength) { sb.append(' '); } - final Long duration = entry.getValue(); + final long duration = timings.get(timingName).longValue(); final String strDuration = toMillisPrint(duration); len = strDuration.length(); for (int i = 0; i < maxValueLength - len; i++) { @@ -233,11 +239,7 @@ } private void accumulateTime(final String module, final long duration) { - Long accumulatedTime = timings.get(module); - if (accumulatedTime == null) { - accumulatedTime = 0L; - } - timings.put(module, accumulatedTime + duration); + timings.computeIfAbsent(module, newTimingCreator).add(duration); } } }
--- a/src/jdk/nashorn/internal/runtime/WithObject.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/WithObject.java Thu Sep 24 10:09:56 2015 -0700 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -209,16 +210,18 @@ } @Override - protected Object invokeNoSuchProperty(final String name, final int programPoint) { + protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) { FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true); if (find != null) { final Object func = find.getObjectValue(); if (func instanceof ScriptFunction) { - return ScriptRuntime.apply((ScriptFunction)func, expression, name); + final ScriptFunction sfunc = (ScriptFunction)func; + final Object self = isScope && sfunc.isStrict()? UNDEFINED : expression; + return ScriptRuntime.apply(sfunc, self, name); } } - return getProto().invokeNoSuchProperty(name, programPoint); + return getProto().invokeNoSuchProperty(name, isScope, programPoint); } @Override @@ -352,7 +355,7 @@ } private static Object bindToExpression(final ScriptFunction fn, final Object receiver) { - return fn.makeBoundFunction(withFilterExpression(receiver), ScriptRuntime.EMPTY_ARRAY); + return fn.createBound(withFilterExpression(receiver), ScriptRuntime.EMPTY_ARRAY); } private MethodHandle expressionGuard(final String name, final ScriptObject owner) {
--- a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Thu Sep 24 10:09:56 2015 -0700 @@ -191,7 +191,7 @@ /** * Return element setter for a {@link ContinuousArrayData} - * @param clazz clazz for exact type guard + * @param clazz class for exact type guard * @param setHas set has guard * @param elementType element type * @return method handle for element setter
--- a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java Thu Sep 24 10:09:56 2015 -0700 @@ -34,7 +34,7 @@ * This filter handles the presence of undefined array elements. */ final class UndefinedArrayFilter extends ArrayFilter { - /** Bit vector tracking undefines. */ + /** Bit vector tracking undefined slots. */ private final BitVector undefined; UndefinedArrayFilter(final ArrayData underlying) {
--- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Sep 24 10:09:56 2015 -0700 @@ -50,7 +50,6 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.lookup.MethodHandleFactory; import jdk.nashorn.internal.lookup.MethodHandleFunctionality; -import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.OptimisticReturnFilters; @@ -70,7 +69,7 @@ private static final MethodHandle VOID_TO_OBJECT = MH.constant(Object.class, ScriptRuntime.UNDEFINED); /** - * The default dynalink relink threshold for megamorphisism is 8. In the case + * The default dynalink relink threshold for megamorphism is 8. In the case * of object fields only, it is fine. However, with dual fields, in order to get * performance on benchmarks with a lot of object instantiation and then field * reassignment, it can take slightly more relinks to become stable with type @@ -214,7 +213,7 @@ * @param type method type * @param programPoint program point to bind to callsite * - * @return callsite for a math instrinic node + * @return callsite for a math intrinsic node */ public static CallSite mathBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int programPoint) { final MethodHandle mh; @@ -397,8 +396,8 @@ * @throws ECMAException with {@code TypeError} if the object is not a callable. */ public static Object bindCallable(final Object callable, final Object boundThis, final Object[] boundArgs) { - if (callable instanceof ScriptFunctionImpl) { - return ((ScriptFunctionImpl)callable).makeBoundFunction(boundThis, boundArgs); + if (callable instanceof ScriptFunction) { + return ((ScriptFunction)callable).createBound(boundThis, boundArgs); } else if (callable instanceof BoundCallable) { return ((BoundCallable)callable).bind(boundArgs); } else if (isCallable(callable)) {
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu Sep 24 10:09:56 2015 -0700 @@ -571,7 +571,7 @@ mv.visitVarInsn(ALOAD, 0); if (fromFunction && !mi.getName().equals(samName)) { // Constructors initializing from a ScriptFunction only initialize methods with the SAM name. - // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This + // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overridden too. This // is a deliberate design choice. All other method handles are initialized to null. mv.visitInsn(ACONST_NULL); } else {
--- a/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Thu Sep 24 10:09:56 2015 -0700 @@ -43,6 +43,7 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; import jdk.internal.dynalink.ChainedCallSite; import jdk.internal.dynalink.DynamicLinker; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -70,7 +71,7 @@ LinkerCallSite(final NashornCallSiteDescriptor descriptor) { super(descriptor); if (Context.DEBUG) { - LinkerCallSite.count++; + LinkerCallSite.count.increment(); } } @@ -173,7 +174,7 @@ * @return self reference */ public static Object increaseMissCount(final String desc, final Object self) { - ++missCount; + missCount.increment(); if (r.nextInt(100) < missSamplingPercentage) { final AtomicInteger i = missCounts.get(desc); if (i == null) { @@ -500,7 +501,7 @@ * @param desc callsite descriptor string * @param args arguments to function * - * @throws Throwable if invocation failes or throws exception/error + * @throws Throwable if invocation fails or throws exception/error */ @SuppressWarnings("unused") public void traceMiss(final String desc, final Object... args) throws Throwable { @@ -509,12 +510,19 @@ } // counters updated in debug mode - private static int count; + private static LongAdder count; private static final HashMap<String, AtomicInteger> missCounts = new HashMap<>(); - private static int missCount; + private static LongAdder missCount; private static final Random r = new Random(); private static final int missSamplingPercentage = Options.getIntProperty("nashorn.tcs.miss.samplePercent", 1); + static { + if (Context.DEBUG) { + count = new LongAdder(); + missCount = new LongAdder(); + } + } + @Override protected int getMaxChainLength() { return 8; @@ -524,16 +532,16 @@ * Get the callsite count * @return the count */ - public static int getCount() { - return count; + public static long getCount() { + return count.longValue(); } /** * Get the callsite miss count * @return the missCount */ - public static int getMissCount() { - return missCount; + public static long getMissCount() { + return missCount.longValue(); } /**
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Thu Sep 24 10:09:56 2015 -0700 @@ -26,7 +26,6 @@ package jdk.nashorn.internal.runtime.linker; import static jdk.nashorn.internal.lookup.Lookup.MH; -import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -42,13 +41,11 @@ import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.MethodHandleTransformer; import jdk.internal.dynalink.support.DefaultInternalObjectFilter; -import jdk.internal.dynalink.support.Guards; import jdk.internal.dynalink.support.Lookup; import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.options.Options; /** @@ -171,7 +168,7 @@ } for (final Class<?> iface : clazz.getInterfaces()) { - // check accessiblity up-front + // check accessibility up-front if (! Context.isAccessibleClass(iface)) { continue; }
--- a/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Thu Sep 24 10:09:56 2015 -0700 @@ -323,7 +323,7 @@ * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code * generated outside of Nashorn. * @param flag the tested flag - * @return true if the flag is set, false otherwise (it will be false if the decriptor is not a Nashorn call site + * @return true if the flag is set, false otherwise (it will be false if the descriptor is not a Nashorn call site * descriptor). */ private static boolean isFlag(final CallSiteDescriptor desc, final int flag) {
--- a/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Thu Sep 24 10:09:56 2015 -0700 @@ -163,7 +163,7 @@ /** * Does this option automatically enable another option, i.e. a dependency. - * @return the dependecy or null if non exists + * @return the dependency or null if none exists */ public String getDependency() { return this.dependency; @@ -304,8 +304,8 @@ } } - boolean matches(final String key0) { - return key0.equals(this.shortName) || key0.equals(this.name); + boolean nameMatches(final String aName) { + return aName.equals(this.shortName) || aName.equals(this.name); } private static final int LINE_BREAK = 64;
--- a/src/jdk/nashorn/internal/runtime/options/Options.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/options/Options.java Thu Sep 24 10:09:56 2015 -0700 @@ -519,9 +519,25 @@ } } - private static OptionTemplate getOptionTemplate(final String key) { + /** + * Retrieves an option template identified by key. + * @param shortKey the short (that is without the e.g. "nashorn.option." part) key + * @return the option template identified by the key + * @throws IllegalArgumentException if the key doesn't specify an existing template + */ + public OptionTemplate getOptionTemplateByKey(final String shortKey) { + final String fullKey = key(shortKey); + for(final OptionTemplate t: validOptions) { + if(t.getKey().equals(fullKey)) { + return t; + } + } + throw new IllegalArgumentException(shortKey); + } + + private static OptionTemplate getOptionTemplateByName(final String name) { for (final OptionTemplate t : Options.validOptions) { - if (t.matches(key)) { + if (t.nameMatches(name)) { return t; } } @@ -681,7 +697,7 @@ } final String token = st.nextToken(); - this.template = Options.getOptionTemplate(token); + this.template = getOptionTemplateByName(token); if (this.template == null) { throw new IllegalArgumentException(argument); }
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/Config.java Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Config.java Thu Sep 24 10:09:56 2015 -0700 @@ -65,7 +65,7 @@ final boolean DONT_OPTIMIZE = false; - final boolean USE_STRING_TEMPLATES = true; // use embeded string templates in Regex object as byte arrays instead of compiling them into int bytecode array + final boolean USE_STRING_TEMPLATES = true; // use embedded string templates in Regex object as byte arrays instead of compiling them into int bytecode array final boolean NON_UNICODE_SDW = true;
--- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Sep 24 10:09:56 2015 -0700 @@ -150,6 +150,7 @@ type.error.method.not.constructor=Java method {0} cannot be used as a constructor. type.error.env.not.object=$ENV must be an Object. type.error.unsupported.java.to.type=Unsupported Java.to target type {0}. +type.error.java.array.conversion.failed=Java.to conversion to array type {0} failed type.error.constructor.requires.new=Constructor {0} requires "new". type.error.new.on.nonpublic.javatype=new cannot be used with non-public java type {0}. @@ -163,6 +164,7 @@ range.error.invalid.date=Invalid Date range.error.too.many.errors=Script contains too many errors: {0} errors range.error.concat.string.too.big=Concatenated String is too big +range.error.exec.returned.non.zero=$EXEC returned non-zero exit code: {0} reference.error.not.defined="{0}" is not defined reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side of assignment @@ -173,7 +175,9 @@ syntax.error.unprotected.switch.declaration=Unsupported {0} declaration in unprotected switch statement io.error.cant.write=cannot write "{0}" + config.error.no.dest=no destination directory supplied +config.error.eagerCompilationConflictsWithOptimisticTypes={0}=false (eager compilation) is not compatible with {1}=true. uri.error.bad.uri=Bad URI "{0}" near offset {1} list.adapter.null.global=Attempted to create the adapter from outside a JavaScript execution context.
--- a/src/jdk/nashorn/internal/runtime/resources/parser.js Thu Sep 24 10:00:42 2015 -0700 +++ b/src/jdk/nashorn/internal/runtime/resources/parser.js Thu Sep 24 10:09:56 2015 -0700 @@ -55,7 +55,7 @@ // do not start with '/'. If regexp, then eval it to make RegExp object return value.startsWith('/')? eval(value) : value.substring(1); } else { - // anythin else is returned "as is"" + // anything else is returned "as is" return value; } });
--- a/test/script/basic/JDK-8043232.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8043232.js Thu Sep 24 10:09:56 2015 -0700 @@ -29,14 +29,14 @@ */ // call explicit constructor -print(new (java.awt["Color(int,int,int)"])(255,0,255)); +print(new (java.lang["String(char[],int,int)"])(['a','b', 'c', 'd'], 1, 3)); // print the constructor itself -print(java.awt["Color(int,int,int)"]); +print(java.lang["String(char[],int,int)"]); // store constructor to call later -var Color = java.awt["Color(int,int,int)"]; +var Color = java.lang["String(char[],int,int)"]; // call stored constructor -print(new Color(33, 233, 2)) +print(new Color(['r','r', 'e', 'd'], 1, 3)) // check if default constructor works var obj = new (java.lang["Object()"])();
--- a/test/script/basic/JDK-8043232.js.EXPECTED Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8043232.js.EXPECTED Thu Sep 24 10:09:56 2015 -0700 @@ -1,14 +1,28 @@ -java.awt.Color[r=255,g=0,b=255] -[jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] -java.awt.Color[r=33,g=233,b=2] +bcd +[jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] +red TypeError: No such Java class: java.lang.NonExistent TypeError: No such Java constructor: Object(String) TypeError: Java constructor signature invalid: Object()xxxxx TypeError: Java constructor signature invalid: Object( TypeError: Java constructor signature invalid: Object) -TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.lang.System.getProperty] cannot be used as a constructor. -TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] cannot be used as a constructor. -TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] requires "new". +TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod + String java.lang.System.getProperty(String,String) + String java.lang.System.getProperty(String) +] cannot be used as a constructor. +TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod + void java.io.PrintStream.println() + void java.io.PrintStream.println(boolean) + void java.io.PrintStream.println(char) + void java.io.PrintStream.println(char[]) + void java.io.PrintStream.println(double) + void java.io.PrintStream.println(float) + void java.io.PrintStream.println(int) + void java.io.PrintStream.println(long) + void java.io.PrintStream.println(Object) + void java.io.PrintStream.println(String) +] cannot be used as a constructor. +TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new". TypeError: No such Java constructor: Runnable() TypeError: No such Java constructor: Runnable(int) java.lang.InstantiationException: java.io.InputStream
--- a/test/script/basic/JDK-8044750.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8044750.js Thu Sep 24 10:09:56 2015 -0700 @@ -25,6 +25,8 @@ * JDK-8044750: megamorphic getter for scope objects does not call __noSuchProperty__ hook * * @test + * @fork + * @option -Dnashorn.unstable.relink.threshold=16 * @run */ @@ -40,7 +42,9 @@ } } -for (var i = 0; i < 20; i++) { +var LIMIT = 20; // should be more than megamorphic threshold set via @option + +for (var i = 0; i < LIMIT; i++) { var obj = {}; obj.foo = i; obj[i] = i; @@ -51,3 +55,30 @@ // callsite inside func should see __noSuchProperty__ // hook on global scope object. func({}); + +function checkFoo() { + with({}) { + try { + foo; + return true; + } catch (e) { + return false; + } + } +} + +var oldNoSuchProperty = this.__noSuchProperty__; +delete this.__noSuchProperty__; + +// keep deleting/restorting __noSuchProperty__ alternatively +// to make "foo" access in checkFoo function megamorphic! + +for (var i = 0; i < LIMIT; i++) { + // no __noSuchProperty__ and 'with' scope object has no 'foo' + delete __noSuchProperty__; + Assert.assertFalse(checkFoo(), "Expected false in iteration " + i); + + // __noSuchProperty__ is exists but 'with' scope object has no 'foo' + this.__noSuchProperty__ = oldNoSuchProperty; + Assert.assertTrue(checkFoo(), "Expected true in iteration " + i); +}
--- a/test/script/basic/JDK-8049086.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8049086.js Thu Sep 24 10:09:56 2015 -0700 @@ -58,7 +58,7 @@ // (a) Java methods (b) Java classes (as these respond to new) // (c) FunctionalInterface objects (d) JSObjects that are 'functions' -print("java.awt.Color is java function? " + Java.isJavaFunction(java.awt.Color)); +print("java.lang.String is java function? " + Java.isJavaFunction(java.lang.String)); print("java.lang.Runnable instance is java function? " + Java.isJavaFunction(new java.lang.Runnable(function() {}))); print("eval is java function? " + Java.isJavaFunction(eval));
--- a/test/script/basic/JDK-8049086.js.EXPECTED Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8049086.js.EXPECTED Thu Sep 24 10:09:56 2015 -0700 @@ -13,7 +13,7 @@ Object is script object? true {} is script object? true /foo/ is script object? true -java.awt.Color is java function? true +java.lang.String is java function? true java.lang.Runnable instance is java function? true eval is java function? false println is java function? true
--- a/test/script/basic/JDK-8049242.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8049242.js Thu Sep 24 10:09:56 2015 -0700 @@ -29,14 +29,14 @@ */ // call explicit constructor -print(new (Java.type("java.awt.Color")["(int,int,int)"])(255,0,255)); +print(new (Java.type("java.lang.String")["(char[],int,int)"])(['a', 'b', 'c'],0, 3)); // print the constructor itself -print(Java.type("java.awt.Color")["(int,int,int)"]); +print(Java.type("java.lang.String")["(char[],int,int)"]); // store constructor to call later -var Color = Java.type("java.awt.Color")["(int,int,int)"]; +var Color = Java.type("java.lang.String")["(char[],int,int)"]; // call stored constructor -print(new Color(33, 233, 2)) +print(new Color(['j', 'a', 'v', 'a'], 1, 3)) // check if default constructor works var obj = new (Java.type("java.lang.Object")["()"])();
--- a/test/script/basic/JDK-8049242.js.EXPECTED Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8049242.js.EXPECTED Thu Sep 24 10:09:56 2015 -0700 @@ -1,10 +1,10 @@ -java.awt.Color[r=255,g=0,b=255] -[jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] -java.awt.Color[r=33,g=233,b=2] +abc +[jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] +ava TypeError: null is not a function TypeError: null is not a function TypeError: null is not a function -TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] requires "new". +TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new". TypeError: null is not a function TypeError: null is not a function java.lang.InstantiationException: java.io.InputStream
--- a/test/script/basic/JDK-8053905.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8053905.js Thu Sep 24 10:09:56 2015 -0700 @@ -28,6 +28,7 @@ * @runif external.octane * @fork * @option -Dnashorn.compiler.splitter.threshold=1000 + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @option -scripting * @option --lazy-compilation=false */
--- a/test/script/basic/JDK-8058561.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8058561.js Thu Sep 24 10:09:56 2015 -0700 @@ -26,7 +26,9 @@ * * @test * @run + * @fork * @option --lazy-compilation=false + * @option -Dnashorn.options.allowEagerCompilationSilentOverride */ // Just attempting to compile this caused the NPE
--- a/test/script/basic/JDK-8078612_eager_1a.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8078612_eager_1a.js Thu Sep 24 10:09:56 2015 -0700 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8078612_eager_1b.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8078612_eager_1b.js Thu Sep 24 10:09:56 2015 -0700 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8078612_eager_2a.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8078612_eager_2a.js Thu Sep 24 10:09:56 2015 -0700 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8078612_eager_2b.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8078612_eager_2b.js Thu Sep 24 10:09:56 2015 -0700 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8079470.js.EXPECTED Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/JDK-8079470.js.EXPECTED Thu Sep 24 10:09:56 2015 -0700 @@ -1,2 +1,2 @@ -TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod File java.io.File.java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures. -TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures. +TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures. +TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134939.js Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 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-8134939: Improve toString method of Dynalink OverloadedDynamicMethod + * + * @test + * @run + */ + +var overloadedSetter = new (Java.type("jdk.nashorn.test.models.OverloadedSetter")); + +Assert.assertEquals(String(overloadedSetter.foo), + "[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" + + " String jdk.nashorn.test.models.OverloadedSetter.foo(String)\n" + + " void jdk.nashorn.test.models.OverloadedSetter.foo(int)\n" + + "]"); + +Assert.assertEquals(String(overloadedSetter.setColor), + "[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" + + " void jdk.nashorn.test.models.OverloadedSetter.setColor(int)\n" + + " void jdk.nashorn.test.models.OverloadedSetter.setColor(String)\n" + + "]");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8136544.js Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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-8136544: Call site switching to megamorphic causes incorrect property read + * + * @test + * @fork + * @option -Dnashorn.unstable.relink.threshold=8 + * @run + */ + +var ScriptContext = Java.type("javax.script.ScriptContext"); +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +var m = new ScriptEngineManager(); +var e = m.getEngineByName("nashorn"); + +var scope = e.getBindings(ScriptContext.ENGINE_SCOPE); +var MYVAR = "myvar"; + +function loopupVar() { + try { + e.eval(MYVAR); + return true; + } catch (e) { + return false; + } +} + +// make sure we exercise callsite beyond megamorphic threshold we set +// in this test via nashorn.unstable.relink.threshold property +// In each iteration, callsite is exercised twice (two evals) +// So, LIMIT should be more than 4 to exercise megamorphic callsites. + +var LIMIT = 5; // This LIMIT should be more than 4 + +for (var i = 0; i < LIMIT; i++) { + // remove the variable and lookup + delete scope[MYVAR]; + Assert.assertFalse(loopupVar(), "Expected true in iteration " + i); + + // set that variable and check again + scope[MYVAR] = "foo"; + Assert.assertTrue(loopupVar(), "Expected false in iteration " + i); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8136694.js Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, 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-8136694: Megemorphic scope access does not throw ReferenceError when property is missing + * + * @test + * @fork + * @option -Dnashorn.unstable.relink.threshold=16 + * @run + */ + +function checkFoo() { + try { + // The 'foo' access becomes megamorphic + foo; + return true; + } catch (e) { + return false; + } +} + + +// Similar check for 'with' blocks as well. +function checkFooInWith() { + with({}) { + try { + // The 'foo' access becomes megamorphic + foo; + return true; + } catch (e) { + return false; + } + } +} + +function loop(checker) { + // LIMIT has to be more than the megamorphic threashold + // set via @option in this test header! + var LIMIT = 20; + for (var i = 0; i < LIMIT; i++) { + // make sure global has no "foo" + delete foo; + Assert.assertFalse(checker(), "Expected false in interation " + i); + + // now add 'foo' in global + foo = 44; + Assert.assertTrue(checker(), "Expected true in interation " + i); + } +} + + +loop(checkFoo); +loop(checkFooInWith);
--- a/test/script/basic/javaarrayconversion.js Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/basic/javaarrayconversion.js Thu Sep 24 10:09:56 2015 -0700 @@ -128,24 +128,32 @@ // Converting to string, toString takes precedence over valueOf test({ valueOf: function() { return "42"; }, toString: function() { return "43"; } }, "java.lang.String", "43") +function assertCanConvert(sourceType, targetType) { + Java.to([new (Java.type(sourceType))()], targetType + "[]") + ++testCount; +} + function assertCantConvert(sourceType, targetType) { try { - Java.to([new Java.type(sourceType)()], targetType + "[]") + Java.to([new (Java.type(sourceType))()], targetType + "[]") throw "no TypeError encountered" } catch(e) { - if(!(e instanceof TypeError)) { + if(!(e instanceof TypeError) || + !e.message.startsWith("Java.to conversion to array type")) { throw e; } ++testCount; } } +// Arbitrary POJOs to JS Primitive type should work +assertCanConvert("java.util.BitSet", "int") +assertCanConvert("java.util.BitSet", "double") +assertCanConvert("java.util.BitSet", "long") +assertCanConvert("java.util.BitSet", "boolean") +assertCanConvert("java.util.BitSet", "java.lang.String") + // Arbitrary POJOs can't be converted to Java values -assertCantConvert("java.util.BitSet", "int") -assertCantConvert("java.util.BitSet", "double") -assertCantConvert("java.util.BitSet", "long") -assertCantConvert("java.util.BitSet", "boolean") -assertCantConvert("java.util.BitSet", "java.lang.String") assertCantConvert("java.util.BitSet", "java.lang.Double") assertCantConvert("java.util.BitSet", "java.lang.Long")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/trusted/JDK-8087292.js Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 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-8087292: nashorn should have a "fail-fast" option for scripting, analog to bash "set -e" + * + * @test + * @option -scripting + * @run + */ + +function tryExec() { + try { + `java` + } catch (e) { + print(e); + } + + // make sure we got non-zero ("failure") exit code! + if ($EXIT == 0) { + print("Error: expected $EXIT code to be non-zero"); + } +} + +// no exception now! +tryExec(); + +// turn on error with non-zero exit code +$EXEC.throwOnError = true; +tryExec(); + +// no exception after this +$EXEC.throwOnError = false; +tryExec();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/trusted/JDK-8087292.js.EXPECTED Thu Sep 24 10:09:56 2015 -0700 @@ -0,0 +1,1 @@ +RangeError: $EXEC returned non-zero exit code: 1
--- a/test/script/trusted/classfilter.js.EXPECTED Thu Sep 24 10:00:42 2015 -0700 +++ b/test/script/trusted/classfilter.js.EXPECTED Thu Sep 24 10:09:56 2015 -0700 @@ -4,7 +4,18 @@ typeof java.util.Map evalutes to function typeof java.util.HashMap evalutes to function var m = new java.util.HashMap(); m.put('foo', 42); m evalutes to {foo=42} -java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] +java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod + void java.io.PrintStream.println() + void java.io.PrintStream.println(boolean) + void java.io.PrintStream.println(char) + void java.io.PrintStream.println(char[]) + void java.io.PrintStream.println(double) + void java.io.PrintStream.println(float) + void java.io.PrintStream.println(int) + void java.io.PrintStream.println(long) + void java.io.PrintStream.println(Object) + void java.io.PrintStream.println(String) +] java.lang.System.exit evalutes to [jdk.internal.dynalink.beans.SimpleDynamicMethod void java.lang.System.exit(int)] new javax.script.SimpleBindings throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.SimpleBindings Java.type('javax.script.ScriptContext') throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.ScriptContext
--- a/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Thu Sep 24 10:00:42 2015 -0700 +++ b/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Thu Sep 24 10:09:56 2015 -0700 @@ -26,6 +26,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import javax.script.Bindings; @@ -820,4 +821,38 @@ public void recursiveEvalCallScriptContextTest() throws ScriptException { new RecursiveEval().program(); } + + private static final String VAR_NAME = "myvar"; + + private static boolean lookupVar(final ScriptEngine engine, final String varName) { + try { + engine.eval(varName); + return true; + } catch (final ScriptException se) { + return false; + } + } + + // @bug 8136544: Call site switching to megamorphic causes incorrect property read + @Test + public void megamorphicPropertyReadTest() throws ScriptException { + final NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); + final ScriptEngine engine = factory.getScriptEngine(); + final Bindings scope = engine.getBindings(ScriptContext.ENGINE_SCOPE); + boolean ret; + + // Why 16 is the upper limit of this loop? The default nashorn dynalink megamorphic threshold is 16. + // See jdk.nashorn.internal.runtime.linker.Bootstrap.NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD + // We do, 'eval' of the same in this loop twice. So, 16*2 = 32 times that callsite in the script + // is exercised - much beyond the default megamorphic threshold. + + for (int i = 0; i < 16; i++) { + scope.remove(VAR_NAME); + ret = lookupVar(engine, VAR_NAME); + assertFalse(ret, "Expected false in iteration " + i); + scope.put(VAR_NAME, "foo"); + ret = lookupVar(engine, VAR_NAME); + assertTrue(ret, "Expected true in iteration " + i); + } + } }