# HG changeset patch # User asaha # Date 1443114596 25200 # Node ID a82fc95737d102a991e8b08b325041a0d32dd76f # Parent 1eda0618f55d51d2cc5c6bed8214ff79ea543ada# Parent e4a553f79ebd7ac795b9cfef82cd4f54d8364949 Merge diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java --- 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) { diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java --- 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. - 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); diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java --- 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 */ diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java --- 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() + " "); + System.err.println("Usage: " + PrototypeGenerator.class.getName() + " "); System.exit(1); } diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java --- 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()) { diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java --- 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; diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java --- 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() + " "); + System.err.println("Usage: " + ScriptClassInstrumentor.class.getName() + " "); System.exit(1); } diff -r 1eda0618f55d -r a82fc95737d1 buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java --- 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 diff -r 1eda0618f55d -r a82fc95737d1 samples/exceptionswallow.js --- /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 -- + +/* + * 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 -- "); + 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])); diff -r 1eda0618f55d -r a82fc95737d1 samples/find_nonfinals2.js --- 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")) { diff -r 1eda0618f55d -r a82fc95737d1 samples/javafoovars.js --- 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")) { diff -r 1eda0618f55d -r a82fc95737d1 samples/resourcetrysuggester.js --- /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 -- + +/* + * 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 -- "); + 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])); diff -r 1eda0618f55d -r a82fc95737d1 samples/zipfs.js --- 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() diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/DynamicLinker.java --- 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))); * } * } diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java --- 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 diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/beans/FacetIntrospector.java --- 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); } diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java --- 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 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; } diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java --- 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 diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/beans/SingleDynamicMethod.java --- 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(); } /** diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/linker/GuardedInvocation.java --- 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 */ diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/internal/dynalink/linker/GuardedTypeConversion.java --- 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; diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/api/scripting/AbstractJSObject.java --- 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 diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/api/scripting/JSObject.java --- 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); diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/api/scripting/NashornException.java --- 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; + } + } } diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/api/scripting/ScriptUtils.java --- 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)); } /** diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/api/scripting/URLReader.java --- 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; diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/internal/codegen/ApplySpecialization.java --- 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) { diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/internal/codegen/AssignSymbols.java --- 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> thisProperties = new ArrayDeque<>(); private final Map 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 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; } diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/internal/codegen/AstSerializer.java --- 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(new LexicalContext()) { - @Override - public Node leaveBlock(final Block block) { - if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { - return block.setStatements(lc, Collections.emptyList()); - } - return super.leaveBlock(block); - } - }); - } -} diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/internal/codegen/CacheAst.java --- /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 { + private final Deque 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.emptyList())); + } + return functionNode; + } +} diff -r 1eda0618f55d -r a82fc95737d1 src/jdk/nashorn/internal/codegen/CodeGenerator.java --- 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 emittedMethods = new HashSet<>(); // Function Id -> ContinuationInfo. Used by compilation of rest-of function only. - private final Map fnIdToContinuationInfo = new HashMap<>(); + private ContinuationInfo continuationInfo; private final Deque