From f29d7f78eb8352878eb82c0ec3af77327f722c5b Mon Sep 17 00:00:00 2001 From: sundar Date: Tue, 12 Mar 2013 18:12:42 +0530 Subject: 8009757: Package access clean up and refactoring Reviewed-by: jlaskey, lagergren, attila --HG-- rename : test/src/jdk/nashorn/internal/test/models/ConstructorWithArgument.java => test/src/jdk/nashorn/test/models/ConstructorWithArgument.java rename : test/src/jdk/nashorn/internal/test/models/DessertTopping.java => test/src/jdk/nashorn/test/models/DessertTopping.java rename : test/src/jdk/nashorn/internal/test/models/DessertToppingFloorWaxDriver.java => test/src/jdk/nashorn/test/models/DessertToppingFloorWaxDriver.java rename : test/src/jdk/nashorn/internal/test/models/FinalClass.java => test/src/jdk/nashorn/test/models/FinalClass.java rename : test/src/jdk/nashorn/internal/test/models/FloorWax.java => test/src/jdk/nashorn/test/models/FloorWax.java rename : test/src/jdk/nashorn/internal/runtime/Nashorn401TestSubject.java => test/src/jdk/nashorn/test/models/Nashorn401TestSubject.java rename : test/src/jdk/nashorn/internal/test/models/NoAccessibleConstructorClass.java => test/src/jdk/nashorn/test/models/NoAccessibleConstructorClass.java rename : test/src/jdk/nashorn/internal/test/models/NonPublicClass.java => test/src/jdk/nashorn/test/models/NonPublicClass.java rename : test/src/jdk/nashorn/internal/test/models/OuterClass.java => test/src/jdk/nashorn/test/models/OuterClass.java rename : test/src/jdk/nashorn/internal/test/models/OverloadedSam.java => test/src/jdk/nashorn/test/models/OverloadedSam.java rename : test/src/jdk/nashorn/internal/test/models/OverrideObject.java => test/src/jdk/nashorn/test/models/OverrideObject.java rename : test/src/jdk/nashorn/internal/test/models/StringArgs.java => test/src/jdk/nashorn/test/models/StringArgs.java rename : test/src/jdk/nashorn/internal/test/models/Toothpaste.java => test/src/jdk/nashorn/test/models/Toothpaste.java --- docs/JavaScriptingProgrammersGuide.html | 8 +-- docs/source/javaarray.js | 3 +- make/build.xml | 4 +- make/java.security.override | 4 +- .../api/scripting/NashornScriptEngineFactory.java | 10 ++++ src/jdk/nashorn/api/scripting/ScriptUtils.java | 48 ++++++++++++++++ src/jdk/nashorn/internal/objects/Global.java | 24 +++++++- src/jdk/nashorn/internal/objects/NativeDebug.java | 2 +- src/jdk/nashorn/internal/objects/NativeJava.java | 17 ++++++ src/jdk/nashorn/internal/runtime/Context.java | 25 +++++++-- .../nashorn/internal/runtime/NashornLoader.java | 47 +++++++++++++++- src/jdk/nashorn/internal/runtime/ScriptLoader.java | 7 ++- .../nashorn/internal/runtime/StructureLoader.java | 3 +- .../nashorn/internal/runtime/linker/Bootstrap.java | 2 +- .../runtime/linker/JavaAdapterFactory.java | 28 +++++++++- .../runtime/linker/ReflectionCheckLinker.java | 60 ++++++++++++++++++++ .../internal/runtime/resources/mozilla_compat.js | 18 +++--- .../nashorn/internal/runtime/resources/parser.js | 10 ++-- test/script/basic/JDK-8008448.js | 4 +- test/script/basic/NASHORN-401.js | 2 +- test/script/basic/consstring.js | 2 +- test/script/basic/fileline.js | 4 +- test/script/basic/javainnerclasses.js | 18 +++--- test/script/basic/list.js | 2 +- test/script/basic/map.js | 2 +- test/script/basic/stdin.js | 6 +- test/script/sandbox/javaextend.js | 2 +- test/script/sandbox/javaextend.js.EXPECTED | 6 +- test/script/sandbox/reflection.js | 4 +- test/script/sandbox/reflection.js.EXPECTED | 1 - test/script/sandbox/unsafe.js | 4 +- test/script/sandbox/unsafe.js.EXPECTED | 4 -- test/script/trusted/urlreader.js | 6 +- .../nashorn/api/scripting/ScriptEngineTest.java | 52 ----------------- .../internal/runtime/Nashorn401TestSubject.java | 57 ------------------- .../internal/runtime/TrustedScriptEngineTest.java | 31 ++++++++++- .../test/models/ConstructorWithArgument.java | 40 ------------- .../internal/test/models/DessertTopping.java | 30 ---------- .../test/models/DessertToppingFloorWaxDriver.java | 36 ------------ .../nashorn/internal/test/models/FinalClass.java | 30 ---------- .../jdk/nashorn/internal/test/models/FloorWax.java | 30 ---------- .../test/models/NoAccessibleConstructorClass.java | 30 ---------- .../internal/test/models/NonPublicClass.java | 30 ---------- .../nashorn/internal/test/models/OuterClass.java | 65 ---------------------- .../internal/test/models/OverloadedSam.java | 31 ----------- .../internal/test/models/OverrideObject.java | 44 --------------- .../nashorn/internal/test/models/StringArgs.java | 39 ------------- .../nashorn/internal/test/models/Toothpaste.java | 34 ----------- .../test/models/ConstructorWithArgument.java | 40 +++++++++++++ .../jdk/nashorn/test/models/DessertTopping.java | 30 ++++++++++ .../test/models/DessertToppingFloorWaxDriver.java | 36 ++++++++++++ test/src/jdk/nashorn/test/models/FinalClass.java | 30 ++++++++++ test/src/jdk/nashorn/test/models/FloorWax.java | 30 ++++++++++ .../nashorn/test/models/Nashorn401TestSubject.java | 57 +++++++++++++++++++ .../test/models/NoAccessibleConstructorClass.java | 30 ++++++++++ .../jdk/nashorn/test/models/NonPublicClass.java | 30 ++++++++++ test/src/jdk/nashorn/test/models/OuterClass.java | 65 ++++++++++++++++++++++ .../src/jdk/nashorn/test/models/OverloadedSam.java | 31 +++++++++++ .../jdk/nashorn/test/models/OverrideObject.java | 44 +++++++++++++++ test/src/jdk/nashorn/test/models/SourceHelper.java | 55 ++++++++++++++++++ test/src/jdk/nashorn/test/models/StringArgs.java | 39 +++++++++++++ test/src/jdk/nashorn/test/models/Toothpaste.java | 34 +++++++++++ 62 files changed, 892 insertions(+), 625 deletions(-) create mode 100644 src/jdk/nashorn/api/scripting/ScriptUtils.java create mode 100644 src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java delete mode 100644 test/script/sandbox/reflection.js.EXPECTED delete mode 100644 test/script/sandbox/unsafe.js.EXPECTED delete mode 100644 test/src/jdk/nashorn/internal/runtime/Nashorn401TestSubject.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/ConstructorWithArgument.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/DessertTopping.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/DessertToppingFloorWaxDriver.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/FinalClass.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/FloorWax.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/NoAccessibleConstructorClass.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/NonPublicClass.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/OuterClass.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/OverloadedSam.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/OverrideObject.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/StringArgs.java delete mode 100644 test/src/jdk/nashorn/internal/test/models/Toothpaste.java create mode 100644 test/src/jdk/nashorn/test/models/ConstructorWithArgument.java create mode 100644 test/src/jdk/nashorn/test/models/DessertTopping.java create mode 100644 test/src/jdk/nashorn/test/models/DessertToppingFloorWaxDriver.java create mode 100644 test/src/jdk/nashorn/test/models/FinalClass.java create mode 100644 test/src/jdk/nashorn/test/models/FloorWax.java create mode 100644 test/src/jdk/nashorn/test/models/Nashorn401TestSubject.java create mode 100644 test/src/jdk/nashorn/test/models/NoAccessibleConstructorClass.java create mode 100644 test/src/jdk/nashorn/test/models/NonPublicClass.java create mode 100644 test/src/jdk/nashorn/test/models/OuterClass.java create mode 100644 test/src/jdk/nashorn/test/models/OverloadedSam.java create mode 100644 test/src/jdk/nashorn/test/models/OverrideObject.java create mode 100644 test/src/jdk/nashorn/test/models/SourceHelper.java create mode 100644 test/src/jdk/nashorn/test/models/StringArgs.java create mode 100644 test/src/jdk/nashorn/test/models/Toothpaste.java diff --git a/docs/JavaScriptingProgrammersGuide.html b/docs/JavaScriptingProgrammersGuide.html index cf248140..dd243d3a 100644 --- a/docs/JavaScriptingProgrammersGuide.html +++ b/docs/JavaScriptingProgrammersGuide.html @@ -533,9 +533,8 @@ with (SwingGui) {

Creating, Converting and Using Java Arrays

-

While creating a Java object is the same as in Java, to create -Java arrays in JavaScript we can use Java reflection -explicitly. But once created the element access or length access is +

+Array element access or length access is the same as in Java. Also, a script array can be used when a Java method expects a Java array (auto conversion). So in most cases we don't have to create Java arrays explicitly.

@@ -543,7 +542,8 @@ don't have to create Java arrays explicitly.

// javaarray.js // create Java String array of 5 elements -var a = java.lang.reflect.Array.newInstance(java.lang.String.class, 5); +var StringArray = Java.type("java.lang.String[]"); +var a = new StringArray(5); // Accessing elements and length access is by usual Java syntax a[0] = "scripting is great!"; diff --git a/docs/source/javaarray.js b/docs/source/javaarray.js index b9d93f0d..a02aa3ca 100644 --- a/docs/source/javaarray.js +++ b/docs/source/javaarray.js @@ -30,7 +30,8 @@ */ // create Java String array of 5 elements -var a = java.lang.reflect.Array.newInstance(java.lang.String.class, 5); +var StringArray = Java.type("java.lang.String[]"); +var a = new StringArray(5); // Accessing elements and length access is by usual Java syntax a[0] = "scripting is great!"; diff --git a/make/build.xml b/make/build.xml index 34f56e46..e2d8f0cc 100644 --- a/make/build.xml +++ b/make/build.xml @@ -191,12 +191,12 @@ - + - + diff --git a/make/java.security.override b/make/java.security.override index 00219852..a7edf33b 100644 --- a/make/java.security.override +++ b/make/java.security.override @@ -3,7 +3,7 @@ # We ensure that by overriding "package.access" security property. # The following "package.access" value was copied from default java.security -# of jre/lib/security and appended with nashorn IR, Codegen and Parser packages. +# of jre/lib/security and appended with nashorn sensitive packages. # # List of comma-separated packages that start with or equal this string @@ -11,4 +11,4 @@ # passed to checkPackageAccess unless the # corresponding RuntimePermission ("accessClassInPackage."+package) has # been granted. -package.access=sun.,com.sun.xml.internal.ws.,com.sun.xml.internal.bind.,com.sun.imageio.,com.sun.org.apache.xerces.internal.utils.,com.sun.org.apache.xalan.internal.utils.,com.sun.org.glassfish.external.,com.sun.org.glassfish.gmbal.,jdk.internal.,jdk.nashorn.internal.ir., jdk.nashorn.internal.codegen., jdk.nashorn.internal.lookup., jdk.nashorn.internal.parser. +package.access=sun.,com.sun.xml.internal.ws.,com.sun.xml.internal.bind.,com.sun.imageio.,com.sun.org.apache.xerces.internal.utils.,com.sun.org.apache.xalan.internal.utils.,com.sun.org.glassfish.external.,com.sun.org.glassfish.gmbal.,jdk.internal.,jdk.nashorn.internal.,jdk.nashorn.tools. diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java index 47a0c595..4672f6a0 100644 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java @@ -147,6 +147,7 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory { * @return newly created script engine. */ public ScriptEngine getScriptEngine(final ClassLoader appLoader) { + checkConfigPermission(); return new NashornScriptEngine(this, appLoader); } @@ -157,6 +158,7 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory { * @return newly created script engine. */ public ScriptEngine getScriptEngine(final String[] args) { + checkConfigPermission(); return new NashornScriptEngine(this, args, getAppClassLoader()); } @@ -168,11 +170,19 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory { * @return newly created script engine. */ public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader) { + checkConfigPermission(); return new NashornScriptEngine(this, args, appLoader); } // -- Internals only below this point + private void checkConfigPermission() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("nashorn.setConfig")); + } + } + private static final List names; private static final List mimeTypes; private static final List extensions; diff --git a/src/jdk/nashorn/api/scripting/ScriptUtils.java b/src/jdk/nashorn/api/scripting/ScriptUtils.java new file mode 100644 index 00000000..63358bd7 --- /dev/null +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java @@ -0,0 +1,48 @@ +/* + * 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.api.scripting; + +import jdk.nashorn.internal.runtime.ScriptRuntime; + +/** + * Utilities that are to be called from script code + */ +public final class ScriptUtils { + private ScriptUtils() {} + + /** + * Returns AST as JSON compatible string. This is used to + * implement "parse" function in resources/parse.js script. + * + * @param code code to be parsed + * @param name name of the code source (used for location) + * @param includeLoc tells whether to include location information for nodes or not + * @return JSON string representation of AST of the supplied code + */ + public static String parse(final String code, final String name, final boolean includeLoc) { + return ScriptRuntime.parse(code, name, includeLoc); + } +} diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java index b89d2073..a9835f6f 100644 --- a/src/jdk/nashorn/internal/objects/Global.java +++ b/src/jdk/nashorn/internal/objects/Global.java @@ -34,6 +34,9 @@ import java.io.PrintWriter; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.ref.SoftReference; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -1503,8 +1506,10 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value); // Nashorn extension: global.$OPTIONS (scripting-mode-only) - value = context.getEnv(); - addOwnProperty("$OPTIONS", Attribute.NOT_ENUMERABLE, value); + final ScriptObject options = newEmptyInstance(); + final ScriptEnvironment scriptEnv = context.getEnv(); + copyOptions(options, scriptEnv); + addOwnProperty("$OPTIONS", Attribute.NOT_ENUMERABLE, options); // Nashorn extension: global.$ENV (scripting-mode-only) if (System.getSecurityManager() == null) { @@ -1523,6 +1528,21 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { addOwnProperty(ScriptingFunctions.EXIT_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED); } + private void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + for (Field f : scriptEnv.getClass().getFields()) { + try { + options.set(f.getName(), f.get(scriptEnv), false); + } catch (final IllegalArgumentException | IllegalAccessException exp) { + throw new RuntimeException(exp); + } + } + return null; + } + }); + } + private void initTypedArray() { this.builtinArrayBuffer = initConstructor("ArrayBuffer"); this.builtinInt8Array = initConstructor("Int8Array"); diff --git a/src/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk/nashorn/internal/objects/NativeDebug.java index 3d6fcbc8..8188f426 100644 --- a/src/jdk/nashorn/internal/objects/NativeDebug.java +++ b/src/jdk/nashorn/internal/objects/NativeDebug.java @@ -66,7 +66,7 @@ public final class NativeDebug extends ScriptObject { public static Object getContext(final Object self) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPermission(new RuntimePermission("getNashornContext")); + sm.checkPermission(new RuntimePermission("nashorn.getContext")); } return Global.getThisContext(); } diff --git a/src/jdk/nashorn/internal/objects/NativeJava.java b/src/jdk/nashorn/internal/objects/NativeJava.java index fc16962e..2519c028 100644 --- a/src/jdk/nashorn/internal/objects/NativeJava.java +++ b/src/jdk/nashorn/internal/objects/NativeJava.java @@ -222,6 +222,23 @@ public final class NativeJava { return simpleType(typeName); } + /** + * Returns name of a java type {@link StaticClass}. + * @param self not used + * @param type the type whose name is returned + * @return name of the given type + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) + public static Object typeName(final Object self, final Object type) { + if (type instanceof StaticClass) { + return ((StaticClass)type).getRepresentedClass().getName(); + } else if (type instanceof Class) { + return ((Class)type).getName(); + } else { + return UNDEFINED; + } + } + /** * Given a JavaScript array and a Java type, returns a Java array with the same initial contents, and with the * specified component type. Example: diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java index 1c4a08c8..516c0a79 100644 --- a/src/jdk/nashorn/internal/runtime/Context.java +++ b/src/jdk/nashorn/internal/runtime/Context.java @@ -39,10 +39,13 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; +import java.security.AccessControlContext; import java.security.AccessController; import java.security.CodeSigner; import java.security.CodeSource; +import java.security.Permissions; import java.security.PrivilegedAction; +import java.security.ProtectionDomain; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; import jdk.nashorn.internal.codegen.Compiler; @@ -123,7 +126,7 @@ public final class Context { if (callerLoader != myLoader && !(callerLoader instanceof StructureLoader) && !(JavaAdapterFactory.isAdapterClass(caller))) { - sm.checkPermission(new RuntimePermission("getNashornGlobal")); + sm.checkPermission(new RuntimePermission("nashorn.getGlobal")); } } @@ -137,7 +140,7 @@ public final class Context { public static void setGlobal(final ScriptObject global) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPermission(new RuntimePermission("setNashornGlobal")); + sm.checkPermission(new RuntimePermission("nashorn.setGlobal")); } if (global != null && !(global instanceof GlobalObject)) { @@ -154,7 +157,7 @@ public final class Context { public static Context getContext() { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPermission(new RuntimePermission("getNashornContext")); + sm.checkPermission(new RuntimePermission("nashorn.getContext")); } return getContextTrusted(); } @@ -267,7 +270,7 @@ public final class Context { public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPermission(new RuntimePermission("createNashornContext")); + sm.checkPermission(new RuntimePermission("nashorn.createContext")); } this.env = new ScriptEnvironment(options, out, err); @@ -533,7 +536,13 @@ public final class Context { if (index != -1) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPackageAccess(fullName.substring(0, index)); + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + sm.checkPackageAccess(fullName.substring(0, index)); + return null; + } + }, createNoPermissionsContext()); } } @@ -599,7 +608,7 @@ public final class Context { public ScriptObject newGlobal() { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPermission(new RuntimePermission("createNashornGlobal")); + sm.checkPermission(new RuntimePermission("nashorn.newGlobal")); } return newGlobalTrusted(); @@ -676,6 +685,10 @@ public final class Context { return (context != null) ? context : Context.getContextTrusted(); } + private static AccessControlContext createNoPermissionsContext() { + return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); + } + private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { ScriptFunction script = null; diff --git a/src/jdk/nashorn/internal/runtime/NashornLoader.java b/src/jdk/nashorn/internal/runtime/NashornLoader.java index 80a0b8e6..5ce31008 100644 --- a/src/jdk/nashorn/internal/runtime/NashornLoader.java +++ b/src/jdk/nashorn/internal/runtime/NashornLoader.java @@ -30,6 +30,10 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; import java.security.SecureClassLoader; import jdk.nashorn.tools.Shell; @@ -40,6 +44,28 @@ import jdk.nashorn.tools.Shell; * */ abstract class NashornLoader extends SecureClassLoader { + private static final String OBJECTS_PKG = "jdk.nashorn.internal.objects"; + private static final String RUNTIME_PKG = "jdk.nashorn.internal.runtime"; + private static final String RUNTIME_LINKER_PKG = "jdk.nashorn.internal.runtime.linker"; + private static final String SCRIPTS_PKG = "jdk.nashorn.internal.scripts"; + + private static final Permission[] SCRIPT_PERMISSIONS; + static { + SCRIPT_PERMISSIONS = new Permission[4]; + + /* + * Generated classes get access to runtime, runtime.linker, objects, scripts packages. + * Note that the actual scripts can not access these because Java.type, Packages + * prevent these restricted packages. And Java reflection and JSR292 access is prevented + * for scripts. In other words, nashorn generated portions of script classes can access + * clases in these implementation packages. + */ + SCRIPT_PERMISSIONS[0] = new RuntimePermission("accessClassInPackage." + RUNTIME_PKG); + SCRIPT_PERMISSIONS[1] = new RuntimePermission("accessClassInPackage." + RUNTIME_LINKER_PKG); + SCRIPT_PERMISSIONS[2] = new RuntimePermission("accessClassInPackage." + OBJECTS_PKG); + SCRIPT_PERMISSIONS[3] = new RuntimePermission("accessClassInPackage." + SCRIPTS_PKG); + } + private final Context context; final Context getContext() { @@ -68,11 +94,30 @@ abstract class NashornLoader extends SecureClassLoader { if (i != -1) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPackageAccess(name.substring(0, i)); + final String pkgName = name.substring(0, i); + switch (pkgName) { + case RUNTIME_PKG: + case RUNTIME_LINKER_PKG: + case OBJECTS_PKG: + case SCRIPTS_PKG: + // allow it. + break; + default: + sm.checkPackageAccess(pkgName); + } } } } + @Override + protected PermissionCollection getPermissions(CodeSource codesource) { + final Permissions permCollection = new Permissions(); + for (final Permission perm : SCRIPT_PERMISSIONS) { + permCollection.add(perm); + } + return permCollection; + } + /** * Create a secure URL class loader for the given classpath * @param classPath classpath for the loader to search from diff --git a/src/jdk/nashorn/internal/runtime/ScriptLoader.java b/src/jdk/nashorn/internal/runtime/ScriptLoader.java index d6321662..adecd8a3 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptLoader.java +++ b/src/jdk/nashorn/internal/runtime/ScriptLoader.java @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import java.security.CodeSource; +import java.security.ProtectionDomain; /** * Responsible for loading script generated classes. @@ -57,6 +58,10 @@ final class ScriptLoader extends NashornLoader { * @return Installed class. */ synchronized Class installClass(final String name, final byte[] data, final CodeSource cs) { - return defineClass(name, data, 0, data.length, cs); + if (cs == null) { + return defineClass(name, data, 0, data.length, new ProtectionDomain(null, getPermissions(null))); + } else { + return defineClass(name, data, 0, data.length, cs); + } } } diff --git a/src/jdk/nashorn/internal/runtime/StructureLoader.java b/src/jdk/nashorn/internal/runtime/StructureLoader.java index da64655f..35786b0b 100644 --- a/src/jdk/nashorn/internal/runtime/StructureLoader.java +++ b/src/jdk/nashorn/internal/runtime/StructureLoader.java @@ -38,6 +38,7 @@ import java.security.CodeSigner; import java.security.CodeSource; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; import jdk.nashorn.internal.codegen.ObjectClassGenerator; /** @@ -129,6 +130,6 @@ final class StructureLoader extends NashornLoader { } final byte[] code = new ObjectClassGenerator(context).generate(descriptor); - return defineClass(name, code, 0, code.length); + return defineClass(name, code, 0, code.length, new ProtectionDomain(null, getPermissions(null))); } } diff --git a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java index 3c5e2d81..6b55656b 100644 --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java @@ -57,7 +57,7 @@ public final class Bootstrap { static { final DynamicLinkerFactory factory = new DynamicLinkerFactory(); factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(), - new JSObjectLinker()); + new JSObjectLinker(), new ReflectionCheckLinker()); factory.setFallbackLinkers(new BeansLinker(), new NashornBottomLinker()); factory.setSyncOnRelink(true); final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1); diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java index a9e4bd01..ba599da1 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java @@ -54,6 +54,7 @@ import java.security.CodeSigner; import java.security.CodeSource; import java.security.Permissions; import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; import java.security.SecureClassLoader; import java.security.SecureRandom; @@ -410,9 +411,13 @@ public final class JavaAdapterFactory { */ public static MethodHandle getConstructor(final Class sourceType, final Class targetType) throws Exception { final StaticClass adapterClass = getAdapterClassFor(new Class[] { targetType }); - return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(NashornCallSiteDescriptor.get( - "dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false, - adapterClass, null)).getInvocation(), adapterClass); + return AccessController.doPrivileged(new PrivilegedExceptionAction() { + public MethodHandle run() throws Exception { + return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(NashornCallSiteDescriptor.get( + "dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false, + adapterClass, null)).getInvocation(), adapterClass); + } + }); } /** @@ -456,8 +461,25 @@ public final class JavaAdapterFactory { private static ClassLoader createClassLoader(final ClassLoader parentLoader, final String className, final byte[] classBytes, final String privilegedActionClassName) { return new AdapterLoader(parentLoader) { + private final ClassLoader myLoader = getClass().getClassLoader(); private final ProtectionDomain myProtectionDomain = getClass().getProtectionDomain(); + @Override + public Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { + try { + return super.loadClass(name, resolve); + } catch (final SecurityException se) { + // we may be implementing an interface or extending a class that was + // loaded by a loader that prevents package.access. If so, it'd throw + // SecurityException for nashorn's classes!. For adapter's to work, we + // should be able to refer to nashorn classes. + if (name.startsWith("jdk.nashorn.internal.")) { + return myLoader.loadClass(name); + } + throw se; + } + } + @Override protected Class findClass(final String name) throws ClassNotFoundException { if(name.equals(className)) { diff --git a/src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java b/src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java new file mode 100644 index 00000000..eb8837a8 --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java @@ -0,0 +1,60 @@ +/* + * 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.linker; + +import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.LinkRequest; +import jdk.internal.dynalink.linker.LinkerServices; +import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; + +/** + * Check java reflection permission for java reflective and java.lang.invoke access from scripts + */ +final class ReflectionCheckLinker implements TypeBasedGuardingDynamicLinker{ + @Override + public boolean canLinkType(final Class type) { + return canLinkTypeStatic(type); + } + + private static boolean canLinkTypeStatic(final Class type) { + if (type == Class.class || ClassLoader.class.isAssignableFrom(type)) { + return true; + } + final String name = type.getName(); + return name.startsWith("java.lang.reflect.") || name.startsWith("java.lang.invoke."); + } + + @Override + public GuardedInvocation getGuardedInvocation(final LinkRequest origRequest, final LinkerServices linkerServices) + throws Exception { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("nashorn.JavaReflection")); + } + // let the next linker deal with actual linking + return null; + } +} diff --git a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js index 15c67f94..0b967d69 100644 --- a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js +++ b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js @@ -1,21 +1,21 @@ /* * 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. - * + * * 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. @@ -34,7 +34,7 @@ Object.defineProperty(this, "JavaAdapter", { if (arguments.length < 2) { throw new TypeError("JavaAdapter requires atleast two arguments"); } - + var types = Array.prototype.slice.call(arguments, 0, arguments.length - 1); var NewType = Java.extend.apply(Java, types); return new NewType(arguments[arguments.length - 1]); @@ -56,10 +56,10 @@ Object.defineProperty(this, "importPackage", { return type; } catch (e) {} } - + return oldNoSuchProperty? oldNoSuchProperty(name) : undefined; } - + var prefix = "[JavaPackage "; return function() { for (var i in arguments) { @@ -343,7 +343,9 @@ Object.defineProperty(this, "importClass", { configurable: true, enumerable: false, writable: true, value: function(clazz) { if (Java.isType(clazz)) { - this[clazz.class.getSimpleName()] = clazz; + var className = Java.typeName(clazz); + var simpleName = className.substring(className.lastIndexOf('.') + 1); + this[simpleName] = clazz; } else { throw new TypeError(clazz + " is not a Java class"); } diff --git a/src/jdk/nashorn/internal/runtime/resources/parser.js b/src/jdk/nashorn/internal/runtime/resources/parser.js index b89f7e12..8671d365 100644 --- a/src/jdk/nashorn/internal/runtime/resources/parser.js +++ b/src/jdk/nashorn/internal/runtime/resources/parser.js @@ -1,21 +1,21 @@ /* * 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. - * + * * 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. @@ -47,7 +47,7 @@ function parse(/*code, [name], [location]*/) { code = arguments[0]; } - var jsonStr = Packages.jdk.nashorn.internal.runtime.ScriptRuntime.parse(code, name, location); + var jsonStr = Packages.jdk.nashorn.api.scripting.ScriptUtils.parse(code, name, location); return JSON.parse(jsonStr, function (prop, value) { if (typeof(value) == 'string' && prop == "value") { diff --git a/test/script/basic/JDK-8008448.js b/test/script/basic/JDK-8008448.js index d5ffbd4c..b30e3417 100644 --- a/test/script/basic/JDK-8008448.js +++ b/test/script/basic/JDK-8008448.js @@ -32,7 +32,7 @@ var File = Java.type("java.io.File"); var FilenameFilter = Java.type("java.io.FilenameFilter"); -var Source = Java.type("jdk.nashorn.internal.runtime.Source") +var SourceHelper = Java.type("jdk.nashorn.test.models.SourceHelper") // Filter out non .js files var files = new File(__DIR__).listFiles(new FilenameFilter() { @@ -44,5 +44,5 @@ load("nashorn:parser.js"); // parse each file to make sure it does not result in exception for each (var f in files) { - parse(new Source(f.toString(), f).getString()); + parse(SourceHelper.readFully(f)); } diff --git a/test/script/basic/NASHORN-401.js b/test/script/basic/NASHORN-401.js index b6058a9b..6a663e1a 100644 --- a/test/script/basic/NASHORN-401.js +++ b/test/script/basic/NASHORN-401.js @@ -28,7 +28,7 @@ * @run */ -var t = new Packages.jdk.nashorn.internal.runtime.Nashorn401TestSubject(); +var t = new Packages.jdk.nashorn.test.models.Nashorn401TestSubject(); print(t.method2(10)); print(t.method2(10.2)); diff --git a/test/script/basic/consstring.js b/test/script/basic/consstring.js index b8dc51d8..0cc8b1f8 100644 --- a/test/script/basic/consstring.js +++ b/test/script/basic/consstring.js @@ -37,4 +37,4 @@ list.add(String(new String(str + "2"))); // String() called as function with list.add((str + "3").toString()); // toString() called on primitive string list.add(new String(str + "4").toString()); // toString() called on String object -Packages.jdk.nashorn.internal.test.models.StringArgs.checkString(list); +Packages.jdk.nashorn.test.models.StringArgs.checkString(list); diff --git a/test/script/basic/fileline.js b/test/script/basic/fileline.js index 3323a126..ccf879b2 100644 --- a/test/script/basic/fileline.js +++ b/test/script/basic/fileline.js @@ -41,8 +41,8 @@ print(file + " : " + __LINE__); load(__DIR__ + "loadedfile.js"); // Add check for base part of a URL. We can't test __DIR__ inside -// a script that is downloaded from a URL. check for Source.baseURL +// a script that is downloaded from a URL. check for SourceHelper.baseURL // which is exposed as __DIR__ for URL case. var url = new java.net.URL("http://www.acme.com:8080/foo/bar.js"); -print(Packages.jdk.nashorn.internal.runtime.Source.baseURL(url)); +print(Packages.jdk.nashorn.test.models.SourceHelper.baseURL(url)); diff --git a/test/script/basic/javainnerclasses.js b/test/script/basic/javainnerclasses.js index df1e74df..c84571d7 100644 --- a/test/script/basic/javainnerclasses.js +++ b/test/script/basic/javainnerclasses.js @@ -29,25 +29,25 @@ */ // Do it with Java.type() -var outer = new (Java.type("jdk.nashorn.internal.test.models.OuterClass"))("apple") +var outer = new (Java.type("jdk.nashorn.test.models.OuterClass"))("apple") print(outer) -var innerStatic = new (Java.type("jdk.nashorn.internal.test.models.OuterClass$InnerStaticClass"))("orange") +var innerStatic = new (Java.type("jdk.nashorn.test.models.OuterClass$InnerStaticClass"))("orange") print(innerStatic) -var innerNonStatic = new (Java.type("jdk.nashorn.internal.test.models.OuterClass$InnerNonStaticClass"))(outer, "pear") +var innerNonStatic = new (Java.type("jdk.nashorn.test.models.OuterClass$InnerNonStaticClass"))(outer, "pear") print(innerNonStatic) // Now do it with Packages and explicit $ names -var outer = new Packages.jdk.nashorn.internal.test.models.OuterClass("red") +var outer = new Packages.jdk.nashorn.test.models.OuterClass("red") print(outer) -var innerStatic = new Packages.jdk.nashorn.internal.test.models.OuterClass$InnerStaticClass("green") +var innerStatic = new Packages.jdk.nashorn.test.models.OuterClass$InnerStaticClass("green") print(innerStatic) -var innerNonStatic = new Packages.jdk.nashorn.internal.test.models.OuterClass$InnerNonStaticClass(outer, "blue") +var innerNonStatic = new Packages.jdk.nashorn.test.models.OuterClass$InnerNonStaticClass(outer, "blue") print(innerNonStatic) // Now do it with Packages and nested properties -var outer = new Packages.jdk.nashorn.internal.test.models.OuterClass("sweet") +var outer = new Packages.jdk.nashorn.test.models.OuterClass("sweet") print(outer) -var innerStatic = new Packages.jdk.nashorn.internal.test.models.OuterClass.InnerStaticClass("sour") +var innerStatic = new Packages.jdk.nashorn.test.models.OuterClass.InnerStaticClass("sour") print(innerStatic) -var innerNonStatic = new Packages.jdk.nashorn.internal.test.models.OuterClass.InnerNonStaticClass(outer, "bitter") +var innerNonStatic = new Packages.jdk.nashorn.test.models.OuterClass.InnerNonStaticClass(outer, "bitter") print(innerNonStatic) diff --git a/test/script/basic/list.js b/test/script/basic/list.js index 12e4071b..72ae0be7 100644 --- a/test/script/basic/list.js +++ b/test/script/basic/list.js @@ -28,7 +28,7 @@ * @run */ var l = new java.util.ArrayList(); -print("l.class.name=" + l.class.name) // Has "class" property like any POJO +print("l.class.name=" + Java.typeName(l.class)) // Has "class" property like any POJO l.add("foo") l.add("bar") diff --git a/test/script/basic/map.js b/test/script/basic/map.js index 2ab27aba..c024f6eb 100644 --- a/test/script/basic/map.js +++ b/test/script/basic/map.js @@ -28,7 +28,7 @@ * @run */ var m = new (Java.type("java.util.LinkedHashMap")); -print("m.class.name=" + m.class.name) // Has "class" property like any POJO +print("m.class.name=" + Java.typeName(m.class)) // Has "class" property like any POJO var empty_key = "empty" diff --git a/test/script/basic/stdin.js b/test/script/basic/stdin.js index ba546d59..a3a93324 100644 --- a/test/script/basic/stdin.js +++ b/test/script/basic/stdin.js @@ -28,8 +28,8 @@ * @run */ -print(java.lang.System.in.class.name); +print(Java.typeName(java.lang.System.in.class)); var prop = "in"; -print(java.lang.System[prop].class.name); -print(java.lang.System["in"].class.name); +print(Java.typeName(java.lang.System[prop].class)); +print(Java.typeName(java.lang.System["in"].class)); diff --git a/test/script/sandbox/javaextend.js b/test/script/sandbox/javaextend.js index 318a6794..33cc6b01 100644 --- a/test/script/sandbox/javaextend.js +++ b/test/script/sandbox/javaextend.js @@ -27,7 +27,7 @@ */ function model(n) { - return Java.type("jdk.nashorn.internal.test.models." + n) + return Java.type("jdk.nashorn.test.models." + n) } // Can't extend a final class diff --git a/test/script/sandbox/javaextend.js.EXPECTED b/test/script/sandbox/javaextend.js.EXPECTED index 2a5ad63a..69c78189 100644 --- a/test/script/sandbox/javaextend.js.EXPECTED +++ b/test/script/sandbox/javaextend.js.EXPECTED @@ -1,6 +1,6 @@ -TypeError: Can not extend final class jdk.nashorn.internal.test.models.FinalClass. -TypeError: Can not extend class jdk.nashorn.internal.test.models.NoAccessibleConstructorClass as it has no public or protected constructors. -TypeError: Can not extend/implement non-public class/interface jdk.nashorn.internal.test.models.NonPublicClass. +TypeError: Can not extend final class jdk.nashorn.test.models.FinalClass. +TypeError: Can not extend class jdk.nashorn.test.models.NoAccessibleConstructorClass as it has no public or protected constructors. +TypeError: Can not extend/implement non-public class/interface jdk.nashorn.test.models.NonPublicClass. TypeError: Can not extend multiple classes java.lang.Number and java.lang.Thread. At most one of the specified types can be a class, the rest must all be interfaces. abcdabcd run-object diff --git a/test/script/sandbox/reflection.js b/test/script/sandbox/reflection.js index 7364879d..e892c646 100644 --- a/test/script/sandbox/reflection.js +++ b/test/script/sandbox/reflection.js @@ -30,9 +30,7 @@ */ function check(e) { - if (e instanceof java.lang.SecurityException) { - print(e); - } else { + if (! (e instanceof java.lang.SecurityException)) { fail("expected SecurityException, got " + e); } } diff --git a/test/script/sandbox/reflection.js.EXPECTED b/test/script/sandbox/reflection.js.EXPECTED deleted file mode 100644 index 202333ea..00000000 --- a/test/script/sandbox/reflection.js.EXPECTED +++ /dev/null @@ -1 +0,0 @@ -java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers") diff --git a/test/script/sandbox/unsafe.js b/test/script/sandbox/unsafe.js index b273cd0b..6b3f43af 100644 --- a/test/script/sandbox/unsafe.js +++ b/test/script/sandbox/unsafe.js @@ -30,9 +30,7 @@ */ function check(e) { - if (e instanceof java.lang.SecurityException) { - print(e); - } else { + if (! (e instanceof java.lang.SecurityException)) { fail("expected SecurityException, got " + e); } } diff --git a/test/script/sandbox/unsafe.js.EXPECTED b/test/script/sandbox/unsafe.js.EXPECTED deleted file mode 100644 index 3f1cbec0..00000000 --- a/test/script/sandbox/unsafe.js.EXPECTED +++ /dev/null @@ -1,4 +0,0 @@ -java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessClassInPackage.sun.misc") -java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessClassInPackage.sun.misc") -java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessClassInPackage.sun") -java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getClassLoader") diff --git a/test/script/trusted/urlreader.js b/test/script/trusted/urlreader.js index a5e06ee5..ca037306 100644 --- a/test/script/trusted/urlreader.js +++ b/test/script/trusted/urlreader.js @@ -9,7 +9,7 @@ var URLReader = Java.type("jdk.nashorn.api.scripting.URLReader"); var URL = Java.type("java.net.URL"); var File = Java.type("java.io.File"); var JString = Java.type("java.lang.String"); -var Source = Java.type("jdk.nashorn.internal.runtime.Source"); +var SourceHelper = Java.type("jdk.nashorn.test.models.SourceHelper"); var url = new File(__FILE__).toURI().toURL(); var reader = new URLReader(url); @@ -19,9 +19,9 @@ var reader = new URLReader(url); // check URL read // read URL content by directly reading from URL -var str = new Source(url.toString(), url).getString(); +var str = SourceHelper.readFully(url); // read URL content via URLReader -var content = new JString(Source.readFully(reader)); +var content = new JString(SourceHelper.readFully(reader)); // assert that the content is same Assert.assertEquals(str, content); diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java index f4fd114f..0994b0d2 100644 --- a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java +++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java @@ -47,7 +47,6 @@ import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleScriptContext; -import jdk.nashorn.internal.runtime.Version; import netscape.javascript.JSObject; import org.testng.Assert; import org.testng.annotations.Test; @@ -129,7 +128,6 @@ public class ScriptEngineTest { assertEquals(fac.getParameter(ScriptEngine.NAME), "javascript"); assertEquals(fac.getLanguageVersion(), "ECMA - 262 Edition 5.1"); assertEquals(fac.getEngineName(), "Oracle Nashorn"); - assertEquals(fac.getEngineVersion(), Version.version()); assertEquals(fac.getOutputStatement("context"), "print(context)"); assertEquals(fac.getProgram("print('hello')", "print('world')"), "print('hello');print('world');"); assertEquals(fac.getParameter(ScriptEngine.NAME), "javascript"); @@ -313,27 +311,6 @@ public class ScriptEngineTest { } } - public static void alert(final Object msg) { - System.out.println(msg); - } - - @Test - public void exposeMethodTest() { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - - try { - final Method alert = ScriptEngineTest.class.getMethod("alert", Object.class); - // expose a Method object as global var. - e.put("alert", alert); - // call the global var. - e.eval("alert.invoke(null, 'alert! alert!!')"); - } catch (final NoSuchMethodException | SecurityException | ScriptException exp) { - exp.printStackTrace(); - fail(exp.getMessage()); - } - } - @Test public void putGlobalFunctionTest() { final ScriptEngineManager m = new ScriptEngineManager(); @@ -592,13 +569,6 @@ public class ScriptEngineTest { } } - @Test - public void versionTest() { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - assertEquals(e.getFactory().getEngineVersion(), Version.version()); - } - @Test public void noEnumerablePropertiesTest() { final ScriptEngineManager m = new ScriptEngineManager(); @@ -874,26 +844,4 @@ public class ScriptEngineTest { fail(se.getMessage()); } } - - @Test - public void factoryOptionsTest() { - final ScriptEngineManager sm = new ScriptEngineManager(); - for (ScriptEngineFactory fac : sm.getEngineFactories()) { - if (fac instanceof NashornScriptEngineFactory) { - final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac; - // specify --no-syntax-extensions flag - final String[] options = new String[] { "--no-syntax-extensions" }; - final ScriptEngine e = nfac.getScriptEngine(options); - try { - // try nashorn specific extension - e.eval("var f = funtion(x) 2*x;"); - fail("should have thrown exception!"); - } catch (final ScriptException se) { - } - return; - } - } - - fail("Cannot find nashorn factory!"); - } } diff --git a/test/src/jdk/nashorn/internal/runtime/Nashorn401TestSubject.java b/test/src/jdk/nashorn/internal/runtime/Nashorn401TestSubject.java deleted file mode 100644 index ed6475de..00000000 --- a/test/src/jdk/nashorn/internal/runtime/Nashorn401TestSubject.java +++ /dev/null @@ -1,57 +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.runtime; - -public class Nashorn401TestSubject { - public String method2(int arg) { - return "int method 2"; - } - - public String method2(double arg) { - return "double method 2"; - } - - public String method2(String arg) { - return "string method 2"; - } - - public String method3(double arg) { - return "double method 3: " + arg; - } - - public String method3(int arg) { - return "int method 3: " + arg; - } - - public String method4(Double arg) { - return "double method 4: " + arg; - } - - public String method4(int arg) { - return "int method 4: " + arg; - } - -} diff --git a/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java b/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java index 0f147562..0f740a48 100644 --- a/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java +++ b/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java @@ -25,7 +25,7 @@ package jdk.nashorn.internal.runtime; - +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -40,6 +40,13 @@ import org.testng.annotations.Test; * Tests for trusted client usage of nashorn script engine factory extension API */ public class TrustedScriptEngineTest { + @Test + public void versionTest() { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + assertEquals(e.getFactory().getEngineVersion(), Version.version()); + } + private static class MyClassLoader extends ClassLoader { // to check if script engine uses the specified class loader private final boolean[] reached = new boolean[1]; @@ -116,4 +123,26 @@ public class TrustedScriptEngineTest { fail("Cannot find nashorn factory!"); } + + @Test + public void factoryOptionsTest() { + final ScriptEngineManager sm = new ScriptEngineManager(); + for (ScriptEngineFactory fac : sm.getEngineFactories()) { + if (fac instanceof NashornScriptEngineFactory) { + final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac; + // specify --no-syntax-extensions flag + final String[] options = new String[] { "--no-syntax-extensions" }; + final ScriptEngine e = nfac.getScriptEngine(options); + try { + // try nashorn specific extension + e.eval("var f = funtion(x) 2*x;"); + fail("should have thrown exception!"); + } catch (final ScriptException se) { + } + return; + } + } + + fail("Cannot find nashorn factory!"); + } } diff --git a/test/src/jdk/nashorn/internal/test/models/ConstructorWithArgument.java b/test/src/jdk/nashorn/internal/test/models/ConstructorWithArgument.java deleted file mode 100644 index ba4a2163..00000000 --- a/test/src/jdk/nashorn/internal/test/models/ConstructorWithArgument.java +++ /dev/null @@ -1,40 +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.test.models; - -public abstract class ConstructorWithArgument { - private final String token; - - protected ConstructorWithArgument(String token) { - this.token = token; - } - - public String getToken() { - return token; - } - - protected abstract void doSomething(); -} diff --git a/test/src/jdk/nashorn/internal/test/models/DessertTopping.java b/test/src/jdk/nashorn/internal/test/models/DessertTopping.java deleted file mode 100644 index 8427c837..00000000 --- a/test/src/jdk/nashorn/internal/test/models/DessertTopping.java +++ /dev/null @@ -1,30 +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.test.models; - -public interface DessertTopping { - public String pourOnDessert(); -} diff --git a/test/src/jdk/nashorn/internal/test/models/DessertToppingFloorWaxDriver.java b/test/src/jdk/nashorn/internal/test/models/DessertToppingFloorWaxDriver.java deleted file mode 100644 index dc04a0d1..00000000 --- a/test/src/jdk/nashorn/internal/test/models/DessertToppingFloorWaxDriver.java +++ /dev/null @@ -1,36 +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.test.models; - -public class DessertToppingFloorWaxDriver { - public void decorateDessert(DessertTopping dt) { - dt.pourOnDessert(); - } - - public void waxFloor(FloorWax fw) { - fw.shineUpTheFloor(); - } -} diff --git a/test/src/jdk/nashorn/internal/test/models/FinalClass.java b/test/src/jdk/nashorn/internal/test/models/FinalClass.java deleted file mode 100644 index b20257cb..00000000 --- a/test/src/jdk/nashorn/internal/test/models/FinalClass.java +++ /dev/null @@ -1,30 +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.test.models; - -public final class FinalClass { - //empty -} diff --git a/test/src/jdk/nashorn/internal/test/models/FloorWax.java b/test/src/jdk/nashorn/internal/test/models/FloorWax.java deleted file mode 100644 index c094ccf6..00000000 --- a/test/src/jdk/nashorn/internal/test/models/FloorWax.java +++ /dev/null @@ -1,30 +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.test.models; - -public interface FloorWax { - public String shineUpTheFloor(); -} diff --git a/test/src/jdk/nashorn/internal/test/models/NoAccessibleConstructorClass.java b/test/src/jdk/nashorn/internal/test/models/NoAccessibleConstructorClass.java deleted file mode 100644 index c79edfbb..00000000 --- a/test/src/jdk/nashorn/internal/test/models/NoAccessibleConstructorClass.java +++ /dev/null @@ -1,30 +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.test.models; - -public class NoAccessibleConstructorClass { - NoAccessibleConstructorClass() { } -} diff --git a/test/src/jdk/nashorn/internal/test/models/NonPublicClass.java b/test/src/jdk/nashorn/internal/test/models/NonPublicClass.java deleted file mode 100644 index 046cdf24..00000000 --- a/test/src/jdk/nashorn/internal/test/models/NonPublicClass.java +++ /dev/null @@ -1,30 +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.test.models; - -class NonPublicClass { - public NonPublicClass() { } -} diff --git a/test/src/jdk/nashorn/internal/test/models/OuterClass.java b/test/src/jdk/nashorn/internal/test/models/OuterClass.java deleted file mode 100644 index cb9484b9..00000000 --- a/test/src/jdk/nashorn/internal/test/models/OuterClass.java +++ /dev/null @@ -1,65 +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.test.models; - -public class OuterClass { - private final String value; - - public OuterClass(String value) { - this.value = value; - } - - public static class InnerStaticClass { - private final String value; - - public InnerStaticClass(String value) { - this.value = value; - } - - @Override - public String toString() { - return "InnerStaticClass[value=" + value + "]"; - } - } - - public class InnerNonStaticClass { - private final String value; - - public InnerNonStaticClass(String value) { - this.value = value; - } - - @Override - public String toString() { - return "InnerNonStaticClass[value=" + value + ", outer=" + OuterClass.this + "]"; - } - } - - @Override - public String toString() { - return "OuterClass[value=" + value + "]"; - } -} diff --git a/test/src/jdk/nashorn/internal/test/models/OverloadedSam.java b/test/src/jdk/nashorn/internal/test/models/OverloadedSam.java deleted file mode 100644 index c63da54c..00000000 --- a/test/src/jdk/nashorn/internal/test/models/OverloadedSam.java +++ /dev/null @@ -1,31 +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.test.models; - -public interface OverloadedSam { - public void sam(String s); - public void sam(String s1, String s2); -} diff --git a/test/src/jdk/nashorn/internal/test/models/OverrideObject.java b/test/src/jdk/nashorn/internal/test/models/OverrideObject.java deleted file mode 100644 index 40cf7f97..00000000 --- a/test/src/jdk/nashorn/internal/test/models/OverrideObject.java +++ /dev/null @@ -1,44 +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.test.models; - -public class OverrideObject { - @Override - public int hashCode() { - return 5; - } - - @Override - public String toString() { - return "override-object"; - } - - @Override - public boolean equals(Object o) { - // TODO: add a FindBugs annotation to ignore EQ_ALWAYS_FALSE here. This is just a test. - return false; - } -} diff --git a/test/src/jdk/nashorn/internal/test/models/StringArgs.java b/test/src/jdk/nashorn/internal/test/models/StringArgs.java deleted file mode 100644 index 8ecdbfd3..00000000 --- a/test/src/jdk/nashorn/internal/test/models/StringArgs.java +++ /dev/null @@ -1,39 +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.test.models; - -import java.util.List; - -public class StringArgs { - - public static void checkString(List list) { - for (Object s : list) { - if (!(s instanceof String)) { - throw new AssertionError("Not a String: " + s); - } - } - } -} diff --git a/test/src/jdk/nashorn/internal/test/models/Toothpaste.java b/test/src/jdk/nashorn/internal/test/models/Toothpaste.java deleted file mode 100644 index 0946eb35..00000000 --- a/test/src/jdk/nashorn/internal/test/models/Toothpaste.java +++ /dev/null @@ -1,34 +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.test.models; - -public abstract class Toothpaste { - public void applyToBrush() { - applyToBrushImpl(); - } - - protected abstract void applyToBrushImpl(); -} diff --git a/test/src/jdk/nashorn/test/models/ConstructorWithArgument.java b/test/src/jdk/nashorn/test/models/ConstructorWithArgument.java new file mode 100644 index 00000000..9a201575 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/ConstructorWithArgument.java @@ -0,0 +1,40 @@ +/* + * 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.test.models; + +public abstract class ConstructorWithArgument { + private final String token; + + protected ConstructorWithArgument(String token) { + this.token = token; + } + + public String getToken() { + return token; + } + + protected abstract void doSomething(); +} diff --git a/test/src/jdk/nashorn/test/models/DessertTopping.java b/test/src/jdk/nashorn/test/models/DessertTopping.java new file mode 100644 index 00000000..591e032d --- /dev/null +++ b/test/src/jdk/nashorn/test/models/DessertTopping.java @@ -0,0 +1,30 @@ +/* + * 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.test.models; + +public interface DessertTopping { + public String pourOnDessert(); +} diff --git a/test/src/jdk/nashorn/test/models/DessertToppingFloorWaxDriver.java b/test/src/jdk/nashorn/test/models/DessertToppingFloorWaxDriver.java new file mode 100644 index 00000000..856029ad --- /dev/null +++ b/test/src/jdk/nashorn/test/models/DessertToppingFloorWaxDriver.java @@ -0,0 +1,36 @@ +/* + * 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.test.models; + +public class DessertToppingFloorWaxDriver { + public void decorateDessert(DessertTopping dt) { + dt.pourOnDessert(); + } + + public void waxFloor(FloorWax fw) { + fw.shineUpTheFloor(); + } +} diff --git a/test/src/jdk/nashorn/test/models/FinalClass.java b/test/src/jdk/nashorn/test/models/FinalClass.java new file mode 100644 index 00000000..8a3e8432 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/FinalClass.java @@ -0,0 +1,30 @@ +/* + * 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.test.models; + +public final class FinalClass { + //empty +} diff --git a/test/src/jdk/nashorn/test/models/FloorWax.java b/test/src/jdk/nashorn/test/models/FloorWax.java new file mode 100644 index 00000000..44ac96e9 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/FloorWax.java @@ -0,0 +1,30 @@ +/* + * 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.test.models; + +public interface FloorWax { + public String shineUpTheFloor(); +} diff --git a/test/src/jdk/nashorn/test/models/Nashorn401TestSubject.java b/test/src/jdk/nashorn/test/models/Nashorn401TestSubject.java new file mode 100644 index 00000000..2e7d9c6c --- /dev/null +++ b/test/src/jdk/nashorn/test/models/Nashorn401TestSubject.java @@ -0,0 +1,57 @@ +/* + * 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.test.models; + +public class Nashorn401TestSubject { + public String method2(int arg) { + return "int method 2"; + } + + public String method2(double arg) { + return "double method 2"; + } + + public String method2(String arg) { + return "string method 2"; + } + + public String method3(double arg) { + return "double method 3: " + arg; + } + + public String method3(int arg) { + return "int method 3: " + arg; + } + + public String method4(Double arg) { + return "double method 4: " + arg; + } + + public String method4(int arg) { + return "int method 4: " + arg; + } + +} diff --git a/test/src/jdk/nashorn/test/models/NoAccessibleConstructorClass.java b/test/src/jdk/nashorn/test/models/NoAccessibleConstructorClass.java new file mode 100644 index 00000000..f0ddb1aa --- /dev/null +++ b/test/src/jdk/nashorn/test/models/NoAccessibleConstructorClass.java @@ -0,0 +1,30 @@ +/* + * 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.test.models; + +public class NoAccessibleConstructorClass { + NoAccessibleConstructorClass() { } +} diff --git a/test/src/jdk/nashorn/test/models/NonPublicClass.java b/test/src/jdk/nashorn/test/models/NonPublicClass.java new file mode 100644 index 00000000..d0a61856 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/NonPublicClass.java @@ -0,0 +1,30 @@ +/* + * 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.test.models; + +class NonPublicClass { + public NonPublicClass() { } +} diff --git a/test/src/jdk/nashorn/test/models/OuterClass.java b/test/src/jdk/nashorn/test/models/OuterClass.java new file mode 100644 index 00000000..5db86f28 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/OuterClass.java @@ -0,0 +1,65 @@ +/* + * 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.test.models; + +public class OuterClass { + private final String value; + + public OuterClass(String value) { + this.value = value; + } + + public static class InnerStaticClass { + private final String value; + + public InnerStaticClass(String value) { + this.value = value; + } + + @Override + public String toString() { + return "InnerStaticClass[value=" + value + "]"; + } + } + + public class InnerNonStaticClass { + private final String value; + + public InnerNonStaticClass(String value) { + this.value = value; + } + + @Override + public String toString() { + return "InnerNonStaticClass[value=" + value + ", outer=" + OuterClass.this + "]"; + } + } + + @Override + public String toString() { + return "OuterClass[value=" + value + "]"; + } +} diff --git a/test/src/jdk/nashorn/test/models/OverloadedSam.java b/test/src/jdk/nashorn/test/models/OverloadedSam.java new file mode 100644 index 00000000..05736bca --- /dev/null +++ b/test/src/jdk/nashorn/test/models/OverloadedSam.java @@ -0,0 +1,31 @@ +/* + * 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.test.models; + +public interface OverloadedSam { + public void sam(String s); + public void sam(String s1, String s2); +} diff --git a/test/src/jdk/nashorn/test/models/OverrideObject.java b/test/src/jdk/nashorn/test/models/OverrideObject.java new file mode 100644 index 00000000..5312ffbb --- /dev/null +++ b/test/src/jdk/nashorn/test/models/OverrideObject.java @@ -0,0 +1,44 @@ +/* + * 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.test.models; + +public class OverrideObject { + @Override + public int hashCode() { + return 5; + } + + @Override + public String toString() { + return "override-object"; + } + + @Override + public boolean equals(Object o) { + // TODO: add a FindBugs annotation to ignore EQ_ALWAYS_FALSE here. This is just a test. + return false; + } +} diff --git a/test/src/jdk/nashorn/test/models/SourceHelper.java b/test/src/jdk/nashorn/test/models/SourceHelper.java new file mode 100644 index 00000000..46b1e488 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/SourceHelper.java @@ -0,0 +1,55 @@ +/* + * 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.test.models; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.net.URL; +import jdk.nashorn.internal.runtime.Source; + +/** + * Helper class to facilitate script access of nashorn Source class. + */ +public final class SourceHelper { + private SourceHelper() {} + + public static String baseURL(final URL url) { + return Source.baseURL(url); + } + + public static String readFully(final File file) throws IOException { + return new String(Source.readFully(file)); + } + + public static String readFully(final URL url) throws IOException { + return new Source(url.toString(), url).getString(); + } + + public static String readFully(final Reader reader) throws IOException { + return new String(Source.readFully(reader)); + } +} diff --git a/test/src/jdk/nashorn/test/models/StringArgs.java b/test/src/jdk/nashorn/test/models/StringArgs.java new file mode 100644 index 00000000..1fdcd5de --- /dev/null +++ b/test/src/jdk/nashorn/test/models/StringArgs.java @@ -0,0 +1,39 @@ +/* + * 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.test.models; + +import java.util.List; + +public class StringArgs { + + public static void checkString(List list) { + for (Object s : list) { + if (!(s instanceof String)) { + throw new AssertionError("Not a String: " + s); + } + } + } +} diff --git a/test/src/jdk/nashorn/test/models/Toothpaste.java b/test/src/jdk/nashorn/test/models/Toothpaste.java new file mode 100644 index 00000000..7eae95f3 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/Toothpaste.java @@ -0,0 +1,34 @@ +/* + * 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.test.models; + +public abstract class Toothpaste { + public void applyToBrush() { + applyToBrushImpl(); + } + + protected abstract void applyToBrushImpl(); +} -- cgit v1.2.3 From d325726a5d184bd0f737c7c20e89a76ca11d2215 Mon Sep 17 00:00:00 2001 From: lagergren Date: Tue, 12 Mar 2013 15:30:53 +0100 Subject: 8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change. Reviewed-by: attila, jlaskey --- src/jdk/nashorn/internal/codegen/Attr.java | 143 ++-- .../nashorn/internal/codegen/BranchOptimizer.java | 1 - .../nashorn/internal/codegen/CodeGenerator.java | 47 +- .../nashorn/internal/codegen/CompilationPhase.java | 51 +- src/jdk/nashorn/internal/codegen/CompileUnit.java | 20 + src/jdk/nashorn/internal/codegen/Compiler.java | 177 +++-- .../nashorn/internal/codegen/FinalizeTypes.java | 14 +- .../nashorn/internal/codegen/FoldConstants.java | 16 + .../internal/codegen/FunctionSignature.java | 10 +- src/jdk/nashorn/internal/codegen/Lower.java | 5 +- src/jdk/nashorn/internal/codegen/Splitter.java | 10 +- src/jdk/nashorn/internal/codegen/types/Type.java | 15 +- src/jdk/nashorn/internal/ir/Block.java | 19 +- src/jdk/nashorn/internal/ir/FunctionNode.java | 50 +- src/jdk/nashorn/internal/ir/ObjectNode.java | 21 +- src/jdk/nashorn/internal/ir/UnaryNode.java | 6 +- .../nashorn/internal/ir/annotations/ChildNode.java | 42 - .../internal/ir/annotations/ParentNode.java | 44 -- .../nashorn/internal/ir/annotations/Reference.java | 1 - src/jdk/nashorn/internal/ir/debug/ASTWriter.java | 38 +- src/jdk/nashorn/internal/objects/NativeArray.java | 4 +- src/jdk/nashorn/internal/objects/NativeDebug.java | 15 - src/jdk/nashorn/internal/objects/NativeError.java | 2 +- .../internal/objects/ScriptFunctionImpl.java | 11 +- .../objects/ScriptFunctionTrampolineImpl.java | 122 --- src/jdk/nashorn/internal/parser/JSONParser.java | 2 +- src/jdk/nashorn/internal/parser/Parser.java | 170 ++--- .../nashorn/internal/runtime/AccessorProperty.java | 4 +- .../nashorn/internal/runtime/CodeInstaller.java | 6 +- .../nashorn/internal/runtime/CompiledFunction.java | 162 ++++ .../internal/runtime/CompiledFunctions.java | 73 ++ src/jdk/nashorn/internal/runtime/Context.java | 2 +- .../nashorn/internal/runtime/ECMAException.java | 2 +- .../internal/runtime/FinalScriptFunctionData.java | 100 +++ .../runtime/RecompilableScriptFunctionData.java | 185 +++++ .../internal/runtime/ScriptEnvironment.java | 4 + .../nashorn/internal/runtime/ScriptFunction.java | 115 +-- .../internal/runtime/ScriptFunctionData.java | 847 +++++++++------------ .../internal/runtime/SpecializedMethodChooser.java | 99 --- .../runtime/linker/JavaArgumentConverters.java | 2 +- .../internal/runtime/linker/NashornGuards.java | 39 - .../internal/runtime/options/OptionTemplate.java | 2 +- .../internal/runtime/resources/Options.properties | 6 + test/script/currently-failing/JDK-8006529.js | 13 +- test/script/currently-failing/clone_ir.js | 100 +++ 45 files changed, 1555 insertions(+), 1262 deletions(-) delete mode 100644 src/jdk/nashorn/internal/ir/annotations/ChildNode.java delete mode 100644 src/jdk/nashorn/internal/ir/annotations/ParentNode.java delete mode 100644 src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java create mode 100644 src/jdk/nashorn/internal/runtime/CompiledFunction.java create mode 100644 src/jdk/nashorn/internal/runtime/CompiledFunctions.java create mode 100644 src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java create mode 100644 src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java delete mode 100644 src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java create mode 100644 test/script/currently-failing/clone_ir.js diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java index 211140e4..a441ef4d 100644 --- a/src/jdk/nashorn/internal/codegen/Attr.java +++ b/src/jdk/nashorn/internal/codegen/Attr.java @@ -42,6 +42,7 @@ import static jdk.nashorn.internal.ir.Symbol.IS_VAR; import java.util.ArrayList; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -51,6 +52,7 @@ import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CallNode.EvalArgs; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ForNode; @@ -332,8 +334,14 @@ final class Attr extends NodeOperatorVisitor { functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this)); } + if (functionNode.hasLazyChildren()) { + objectifySymbols(functionNode); + } + functionNode.popFrame(); + functionNode.setState(CompilationState.ATTR); + end(functionNode, false); return null; @@ -957,20 +965,17 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveBIT_AND(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveBIT_OR(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveBIT_XOR(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override @@ -1002,14 +1007,28 @@ final class Attr extends NodeOperatorVisitor { return binaryNode; } + private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) { + // TODO we currently don't support changing inferred type based on uses, only on + // definitions. we would need some additional logic. We probably want to do that + // in the future, if e.g. a specialized method gets parameter that is only used + // as, say, an int : function(x) { return x & 4711 }, and x is not defined in + // the function. to make this work, uncomment the following two type inferences + // and debug. + + //newType(binaryNode.lhs().getSymbol(), operandType); + //newType(binaryNode.rhs().getSymbol(), operandType); + newTemporary(destType, binaryNode); + return binaryNode; + } + + private Node coerce(final BinaryNode binaryNode, final Type type) { + return coerce(binaryNode, type, type); + } + //leave a binary node and inherit the widest type of lhs , rhs private Node leaveBinaryArithmetic(final BinaryNode binaryNode) { - if (!Compiler.shouldUseIntegerArithmetic()) { - newTemporary(Type.NUMBER, binaryNode); - return binaryNode; - } - newTemporary(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType(), Type.NUMBER), binaryNode); - return binaryNode; + assert !Compiler.shouldUseIntegerArithmetic(); + return end(coerce(binaryNode, Type.NUMBER)); } @Override @@ -1089,23 +1108,17 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveSAR(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - end(binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveSHL(final BinaryNode binaryNode) { - newTemporary(Type.INT, binaryNode); - end(binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.INT)); } @Override public Node leaveSHR(final BinaryNode binaryNode) { - newTemporary(Type.LONG, binaryNode); - end(binaryNode); - return binaryNode; + return end(coerce(binaryNode, Type.LONG)); } @Override @@ -1211,11 +1224,17 @@ final class Attr extends NodeOperatorVisitor { // type or its parameters with the widest (OBJECT) type for safety. functionNode.setReturnType(Type.UNKNOWN); - for (final IdentNode ident : functionNode.getParameters()) { - addLocalDef(ident.getName()); - final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident); + for (final IdentNode param : functionNode.getParameters()) { + addLocalDef(param.getName()); + final Symbol paramSymbol = functionNode.defineSymbol(param.getName(), IS_PARAM, param); if (paramSymbol != null) { - newType(paramSymbol, Type.UNKNOWN); + final Type callSiteParamType = functionNode.getSpecializedType(param); + if (callSiteParamType != null) { + LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that."); + + System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that."); + } + newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType); } LOG.info("Initialized param " + paramSymbol); @@ -1229,36 +1248,29 @@ final class Attr extends NodeOperatorVisitor { * @param functionNode functionNode */ private static void finalizeParameters(final FunctionNode functionNode) { - boolean nonObjectParams = false; - List paramSpecializations = new ArrayList<>(); + final boolean isVarArg = functionNode.isVarArg(); for (final IdentNode ident : functionNode.getParameters()) { final Symbol paramSymbol = ident.getSymbol(); - if (paramSymbol != null) { - Type type = paramSymbol.getSymbolType(); - if (type.isUnknown()) { - type = Type.OBJECT; - } - paramSpecializations.add(type); - if (!type.isObject()) { - nonObjectParams = true; - } - newType(paramSymbol, Type.OBJECT); + + assert paramSymbol != null; + Type type = functionNode.getSpecializedType(ident); + if (type == null) { + type = Type.OBJECT; } - } - if (!nonObjectParams) { - paramSpecializations = null; - // Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters - // here. If the callee has parameter specializations, we can regenerate it with those particular types for speed. - } else { - LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations); - } + // if we know that a parameter is only used as a certain type throughout + // this function, we can tell the runtime system that no matter what the + // call site is, use this information. TODO + if (!paramSymbol.getSymbolType().isObject()) { + LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType()); + } - // parameters should not be slots for a function that uses variable arity signature - if (functionNode.isVarArg()) { - for (final IdentNode param : functionNode.getParameters()) { - param.getSymbol().setNeedsSlot(false); + newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType())); + + // parameters should not be slots for a function that uses variable arity signature + if (isVarArg) { + paramSymbol.setNeedsSlot(false); } } } @@ -1548,6 +1560,39 @@ final class Attr extends NodeOperatorVisitor { localUses.add(name); } + /** + * Pessimistically promote all symbols in current function node to Object types + * This is done when the function contains unevaluated black boxes such as + * lazy sub-function nodes that have not been compiled. + * + * @param functionNode function node in whose scope symbols should conservatively be made objects + */ + private static void objectifySymbols(final FunctionNode functionNode) { + functionNode.accept(new NodeVisitor() { + private void toObject(final Block block) { + for (final Iterator iter = block.symbolIterator(); iter.hasNext();) { + final Symbol symbol = iter.next(); + newType(symbol, Type.OBJECT); + } + } + + @Override + public Node enter(final Block block) { + toObject(block); + return block; + } + + @Override + public Node enter(final FunctionNode node) { + toObject(node); + if (node.isLazy()) { + return null; + } + return node; + } + }); + } + private static String name(final Node node) { final String cn = node.getClass().getName(); int lastDot = cn.lastIndexOf('.'); diff --git a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java index 84cef437..ee922115 100644 --- a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java +++ b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java @@ -32,7 +32,6 @@ import static jdk.nashorn.internal.codegen.Condition.LE; import static jdk.nashorn.internal.codegen.Condition.LT; import static jdk.nashorn.internal.codegen.Condition.NE; -import jdk.nashorn.internal.codegen.Label; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Node; diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 2ffb5bd8..71a2eb0f 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -25,10 +25,8 @@ package jdk.nashorn.internal.codegen; -import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC; import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE; import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC; -import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE; import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP; import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF; @@ -50,7 +48,6 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT; import java.io.PrintWriter; -import java.lang.invoke.MethodHandle; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; @@ -84,6 +81,7 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; @@ -108,14 +106,13 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.TokenType; -import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.Scope; import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.Source; @@ -149,8 +146,6 @@ final class CodeGenerator extends NodeOperatorVisitor { /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */ private static final String SCRIPTFUNCTION_IMPL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl"; - private static final String SCRIPTFUNCTION_TRAMPOLINE_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionTrampolineImpl"; - /** Constant data & installation. The only reason the compiler keeps this is because it is assigned * by reflection in class installation */ private final Compiler compiler; @@ -982,6 +977,7 @@ final class CodeGenerator extends NodeOperatorVisitor { method.label(functionNode.getEntryLabel()); initLocals(functionNode); + functionNode.setState(CompilationState.EMITTED); return functionNode; } @@ -3234,42 +3230,23 @@ final class CodeGenerator extends NodeOperatorVisitor { } private void newFunctionObject(final FunctionNode functionNode) { - final boolean isLazy = functionNode.isLazy(); - final Class[] cparams = new Class[] { ScriptFunctionData.class, ScriptObject.class, MethodHandle.class }; + final boolean isLazy = functionNode.isLazy(); + final Class[] cparams = new Class[] { RecompilableScriptFunctionData.class, ScriptObject.class }; new ObjectCreator(this, new ArrayList(), new ArrayList(), false, false) { @Override - protected void makeObject(final MethodEmitter method) { - final String className = isLazy ? SCRIPTFUNCTION_TRAMPOLINE_OBJECT : SCRIPTFUNCTION_IMPL_OBJECT; + protected void makeObject(final MethodEmitter m) { + final String className = SCRIPTFUNCTION_IMPL_OBJECT; - method._new(className).dup(); - if (isLazy) { - loadConstant(compiler.getCodeInstaller()); - loadConstant(functionNode); - } else { - final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString(); - method.loadHandle(functionNode.getCompileUnit().getUnitClassName(), functionNode.getName(), signature, EnumSet.of(HANDLE_STATIC)); // function - } - loadConstant(new ScriptFunctionData(functionNode, makeMap())); + m._new(className).dup(); + loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap())); if (isLazy || functionNode.needsParentScope()) { - method.loadScope(); - } else { - method.loadNull(); - } - - method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC)); - - final List> cparamList = new ArrayList<>(); - if (isLazy) { - cparamList.add(CodeInstaller.class); - cparamList.add(FunctionNode.class); + m.loadScope(); } else { - cparamList.add(MethodHandle.class); + m.loadNull(); } - cparamList.addAll(Arrays.asList(cparams)); - - method.invoke(constructorNoLookup(className, cparamList.toArray(new Class[cparamList.size()]))); + m.invoke(constructorNoLookup(className, cparams)); } }.makeObject(method); } diff --git a/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/src/jdk/nashorn/internal/codegen/CompilationPhase.java index e22454cc..0125efff 100644 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java @@ -2,10 +2,10 @@ package jdk.nashorn.internal.codegen; import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.ATTR; import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.EMITTED; import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.FINALIZED; import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED; import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED; +import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED; import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; import java.io.File; @@ -39,7 +39,7 @@ enum CompilationPhase { * default policy. The will get trampolines and only be generated when * called */ - LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) { + LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) { @Override void transform(final Compiler compiler, final FunctionNode fn) { @@ -79,9 +79,11 @@ enum CompilationPhase { if (node == outermostFunctionNode) { return node; } - assert Compiler.LAZY_JIT; + assert compiler.isLazy(); lazy.add(node); + //also needs scope, potentially needs arguments etc etc + return node; } }); @@ -113,7 +115,7 @@ enum CompilationPhase { * Constant folding pass * Simple constant folding that will make elementary constructs go away */ - CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) { + CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) { @Override void transform(final Compiler compiler, final FunctionNode fn) { fn.accept(new FoldConstants()); @@ -134,7 +136,7 @@ enum CompilationPhase { * as runtime nodes where applicable. * */ - LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) { + LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) { @Override void transform(final Compiler compiler, final FunctionNode fn) { fn.accept(new Lower()); @@ -150,19 +152,10 @@ enum CompilationPhase { * Attribution * Assign symbols and types to all nodes. */ - ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) { + ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) { @Override void transform(final Compiler compiler, final FunctionNode fn) { - final ScriptEnvironment env = compiler.getEnv(); - fn.accept(new Attr()); - if (env._print_lower_ast) { - env.getErr().println(new ASTWriter(fn)); - } - - if (env._print_lower_parse) { - env.getErr().println(new PrintVisitor(fn)); - } } @Override @@ -178,7 +171,7 @@ enum CompilationPhase { * a + b a ScriptRuntime.ADD with call overhead or a dadd with much * less). Split IR can lead to scope information being changed. */ - SPLITTING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR), SPLIT) { + SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) { @Override void transform(final Compiler compiler, final FunctionNode fn) { final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName()); @@ -212,10 +205,20 @@ enum CompilationPhase { * Contract: all variables must have slot assignments and scope assignments * before type finalization. */ - TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT), FINALIZED) { + TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) { @Override void transform(final Compiler compiler, final FunctionNode fn) { + final ScriptEnvironment env = compiler.getEnv(); + fn.accept(new FinalizeTypes()); + + if (env._print_lower_ast) { + env.getErr().println(new ASTWriter(fn)); + } + + if (env._print_lower_parse) { + env.getErr().println(new PrintVisitor(fn)); + } } @Override @@ -229,7 +232,7 @@ enum CompilationPhase { * * Generate the byte code class(es) resulting from the compiled FunctionNode */ - BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) { + BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) { @Override void transform(final Compiler compiler, final FunctionNode fn) { final ScriptEnvironment env = compiler.getEnv(); @@ -306,18 +309,12 @@ enum CompilationPhase { }; private final EnumSet pre; - private final CompilationState post; private long startTime; private long endTime; private boolean isFinished; private CompilationPhase(final EnumSet pre) { - this(pre, null); - } - - private CompilationPhase(final EnumSet pre, final CompilationState post) { - this.pre = pre; - this.post = post; + this.pre = pre; } boolean isApplicable(final FunctionNode functionNode) { @@ -343,10 +340,6 @@ enum CompilationPhase { endTime = System.currentTimeMillis(); Timing.accumulateTime(toString(), endTime - startTime); - if (post != null) { - functionNode.setState(post); - } - isFinished = true; } diff --git a/src/jdk/nashorn/internal/codegen/CompileUnit.java b/src/jdk/nashorn/internal/codegen/CompileUnit.java index 5e62116b..ff88fa99 100644 --- a/src/jdk/nashorn/internal/codegen/CompileUnit.java +++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java @@ -37,6 +37,8 @@ public class CompileUnit { private long weight; + private Class clazz; + CompileUnit(final String className, final ClassEmitter classEmitter) { this(className, classEmitter, 0L); } @@ -47,6 +49,24 @@ public class CompileUnit { this.weight = initialWeight; } + /** + * Return the class that contains the code for this unit, null if not + * generated yet + * + * @return class with compile unit code + */ + public Class getCode() { + return clazz; + } + + /** + * Set class when it exists. Only accessible from compiler + * @param clazz class with code for this compile unit + */ + void setCode(final Class clazz) { + this.clazz = clazz; + } + /** * Add weight to this compile unit * @param w weight to add diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java index ca23c428..a799ee05 100644 --- a/src/jdk/nashorn/internal/codegen/Compiler.java +++ b/src/jdk/nashorn/internal/codegen/Compiler.java @@ -45,11 +45,15 @@ import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.logging.Level; + import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.ScriptEnvironment; @@ -71,8 +75,6 @@ public final class Compiler { /** Name of the objects package */ public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects"; - static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy"); - private final Map bytecode; private final Set compileUnits; @@ -164,7 +166,7 @@ public final class Compiler { * and JIT it at once. This can lead to long startup time and fewer type * specializations */ - final static CompilationSequence SEQUENCE_NORMAL = new CompilationSequence( + final static CompilationSequence SEQUENCE_EAGER = new CompilationSequence( CompilationPhase.CONSTANT_FOLDING_PHASE, CompilationPhase.LOWERING_PHASE, CompilationPhase.ATTRIBUTION_PHASE, @@ -173,12 +175,15 @@ public final class Compiler { CompilationPhase.BYTECODE_GENERATION_PHASE); final static CompilationSequence SEQUENCE_LAZY = - SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE); + SEQUENCE_EAGER.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE); + + private static CompilationSequence sequence(final boolean lazy) { + return lazy ? SEQUENCE_LAZY : SEQUENCE_EAGER; + } - final static CompilationSequence SEQUENCE_DEFAULT = - LAZY_JIT ? - SEQUENCE_LAZY : - SEQUENCE_NORMAL; + boolean isLazy() { + return sequence == SEQUENCE_LAZY; + } private static String lazyTag(final FunctionNode functionNode) { if (functionNode.isLazy()) { @@ -213,10 +218,7 @@ public final class Compiler { this.scriptName = sb.toString(); - LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'"); - if (functionNode.isLazy()) { - LOG.info(">>> This is a lazy recompilation triggered by a trampoline"); - } + LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "' lazy=" + functionNode.isLazy()); } /** @@ -227,7 +229,7 @@ public final class Compiler { * @param strict should this compilation use strict mode semantics */ public Compiler(final CodeInstaller installer, final FunctionNode functionNode, final boolean strict) { - this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict); + this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), strict); } /** @@ -237,7 +239,7 @@ public final class Compiler { * @param functionNode function node (in any available {@link CompilationState}) to compile */ public Compiler(final CodeInstaller installer, final FunctionNode functionNode) { - this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict); + this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict); } /** @@ -247,28 +249,104 @@ public final class Compiler { * @param functionNode functionNode to compile */ public Compiler(final ScriptEnvironment env, final FunctionNode functionNode) { - this(env, null, functionNode, SEQUENCE_DEFAULT, env._strict); + this(env, null, functionNode, sequence(env._lazy_compilation), env._strict); + } + + /** + * Execute the compilation this Compiler was created with + * @params param types if known, for specialization + * @throws CompilationException if something goes wrong + * @return this compiler, for possible chaining + */ + public Compiler compile() throws CompilationException { + return compile(null); } /** * Execute the compilation this Compiler was created with + * @param paramTypes param types if known, for specialization * @throws CompilationException if something goes wrong + * @return this compiler, for possible chaining */ - public void compile() throws CompilationException { + public Compiler compile(final Class paramTypes) throws CompilationException { for (final String reservedName : RESERVED_NAMES) { functionNode.uniqueName(reservedName); } + final boolean fine = !LOG.levelAbove(Level.FINE); + final boolean info = !LOG.levelAbove(Level.INFO); + + long time = 0L; + for (final CompilationPhase phase : sequence) { phase.apply(this, functionNode); - final String end = phase.toString() + " done for function '" + functionNode.getName() + "'"; - if (Timing.isEnabled()) { - final long duration = phase.getEndTime() - phase.getStartTime(); - LOG.info(end + " in " + duration + " ms"); - } else { - LOG.info(end); + + final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L; + time += duration; + + if (fine) { + final StringBuilder sb = new StringBuilder(); + + sb.append(phase.toString()). + append(" done for function '"). + append(functionNode.getName()). + append('\''); + + if (duration > 0L) { + sb.append(" in "). + append(duration). + append(" ms "); + } + + LOG.fine(sb.toString()); } } + + if (info) { + final StringBuilder sb = new StringBuilder(); + sb.append("Compile job for '"). + append(functionNode.getName()). + append("' finished"); + + if (time > 0L) { + sb.append(" in "). + append(time). + append(" ms"); + } + + LOG.info(sb.toString()); + } + + return this; + } + + private Class install(final String className, final byte[] code) { + LOG.fine("Installing class " + className); + + final Class clazz = installer.install(Compiler.binaryName(className), code); + + try { + final Source source = getSource(); + final Object[] constants = getConstantData().toArray(); + // Need doPrivileged because these fields are private + AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + //use reflection to write source and constants table to installed classes + final Field sourceField = clazz.getDeclaredField(SOURCE.tag()); + final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag()); + sourceField.setAccessible(true); + constantsField.setAccessible(true); + sourceField.set(null, source); + constantsField.set(null, constants); + return null; + } + }); + } catch (final PrivilegedActionException e) { + throw new RuntimeException(e); + } + + return clazz; } /** @@ -280,42 +358,38 @@ public final class Compiler { assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed"; - Class rootClass = null; + final Map> installedClasses = new HashMap<>(); - for (final Entry entry : bytecode.entrySet()) { - final String className = entry.getKey(); - LOG.fine("Installing class " + className); + final String rootClassName = firstCompileUnitName(); + final Class rootClass = install(rootClassName, bytecode.get(rootClassName)); - final byte[] code = entry.getValue(); - final Class clazz = installer.install(Compiler.binaryName(className), code); + installedClasses.put(rootClassName, rootClass); - if (rootClass == null && firstCompileUnitName().equals(className)) { - rootClass = clazz; + for (final Entry entry : bytecode.entrySet()) { + final String className = entry.getKey(); + if (className.equals(rootClassName)) { + continue; } + installedClasses.put(className, install(className, entry.getValue())); + } - try { - final Source source = getSource(); - final Object[] constants = getConstantData().toArray(); - // Need doPrivileged because these fields are private - AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Void run() throws Exception { - //use reflection to write source and constants table to installed classes - final Field sourceField = clazz.getDeclaredField(SOURCE.tag()); - final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag()); - sourceField.setAccessible(true); - constantsField.setAccessible(true); - sourceField.set(null, source); - constantsField.set(null, constants); - return null; - } - }); - } catch (final PrivilegedActionException e) { - throw new RuntimeException(e); - } + for (final CompileUnit unit : compileUnits) { + unit.setCode(installedClasses.get(unit.getUnitClassName())); } + functionNode.accept(new NodeVisitor() { + @Override + public Node enter(final FunctionNode node) { + if (node.isLazy()) { + return null; + } + node.setState(CompilationState.INSTALLED); + return node; + } + }); + LOG.info("Installed root class: " + rootClass + " and " + bytecode.size() + " compile unit classes"); + if (Timing.isEnabled()) { final long duration = System.currentTimeMillis() - t0; Timing.accumulateTime("[Code Installation]", duration); @@ -444,8 +518,6 @@ public final class Compiler { * TODO: We currently generate no overflow checks so this is * disabled * - * @see #shouldUseIntegers() - * * @return true if arithmetic operations should not widen integer * operands by default. */ @@ -460,4 +532,5 @@ public final class Compiler { assert !USE_INT_ARITH : "Integer arithmetic is not enabled"; } + } diff --git a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java index 28dfda7d..b302d6fe 100644 --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java @@ -34,6 +34,7 @@ import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CallNode.EvalArgs; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.DoWhileNode; @@ -235,19 +236,19 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveBIT_AND(BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : binaryNode.getSymbol(); + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol(); return leaveBinary(binaryNode, Type.INT, Type.INT); } @Override public Node leaveBIT_OR(BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger(); + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol(); return leaveBinary(binaryNode, Type.INT, Type.INT); } @Override public Node leaveBIT_XOR(BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger(); + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol(); return leaveBinary(binaryNode, Type.INT, Type.INT); } @@ -255,7 +256,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { public Node leaveCOMMALEFT(final BinaryNode binaryNode) { assert binaryNode.getSymbol() != null; binaryNode.setRHS(discard(binaryNode.rhs())); - // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed + // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed // in that case, update the node type as well propagateType(binaryNode, binaryNode.lhs().getType()); return binaryNode; @@ -344,7 +345,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveSHR(final BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong(); + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong() : "long coercion expected: " + binaryNode.getSymbol(); return leaveBinary(binaryNode, Type.INT, Type.INT); } @@ -432,6 +433,8 @@ final class FinalizeTypes extends NodeOperatorVisitor { } updateSymbols(functionNode); + functionNode.setState(CompilationState.FINALIZED); + return functionNode; } @@ -511,7 +514,6 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leave(final VarNode varNode) { - final Node rhs = varNode.getInit(); if (rhs != null) { Type destType = specialize(varNode); diff --git a/src/jdk/nashorn/internal/codegen/FoldConstants.java b/src/jdk/nashorn/internal/codegen/FoldConstants.java index 4ea53a04..af2b02ab 100644 --- a/src/jdk/nashorn/internal/codegen/FoldConstants.java +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java @@ -30,11 +30,13 @@ import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ExecuteNode; +import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.UnaryNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.JSType; @@ -71,6 +73,20 @@ final class FoldConstants extends NodeVisitor { return binaryNode; } + @Override + public Node enter(final FunctionNode functionNode) { + if (functionNode.isLazy()) { + return null; + } + return functionNode; + } + + @Override + public Node leave(final FunctionNode functionNode) { + functionNode.setState(CompilationState.CONSTANT_FOLDED); + return functionNode; + } + @Override public Node leave(final IfNode ifNode) { final Node test = ifNode.getTest(); diff --git a/src/jdk/nashorn/internal/codegen/FunctionSignature.java b/src/jdk/nashorn/internal/codegen/FunctionSignature.java index ded12b0c..5f3740a7 100644 --- a/src/jdk/nashorn/internal/codegen/FunctionSignature.java +++ b/src/jdk/nashorn/internal/codegen/FunctionSignature.java @@ -146,7 +146,7 @@ public final class FunctionSignature { /** * Create a function signature given a function node, using as much - * type information for parameters and return types that is availabe + * type information for parameters and return types that is available * * @param functionNode the function node */ @@ -202,6 +202,14 @@ public final class FunctionSignature { return methodType; } + /** + * Return the return type for this function signature + * @return the return type + */ + public Type getReturnType() { + return returnType; + } + private static Type[] objectArgs(final int nArgs) { final Type[] array = new Type[nArgs]; for (int i = 0; i < nArgs; i++) { diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java index 7a83469b..4be28193 100644 --- a/src/jdk/nashorn/internal/codegen/Lower.java +++ b/src/jdk/nashorn/internal/codegen/Lower.java @@ -69,6 +69,7 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Token; @@ -332,6 +333,8 @@ final class Lower extends NodeOperatorVisitor { LOG.info("END FunctionNode: " + functionNode.getName()); unnest(functionNode); + functionNode.setState(CompilationState.LOWERED); + return null; } @@ -636,7 +639,7 @@ final class Lower extends NodeOperatorVisitor { } else if (conservativeAlwaysTrue(test)) { node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish()); ((ForNode)node).setBody(body); - ((ForNode)node).accept(this); + node.accept(this); setTerminal(node, !escapes); } } diff --git a/src/jdk/nashorn/internal/codegen/Splitter.java b/src/jdk/nashorn/internal/codegen/Splitter.java index b92f2383..b338ced5 100644 --- a/src/jdk/nashorn/internal/codegen/Splitter.java +++ b/src/jdk/nashorn/internal/codegen/Splitter.java @@ -39,6 +39,7 @@ import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.DoWhileNode; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; @@ -92,15 +93,16 @@ final class Splitter extends NodeVisitor { */ void split() { if (functionNode.isLazy()) { - LOG.fine("Postponing split of '" + functionNode.getName() + "' as it's lazy"); + LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy"); return; } - LOG.fine("Initiating split of '" + functionNode.getName() + "'"); + + LOG.finest("Initiating split of '" + functionNode.getName() + "'"); long weight = WeighNodes.weigh(functionNode); if (weight >= SPLIT_THRESHOLD) { - LOG.fine("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD); + LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD); functionNode.accept(this); @@ -133,6 +135,8 @@ final class Splitter extends NodeVisitor { for (final FunctionNode function : functionNode.getFunctions()) { new Splitter(compiler, function, outermostCompileUnit).split(); } + + functionNode.setState(CompilationState.SPLIT); } /** diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java index d36dd20d..77558835 100644 --- a/src/jdk/nashorn/internal/codegen/types/Type.java +++ b/src/jdk/nashorn/internal/codegen/types/Type.java @@ -647,21 +647,20 @@ public abstract class Type implements Comparable, BytecodeOps { } private static void swap(final MethodVisitor method, final Type above, final Type below) { - final MethodVisitor mv = method; if (below.isCategory2()) { if (above.isCategory2()) { - mv.visitInsn(DUP2_X2); - mv.visitInsn(POP2); + method.visitInsn(DUP2_X2); + method.visitInsn(POP2); } else { - mv.visitInsn(DUP_X2); - mv.visitInsn(POP); + method.visitInsn(DUP_X2); + method.visitInsn(POP); } } else { if (above.isCategory2()) { - mv.visitInsn(DUP2_X1); - mv.visitInsn(POP2); + method.visitInsn(DUP2_X1); + method.visitInsn(POP2); } else { - mv.visitInsn(SWAP); + method.visitInsn(SWAP); } } diff --git a/src/jdk/nashorn/internal/ir/Block.java b/src/jdk/nashorn/internal/ir/Block.java index 9449be5b..e729d307 100644 --- a/src/jdk/nashorn/internal/ir/Block.java +++ b/src/jdk/nashorn/internal/ir/Block.java @@ -38,26 +38,25 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import jdk.nashorn.internal.codegen.Frame; import jdk.nashorn.internal.codegen.Label; -import jdk.nashorn.internal.ir.annotations.Ignore; -import jdk.nashorn.internal.ir.annotations.ParentNode; +import jdk.nashorn.internal.ir.annotations.Reference; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; /** * IR representation for a list of statements and functions. All provides the * basis for script body. - * */ public class Block extends Node { /** Parent context */ - @ParentNode @Ignore + @Reference private Block parent; - /** Owning function. */ - @Ignore //don't print it, it is apparent in the tree + /** Owning function - a FunctionNode has itself as function */ + @Reference protected FunctionNode function; /** List of statements */ @@ -273,6 +272,14 @@ public class Block extends Node { return this; } + /** + * Get an iterator for all the symbols defined in this block + * @return symbol iterator + */ + public Iterator symbolIterator() { + return symbols.values().iterator(); + } + /** * Search for symbol. * diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java index d928aa71..63590932 100644 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java @@ -33,8 +33,10 @@ import static jdk.nashorn.internal.ir.Symbol.IS_TEMP; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Stack; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; @@ -45,13 +47,13 @@ import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Parser; +import jdk.nashorn.internal.runtime.Debug; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.UserAccessorProperty; import jdk.nashorn.internal.runtime.linker.LinkerCallSite; /** * IR representation for function (or script.) - * */ public class FunctionNode extends Block { @@ -86,7 +88,9 @@ public class FunctionNode extends Block { /** method has had its types finalized */ FINALIZED, /** method has been emitted to bytecode */ - EMITTED + EMITTED, + /** code installed in a class loader */ + INSTALLED } /** External function identifier. */ @@ -173,6 +177,9 @@ public class FunctionNode extends Block { @Ignore private final EnumSet compilationState; + /** Type hints, e.g based on parameters at call site */ + private final Map specializedTypes; + /** Function flags. */ private int flags; @@ -256,22 +263,27 @@ public class FunctionNode extends Block { // constructed, so it can't be seen from other threads. this.function = this; this.compilationState = EnumSet.of(CompilationState.INITIALIZED); + this.specializedTypes = new HashMap<>(); } @SuppressWarnings("LeakingThisInConstructor") private FunctionNode(final FunctionNode functionNode, final CopyState cs) { super(functionNode, cs); + this.functions = new ArrayList<>(); + for (final FunctionNode f : functionNode.getFunctions()) { + this.functions.add((FunctionNode)cs.existingOrCopy(f)); + } + this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident); this.name = functionNode.name; this.kind = functionNode.kind; this.parameters = new ArrayList<>(); for (final IdentNode param : functionNode.getParameters()) { - this.parameters.add((IdentNode) cs.existingOrCopy(param)); + this.parameters.add((IdentNode)cs.existingOrCopy(param)); } - this.functions = new ArrayList<>(); this.firstToken = functionNode.firstToken; this.lastToken = functionNode.lastToken; this.namespace = functionNode.getNamespace(); @@ -300,6 +312,7 @@ public class FunctionNode extends Block { this.function = this; this.compilationState = EnumSet.copyOf(functionNode.compilationState); + this.specializedTypes = new HashMap<>(); } @Override @@ -710,10 +723,14 @@ public class FunctionNode extends Block { * Check if this function's generated Java method needs a {@code callee} parameter. Functions that need access to * their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object * (since it exposes {@code arguments.callee} property) will need to have a callee parameter. + * + * We also conservatively need a callee if we have lazy children, i.e. nested function nodes that have not yet + * been evaluated. _They_ may need the callee and we don't know it + * * @return true if the function's generated Java method needs a {@code callee} parameter. */ public boolean needsCallee() { - return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode()); + return hasLazyChildren() || needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode()); } /** @@ -918,6 +935,27 @@ public class FunctionNode extends Block { this.parameters = parameters; } + /** + * Get a specialized type for an identity, if one exists + * @param node node to check specialized type for + * @return null if no specialization exists, otherwise type + */ + public Type getSpecializedType(final IdentNode node) { + return specializedTypes.get(node); + } + + /** + * Set parameter type hints for specialization. + * @param types types array of length equal to parameter list size + */ + public void setParameterTypes(final Class[] types) { + assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types"; + //diff - skip the callee and this etc, they are not explicit params in the parse tree + for (int i = 0; i < types.length ; i++) { + specializedTypes.put(parameters.get(i), Type.typeFor(types[i])); + } + } + /** * Get the identifier for the variable in which the function return value * should be stored @@ -1266,7 +1304,7 @@ public class FunctionNode extends Block { * @param parentBlock a block to remember as parent */ public void addReferencingParentBlock(final Block parentBlock) { - assert parentBlock.getFunction() == function.findParentFunction(); // all parent blocks must be in the same function + assert parentBlock.getFunction() == function.findParentFunction() : Debug.id(parentBlock.getFunction()) + "!=" + Debug.id(function.findParentFunction()); // all parent blocks must be in the same function if (parentBlock != function.getParent()) { if (referencingParentBlocks == null) { referencingParentBlocks = new LinkedList<>(); diff --git a/src/jdk/nashorn/internal/ir/ObjectNode.java b/src/jdk/nashorn/internal/ir/ObjectNode.java index ab7e49e4..8b720de4 100644 --- a/src/jdk/nashorn/internal/ir/ObjectNode.java +++ b/src/jdk/nashorn/internal/ir/ObjectNode.java @@ -28,7 +28,6 @@ package jdk.nashorn.internal.ir; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; @@ -36,9 +35,6 @@ import jdk.nashorn.internal.runtime.Source; * IR representation of an object literal. */ public class ObjectNode extends Node { - /** Literal context. */ - @Ignore - private Block context; /** Literal elements. */ private final List elements; @@ -49,13 +45,11 @@ public class ObjectNode extends Node { * @param source the source * @param token token * @param finish finish - * @param context the block for this ObjectNode * @param elements the elements used to initialize this ObjectNode */ - public ObjectNode(final Source source, final long token, final int finish, final Block context, final List elements) { + public ObjectNode(final Source source, final long token, final int finish, final List elements) { super(source, token, finish); - this.context = context; this.elements = elements; } @@ -68,7 +62,6 @@ public class ObjectNode extends Node { newElements.add(cs.existingOrCopy(element)); } - this.context = (Block)cs.existingOrCopy(objectNode.context); this.elements = newElements; } @@ -80,10 +73,6 @@ public class ObjectNode extends Node { @Override public Node accept(final NodeVisitor visitor) { if (visitor.enter(this) != null) { - if (context != null) { - context = (Block)context.accept(visitor); - } - for (int i = 0, count = elements.size(); i < count; i++) { elements.set(i, elements.get(i).accept(visitor)); } @@ -116,14 +105,6 @@ public class ObjectNode extends Node { sb.append('}'); } - /** - * Get the block that is this ObjectNode's literal context - * @return the block - */ - public Block getContext() { - return context; - } - /** * Get the elements of this literal node * @return a list of elements diff --git a/src/jdk/nashorn/internal/ir/UnaryNode.java b/src/jdk/nashorn/internal/ir/UnaryNode.java index 7b608edf..491151fa 100644 --- a/src/jdk/nashorn/internal/ir/UnaryNode.java +++ b/src/jdk/nashorn/internal/ir/UnaryNode.java @@ -152,9 +152,9 @@ public class UnaryNode extends Node implements Assignment { if (isConvert) { convertPos = sb.length(); - sb.append("(("); + sb.append("("); sb.append(getType()); - sb.append(")"); + sb.append(")("); } if (!isPostfix && !isConvert) { @@ -191,8 +191,6 @@ public class UnaryNode extends Node implements Assignment { sb.setCharAt(convertPos, ' '); } } - - //TODO - conversions still have too many parenthesis - makes --print-lower-parse hard to read } /** diff --git a/src/jdk/nashorn/internal/ir/annotations/ChildNode.java b/src/jdk/nashorn/internal/ir/annotations/ChildNode.java deleted file mode 100644 index 181c63d8..00000000 --- a/src/jdk/nashorn/internal/ir/annotations/ChildNode.java +++ /dev/null @@ -1,42 +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.ir.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * This is a child node, a real node, not a reference, to an IR node that should - * be traversed. - *

- * TODO Currently not in use. Would make e.g. accept methods simple and unified - * @see jdk.nashorn.internal.ir.Node - */ -@Retention(value=RetentionPolicy.RUNTIME) -public @interface ChildNode { - /** order of traversal compared to other children */ - public int order() default -1; -} diff --git a/src/jdk/nashorn/internal/ir/annotations/ParentNode.java b/src/jdk/nashorn/internal/ir/annotations/ParentNode.java deleted file mode 100644 index 6a9de3a4..00000000 --- a/src/jdk/nashorn/internal/ir/annotations/ParentNode.java +++ /dev/null @@ -1,44 +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.ir.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Signifies a parent of a node, i.e. node that should not be traversed if we - * go down the AST. In automatic parsing this can be handled by @Reference - * annotations instead, as all parents are references. - *

- * TODO The use case is automating and creating one implementation of something like - * Node.getParent() - * - * @see jdk.nashorn.internal.ir.Node - */ -@Retention(value=RetentionPolicy.RUNTIME) -public @interface ParentNode { - // EMPTY -} diff --git a/src/jdk/nashorn/internal/ir/annotations/Reference.java b/src/jdk/nashorn/internal/ir/annotations/Reference.java index 1dd002c1..20c8ffca 100644 --- a/src/jdk/nashorn/internal/ir/annotations/Reference.java +++ b/src/jdk/nashorn/internal/ir/annotations/Reference.java @@ -33,7 +33,6 @@ import java.lang.annotation.RetentionPolicy; * AST traversal and cloning. Cloning currently as a rule uses * existingOrSame for references and otherwise existingOrCopy *

- * TODO this could probably be automated using the @Reference annotation. */ @Retention(value=RetentionPolicy.RUNTIME) diff --git a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java index 3c66aa12..7bbe3836 100644 --- a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java +++ b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java @@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir.debug; import java.lang.reflect.Field; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.Iterator; @@ -36,10 +37,10 @@ import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.annotations.Ignore; -import jdk.nashorn.internal.ir.annotations.ParentNode; import jdk.nashorn.internal.ir.annotations.Reference; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.Debug; /** * AST-as-text visualizer. Sometimes you want tree form and not source @@ -47,7 +48,6 @@ import jdk.nashorn.internal.runtime.Context; * * see the flags --print-ast and --print-ast-lower */ - public final class ASTWriter { /** Root node from which to start the traversal */ private final Node root; @@ -71,12 +71,22 @@ public final class ASTWriter { @Override public String toString() { final StringBuilder sb = new StringBuilder(); - printAST(sb, null, "root", root, 0); + printAST(sb, null, null, "root", root, 0); return sb.toString(); } + /** + * Return the visited nodes in an ordered list + * @return the list of nodes in order + */ + public Node[] toArray() { + final List preorder = new ArrayList<>(); + printAST(new StringBuilder(), preorder, null, "root", root, 0); + return preorder.toArray(new Node[preorder.size()]); + } + @SuppressWarnings("unchecked") - private void printAST(final StringBuilder sb, final Field field, final String name, final Node node, final int indent) { + private void printAST(final StringBuilder sb, final List preorder, final Field field, final String name, final Node node, final int indent) { ASTWriter.indent(sb, indent); if (node == null) { sb.append("[Object "); @@ -85,13 +95,23 @@ public final class ASTWriter { return; } + if (preorder != null) { + preorder.add(node); + } + final boolean isReference = field != null && field.getAnnotation(Reference.class) != null; Class clazz = node.getClass(); String type = clazz.getName(); type = type.substring(type.lastIndexOf('.') + 1, type.length()); -// type += "@" + Debug.id(node) + "#" + node.getSymbol(); + if (isReference) { + type = "ref: " + type; + } + type += "@" + Debug.id(node); + if (node.getSymbol() != null) { + type += "#" + node.getSymbol(); + } final List children = new LinkedList<>(); @@ -153,9 +173,7 @@ public final class ASTWriter { append('\n'); for (final Field child : children) { - if (child.getAnnotation(ParentNode.class) != null) { - continue; - } else if (child.getAnnotation(Ignore.class) != null) { + if (child.getAnnotation(Ignore.class) != null) { continue; } @@ -168,7 +186,7 @@ public final class ASTWriter { } if (value instanceof Node) { - printAST(sb, child, child.getName(), (Node)value, indent + 1); + printAST(sb, preorder, child, child.getName(), (Node)value, indent + 1); } else if (value instanceof Collection) { int pos = 0; ASTWriter.indent(sb, indent + 1); @@ -180,7 +198,7 @@ public final class ASTWriter { append('\n'); for (final Node member : (Collection)value) { - printAST(sb, child, child.getName() + "[" + pos++ + "]", member, indent + 2); + printAST(sb, preorder, child, child.getName() + "[" + pos++ + "]", member, indent + 2); } } } diff --git a/src/jdk/nashorn/internal/objects/NativeArray.java b/src/jdk/nashorn/internal/objects/NativeArray.java index 31c1d972..16e237ba 100644 --- a/src/jdk/nashorn/internal/objects/NativeArray.java +++ b/src/jdk/nashorn/internal/objects/NativeArray.java @@ -605,7 +605,7 @@ public final class NativeArray extends ScriptObject { final boolean strict = sobj.isStrictContext(); if (bulkable(sobj)) { - return ((NativeArray)sobj).getArray().pop(); + return sobj.getArray().pop(); } final long len = JSType.toUint32(sobj.getLength()); @@ -725,7 +725,7 @@ public final class NativeArray extends ScriptObject { first = sobj.get(0); if (bulkable(sobj)) { - ((NativeArray) sobj).getArray().shiftLeft(1); + sobj.getArray().shiftLeft(1); } else { for (long k = 1; k < len; k++) { sobj.set(k - 1, sobj.get(k), strict); diff --git a/src/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk/nashorn/internal/objects/NativeDebug.java index 8188f426..b311981e 100644 --- a/src/jdk/nashorn/internal/objects/NativeDebug.java +++ b/src/jdk/nashorn/internal/objects/NativeDebug.java @@ -161,21 +161,6 @@ public final class NativeDebug extends ScriptObject { return UNDEFINED; } - /** - * Nashorn extension: get invocation handle from {@link ScriptFunction} - * - * @param self self reference - * @param obj script function - * @return the invocation handle for the given ScriptFunction - */ - @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object methodHandle(final Object self, final Object obj) { - if (obj instanceof ScriptFunction) { - return ((ScriptFunction)obj).getInvokeHandle(); - } - return UNDEFINED; - } - /** * Check object identity comparison regardless of type * diff --git a/src/jdk/nashorn/internal/objects/NativeError.java b/src/jdk/nashorn/internal/objects/NativeError.java index 8cea0c70..433f9317 100644 --- a/src/jdk/nashorn/internal/objects/NativeError.java +++ b/src/jdk/nashorn/internal/objects/NativeError.java @@ -317,7 +317,7 @@ public final class NativeError extends ScriptObject { return name; } // Step 10 : return name + ": " + msg - return (String)name + ": " + (String)msg; + return name + ": " + msg; } private static MethodHandle findOwnMH(final String name, final Class rtype, final Class... types) { diff --git a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java index 0514bd25..46b353f1 100644 --- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java +++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java @@ -31,6 +31,7 @@ import java.lang.invoke.MethodHandle; 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; @@ -86,8 +87,8 @@ public class ScriptFunctionImpl extends ScriptFunction { * @param builtin is this a built-in function * @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted). */ - ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) { - super(name, methodHandle, getMap(strict), scope, specs, strict, builtin, isConstructor); + ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { + super(name, methodHandle, getMap(isStrict), scope, specs, isStrict, isBuiltin, isConstructor); init(); } @@ -95,14 +96,10 @@ public class ScriptFunctionImpl extends ScriptFunction { * Constructor called by (compiler) generated code for {@link ScriptObject}s. * * @param data static function data - * @param methodHandle handle for invocation * @param scope scope object - * @param allocator instance constructor for function */ - public ScriptFunctionImpl(final MethodHandle methodHandle, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) { + public ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope) { super(data, getMap(data.isStrict()), scope); - // Set method handles in script data - data.setMethodHandles(methodHandle, allocator); init(); } diff --git a/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java deleted file mode 100644 index 4d3df509..00000000 --- a/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java +++ /dev/null @@ -1,122 +0,0 @@ -package jdk.nashorn.internal.objects; - -import static jdk.nashorn.internal.lookup.Lookup.MH; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import jdk.nashorn.internal.codegen.CompilationException; -import jdk.nashorn.internal.codegen.Compiler; -import jdk.nashorn.internal.codegen.FunctionSignature; -import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.runtime.CodeInstaller; -import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.ScriptEnvironment; -import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptFunctionData; -import jdk.nashorn.internal.runtime.ScriptObject; - -/** - * A trampoline is a promise to compile a {@link ScriptFunction} later. It just looks like - * the call to the script function, but when invoked it will compile the script function - * (in a new compile unit) and invoke it - */ -public final class ScriptFunctionTrampolineImpl extends ScriptFunctionImpl { - - private CodeInstaller installer; - - /** Function node to lazily recompile when trampoline is hit */ - private FunctionNode functionNode; - - /** - * Constructor - * - * @param installer opaque code installer from context - * @param functionNode function node to lazily compile when trampoline is hit - * @param data {@link ScriptFunctionData} for function - * @param scope scope - * @param allocator allocator - */ - public ScriptFunctionTrampolineImpl(final CodeInstaller installer, final FunctionNode functionNode, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) { - super(null, data, scope, allocator); - - this.installer = installer; - this.functionNode = functionNode; - - data.setMethodHandles(makeTrampoline(), allocator); - } - - private final MethodHandle makeTrampoline() { - final MethodType mt = - new FunctionSignature( - true, - functionNode.needsCallee(), - Type.OBJECT, - functionNode.getParameters().size()). - getMethodType(); - - return - MH.bindTo( - MH.asCollector( - findOwnMH( - "trampoline", - Object.class, - Object[].class), - Object[].class, - mt.parameterCount()), - this); - } - - private static MethodHandle findOwnMH(final String name, final Class rtype, final Class... types) { - return MH.findVirtual(MethodHandles.lookup(), ScriptFunctionTrampolineImpl.class, name, MH.type(rtype, types)); - } - - @Override - protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) { - //prevent trampoline recompilation cycle if a function is bound before use - compile(); - return super.makeBoundFunction(data); - } - - private MethodHandle compile() throws CompilationException { - final Compiler compiler = new Compiler(installer, functionNode); - - compiler.compile(); - - final Class clazz = compiler.install(); - /* compute function signature for lazy method. this can be done first after compilation, as only then do we know - * the final state about callees, scopes and specialized parameter types */ - final FunctionSignature signature = new FunctionSignature(true, functionNode.needsCallee(), Type.OBJECT, functionNode.getParameters().size()); - final MethodType mt = signature.getMethodType(); - - MethodHandle mh = MH.findStatic(MethodHandles.publicLookup(), clazz, functionNode.getName(), mt); - if (functionNode.needsCallee()) { - mh = MH.bindTo(mh, this); - } - - // now the invoker method looks like the one our superclass is expecting - resetInvoker(mh); - - return mh; - } - - @SuppressWarnings("unused") - private Object trampoline(final Object... args) throws CompilationException { - Compiler.LOG.info(">>> TRAMPOLINE: Hitting trampoline for '" + functionNode.getName() + "'"); - MethodHandle mh = compile(); - - Compiler.LOG.info("<<< COMPILED TO: " + mh); - // spread the array to invididual args of the correct type - mh = MH.asSpreader(mh, Object[].class, mh.type().parameterCount()); - - try { - //invoke the real method the trampoline points to. this only happens once - return mh.invoke(args); - } catch (final RuntimeException | Error e) { - throw e; - } catch (final Throwable t) { - throw new RuntimeException(t); - } - } -} diff --git a/src/jdk/nashorn/internal/parser/JSONParser.java b/src/jdk/nashorn/internal/parser/JSONParser.java index 39cf549e..5468ca3d 100644 --- a/src/jdk/nashorn/internal/parser/JSONParser.java +++ b/src/jdk/nashorn/internal/parser/JSONParser.java @@ -313,7 +313,7 @@ loop: } // Construct new object literal. - return new ObjectNode(source, objectToken, finish, null, elements); + return new ObjectNode(source, objectToken, finish, elements); } /** diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java index 41149e4b..bfc96abf 100644 --- a/src/jdk/nashorn/internal/parser/Parser.java +++ b/src/jdk/nashorn/internal/parser/Parser.java @@ -59,6 +59,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Stack; + import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.Namespace; import jdk.nashorn.internal.ir.AccessNode; @@ -96,7 +97,7 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSErrorType; @@ -116,9 +117,6 @@ public class Parser extends AbstractParser { /** Is scripting mode. */ private final boolean scripting; - /** Top level script being parsed. */ - private FunctionNode script; - /** Current function being parsed. */ private FunctionNode function; @@ -304,6 +302,7 @@ loop: final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, block, ident, name); block = function = functionBlock; function.setStrictMode(isStrictMode); + function.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED); return functionBlock; } @@ -312,7 +311,7 @@ loop: * Restore the current block. */ private void restoreBlock() { - block = block.getParent(); + block = block.getParent(); function = block.getFunction(); } @@ -338,7 +337,7 @@ loop: popControlNode(); } - final int possibleEnd = Token.descPosition(token) + Token.descLength(token); + final int possibleEnd = Token.descPosition(token) + Token.descLength(token); // Block closing brace. if (needsBraces) { @@ -438,7 +437,7 @@ loop: } if (lhs instanceof IdentNode) { - if (! checkIdentLValue((IdentNode)lhs)) { + if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, rhs); } verifyStrictIdent((IdentNode)lhs, "assignment"); @@ -621,15 +620,13 @@ loop: // Make a fake token for the script. final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength()); // Set up the script to append elements. - script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName)); - // set kind to be SCRIPT + + final FunctionNode script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName)); + script.setKind(FunctionNode.Kind.SCRIPT); - // Set the first token of the script. script.setFirstToken(functionToken); - // Gather source elements. sourceElements(); expect(EOF); - // Set the last token of the script. script.setLastToken(token); script.setFinish(source.getLength() - 1); @@ -1231,7 +1228,7 @@ loop: } if (init instanceof IdentNode) { - if (! checkIdentLValue((IdentNode)init)) { + if (!checkIdentLValue((IdentNode)init)) { error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken()); } verifyStrictIdent((IdentNode)init, "for-in iterator"); @@ -2026,7 +2023,7 @@ loop: break; default: - if (! elision) { + if (!elision) { error(AbstractParser.message("expected.comma", type.getNameOrType())); } // Add expression element. @@ -2067,15 +2064,11 @@ loop: next(); // Object context. - Block objectContext = null; // Prepare to accumulate elements. final List elements = new ArrayList<>(); final Map map = new HashMap<>(); - try { - // Create a block for the object literal. - objectContext = newBlock(); - + // Create a block for the object literal. boolean commaSeen = true; loop: while (true) { @@ -2090,97 +2083,90 @@ loop: break; default: - if (! commaSeen) { + if (!commaSeen) { error(AbstractParser.message("expected.comma", type.getNameOrType())); - } - - commaSeen = false; - // Get and add the next property. - final PropertyNode property = propertyAssignment(); - final Object key = property.getKeyName(); - final PropertyNode existingProperty = map.get(key); - - if (existingProperty != null) { - // ECMA section 11.1.5 Object Initialiser - // point # 4 on property assignment production - final Node value = property.getValue(); - final Node getter = property.getGetter(); - final Node setter = property.getSetter(); - - final Node prevValue = existingProperty.getValue(); - final Node prevGetter = existingProperty.getGetter(); - final Node prevSetter = existingProperty.getSetter(); - - boolean redefinitionOk = true; - // ECMA 11.1.5 strict mode restrictions - if (isStrictMode) { - if (value != null && prevValue != null) { - redefinitionOk = false; - } - } - - final boolean isPrevAccessor = prevGetter != null || prevSetter != null; - final boolean isAccessor = getter != null || setter != null; + } - // data property redefined as accessor property - if (prevValue != null && isAccessor) { + commaSeen = false; + // Get and add the next property. + final PropertyNode property = propertyAssignment(); + final Object key = property.getKeyName(); + final PropertyNode existingProperty = map.get(key); + + if (existingProperty != null) { + // ECMA section 11.1.5 Object Initialiser + // point # 4 on property assignment production + final Node value = property.getValue(); + final Node getter = property.getGetter(); + final Node setter = property.getSetter(); + + final Node prevValue = existingProperty.getValue(); + final Node prevGetter = existingProperty.getGetter(); + final Node prevSetter = existingProperty.getSetter(); + + boolean redefinitionOk = true; + // ECMA 11.1.5 strict mode restrictions + if (isStrictMode) { + if (value != null && prevValue != null) { redefinitionOk = false; } + } - // accessor property redefined as data - if (isPrevAccessor && value != null) { - redefinitionOk = false; - } + final boolean isPrevAccessor = prevGetter != null || prevSetter != null; + final boolean isAccessor = getter != null || setter != null; - if (isAccessor && isPrevAccessor) { - if (getter != null && prevGetter != null || - setter != null && prevSetter != null) { - redefinitionOk = false; - } - } + // data property redefined as accessor property + if (prevValue != null && isAccessor) { + redefinitionOk = false; + } - if (! redefinitionOk) { - error(AbstractParser.message("property.redefinition", key.toString()), property.getToken()); + // accessor property redefined as data + if (isPrevAccessor && value != null) { + redefinitionOk = false; + } + + if (isAccessor && isPrevAccessor) { + if (getter != null && prevGetter != null || + setter != null && prevSetter != null) { + redefinitionOk = false; } + } - if (value != null) { - final Node existingValue = existingProperty.getValue(); + if (!redefinitionOk) { + error(AbstractParser.message("property.redefinition", key.toString()), property.getToken()); + } - if (existingValue == null) { - existingProperty.setValue(value); - } else { - final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT); - existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value)); - } + if (value != null) { + final Node existingValue = existingProperty.getValue(); - existingProperty.setGetter(null); - existingProperty.setSetter(null); + if (existingValue == null) { + existingProperty.setValue(value); + } else { + final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT); + existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value)); } - if (getter != null) { - existingProperty.setGetter(getter); - } + existingProperty.setGetter(null); + existingProperty.setSetter(null); + } - if (setter != null) { - existingProperty.setSetter(setter); - } - } else { - map.put(key, property); - elements.add(property); + if (getter != null) { + existingProperty.setGetter(getter); } - break; + if (setter != null) { + existingProperty.setSetter(setter); + } + } else { + map.put(key, property); + elements.add(property); } + + break; } - } finally { - restoreBlock(); } - // Construct new object literal. - objectContext.setFinish(finish); - objectContext.setStart(Token.descPosition(objectToken)); - - return new ObjectNode(source, objectToken, finish, objectContext, elements); + return new ObjectNode(source, objectToken, finish, elements); } /** @@ -2845,7 +2831,7 @@ loop: } if (lhs instanceof IdentNode) { - if (! checkIdentLValue((IdentNode)lhs)) { + if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, null); } verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); @@ -2872,7 +2858,7 @@ loop: return referenceError(lhs, null); } if (lhs instanceof IdentNode) { - if (! checkIdentLValue((IdentNode)lhs)) { + if (!checkIdentLValue((IdentNode)lhs)) { next(); return referenceError(lhs, null); } diff --git a/src/jdk/nashorn/internal/runtime/AccessorProperty.java b/src/jdk/nashorn/internal/runtime/AccessorProperty.java index 9f6867dd..996bea35 100644 --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java @@ -164,7 +164,6 @@ public class AccessorProperty extends Property { super(key, flags, slot); /* - * * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also * works in dual field mode, it only means that the property never has a primitive * representation. @@ -348,11 +347,10 @@ public class AccessorProperty extends Property { private MethodHandle debug(final MethodHandle mh, final Class forType, final Class type, final String tag) { if (DEBUG_FIELDS) { - final MethodHandle mhd = MethodHandleFactory.addDebugPrintout( + return MethodHandleFactory.addDebugPrintout( LOG, mh, tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", forType=" + stripName(forType) + ", type=" + stripName(type) + ')'); - return mhd; } return mh; } diff --git a/src/jdk/nashorn/internal/runtime/CodeInstaller.java b/src/jdk/nashorn/internal/runtime/CodeInstaller.java index 5fd16528..80fac179 100644 --- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java +++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java @@ -25,6 +25,8 @@ package jdk.nashorn.internal.runtime; +import jdk.nashorn.internal.codegen.ClassEmitter; + /** * Interface for installing classes passed to the compiler. * As only the code generating package (i.e. Context) knows about @@ -52,12 +54,12 @@ public interface CodeInstaller { */ public Class install(final String className, final byte[] bytecode); - /* + /** * Verify generated bytecode before emission. This is called back from the * {@link ClassEmitter} or the {@link Compiler}. If the "--verify-code" parameter * hasn't been given, this is a nop * - * @param bytecode bytecode to verify + * @param code bytecode to verify */ public void verify(final byte[] code); } diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunction.java b/src/jdk/nashorn/internal/runtime/CompiledFunction.java new file mode 100644 index 00000000..3cc9f09d --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java @@ -0,0 +1,162 @@ +/* + * 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 java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +import jdk.nashorn.internal.codegen.types.Type; + +/** + * An version of a JavaScript function, native or JavaScript. + * Supports lazily generating a constructor version of the invocation. + */ +final class CompiledFunction implements Comparable { + + private final MethodHandle invoker; + private MethodHandle constructor; + + CompiledFunction(final MethodHandle invoker) { + this(invoker, null); + } + + CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) { + this.invoker = invoker; + this.constructor = constructor; //isConstructor + } + + @Override + public String toString() { + return ""; + } + + MethodHandle getInvoker() { + return invoker; + } + + MethodHandle getConstructor() { + return constructor; + } + + void setConstructor(final MethodHandle constructor) { + this.constructor = constructor; + } + + boolean hasConstructor() { + return constructor != null; + } + + MethodType type() { + return invoker.type(); + } + + @Override + public int compareTo(final CompiledFunction o) { + return weight() - o.weight(); + } + + private int weight() { + return weight(type()); + } + + private static int weight(final MethodType type) { + if (isVarArgsType(type)) { + return Integer.MAX_VALUE; //if there is a varargs it should be the heavist and last fallback + } + + int weight = Type.typeFor(type.returnType()).getWeight(); + for (final Class paramType : type.parameterArray()) { + final int pweight = Type.typeFor(paramType).getWeight(); + weight += pweight; + } + return weight; + } + + private static boolean isVarArgsType(final MethodType type) { + assert type.parameterCount() >= 1 : type; + return type.parameterType(type.parameterCount() - 1) == Object[].class; + } + + boolean moreGenericThan(final CompiledFunction o) { + return weight() > o.weight(); + } + + boolean moreGenericThan(final MethodType type) { + return weight() > weight(type); + } + + /** + * Check whether a given method descriptor is compatible with this invocation. + * It is compatible if the types are narrower than the invocation type so that + * a semantically equivalent linkage can be performed. + * + * @param typesc + * @return + */ + boolean typeCompatible(final MethodType type) { + final Class[] wantedParams = type.parameterArray(); + final Class[] existingParams = type().parameterArray(); + + //if we are not examining a varargs type, the number of parameters must be the same + if (wantedParams.length != existingParams.length && !isVarArgsType(type)) { + return false; + } + + //we only go as far as the shortest array. the only chance to make this work if + //parameters lengths do not match is if our type ends with a varargs argument. + //then every trailing parameter in the given callsite can be folded into it, making + //us compatible (albeit slower than a direct specialization) + final int lastParamIndex = Math.min(wantedParams.length, existingParams.length); + for (int i = 0; i < lastParamIndex; i++) { + final Type w = Type.typeFor(wantedParams[i]); + final Type e = Type.typeFor(existingParams[i]); + + //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution + //we also currently don't support boolean as a javascript function callsite type. + //it will always box. + if (w.isBoolean()) { + return false; + } + + //This callsite type has a vararg here. it will swallow all remaining args. + //for consistency, check that it's the last argument + if (e.isArray()) { + return true; + } + + //Our arguments must be at least as wide as the wanted one, if not wider + if (Type.widest(w, e) != e) { + //e.g. this invocation takes double and callsite says "object". reject. won't fit + //but if invocation takes a double and callsite says "int" or "long" or "double", that's fine + return false; + } + } + + return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic. + } + + + +} diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunctions.java b/src/jdk/nashorn/internal/runtime/CompiledFunctions.java new file mode 100644 index 00000000..ff660d3d --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/CompiledFunctions.java @@ -0,0 +1,73 @@ +/* + * 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 java.lang.invoke.MethodType; +import java.util.Iterator; +import java.util.TreeSet; + +/** + * This is a list of code versions of a function. + * The list is sorted in ascending order of generic descriptors + */ +@SuppressWarnings("serial") +final class CompiledFunctions extends TreeSet { + + CompiledFunction best(final MethodType type) { + final Iterator iter = iterator(); + while (iter.hasNext()) { + final CompiledFunction next = iter.next(); + if (next.typeCompatible(type)) { + return next; + } + } + return mostGeneric(); + } + + boolean needsCallee() { + for (final CompiledFunction inv : this) { + assert ScriptFunctionData.needsCallee(inv.getInvoker()) == ScriptFunctionData.needsCallee(mostGeneric().getInvoker()); + } + return ScriptFunctionData.needsCallee(mostGeneric().getInvoker()); + } + + CompiledFunction mostGeneric() { + return last(); + } + + /** + * Is the given type even more specific than this entire list? That means + * we have an opportunity for more specific versions of the method + * through lazy code generation + * + * @param type type to check against + * @return true if the given type is more specific than all invocations available + */ + boolean isLessSpecificThan(final MethodType type) { + return best(type).moreGenericThan(type); + } + + +} diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java index 516c0a79..151615c0 100644 --- a/src/jdk/nashorn/internal/runtime/Context.java +++ b/src/jdk/nashorn/internal/runtime/Context.java @@ -80,7 +80,7 @@ public final class Context { /** * Return the context for this installer - * @return context + * @return ScriptEnvironment */ @Override public ScriptEnvironment getOwner() { diff --git a/src/jdk/nashorn/internal/runtime/ECMAException.java b/src/jdk/nashorn/internal/runtime/ECMAException.java index a07fb580..a32e721c 100644 --- a/src/jdk/nashorn/internal/runtime/ECMAException.java +++ b/src/jdk/nashorn/internal/runtime/ECMAException.java @@ -237,7 +237,7 @@ public final class ECMAException extends NashornException { return (String)name; } - return (String)name + ": " + (String)msg; + return name + ": " + msg; } private static Throwable asThrowable(final Object obj) { diff --git a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java new file mode 100644 index 00000000..ed54b2e9 --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java @@ -0,0 +1,100 @@ +/* + * 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 java.lang.invoke.MethodHandle; + +/** + * This is a subclass that represents a script function that may not be regenerated. + * This is used for example for bound functions and builtins. + */ +public final class FinalScriptFunctionData extends ScriptFunctionData { + + /** + * Constructor - used for bind + * + * @param name name + * @param arity arity + * @param list precompiled code + * @param isStrict strict + * @param isBuiltin builtin + * @param isConstructor constructor + */ + FinalScriptFunctionData(final String name, int arity, CompiledFunctions functions, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { + super(name, arity, isStrict, isBuiltin, isConstructor); + code.addAll(functions); + } + + /** + * Constructor - used from ScriptFunction. This assumes that we have code alraedy for the + * method (typically a native method) and possibly specializations. + * + * @param name name + * @param mh method handle for generic version of method + * @param specs specializations + * @param isStrict strict + * @param isBuiltin builtin + * @param isConstructor constructor + */ + FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { + super(name, arity(mh), isStrict, isBuiltin, isConstructor); + + addInvoker(mh); + if (specs != null) { + for (final MethodHandle spec : specs) { + addInvoker(spec); + } + } + } + + private void addInvoker(final MethodHandle mh) { + boolean needsCallee = needsCallee(mh); + if (isConstructor(mh)) { + //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor + //is too conservative a check. However, isConstructor(mh) always implies isConstructor param + assert isConstructor(); + code.add(new CompiledFunction(MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor + } else { + code.add(new CompiledFunction(mh)); + } + } + + private static int arity(final MethodHandle mh) { + if (isVarArg(mh)) { + return -1; + } + + //drop self, callee and boolean constructor flag to get real arity + return mh.type().parameterCount() - 1 - (needsCallee(mh) ? 1 : 0) - (isConstructor(mh) ? 1 : 0); + } + + private static boolean isConstructor(final MethodHandle mh) { + return mh.type().parameterCount() >= 1 && mh.type().parameterType(0) == boolean.class; + } + +} diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java new file mode 100644 index 00000000..03f0ab5d --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java @@ -0,0 +1,185 @@ +/* + * 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 java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import jdk.nashorn.internal.codegen.Compiler; +import jdk.nashorn.internal.codegen.CompilerConstants; +import jdk.nashorn.internal.codegen.FunctionSignature; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.parser.Token; +import jdk.nashorn.internal.parser.TokenType; + +import static jdk.nashorn.internal.lookup.Lookup.MH; + +/** + * 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. + * The common denominator is that it can get new invokers during its lifespan, + * unlike {@link FinalScriptFunctionData} + */ +public final class RecompilableScriptFunctionData extends ScriptFunctionData { + + private final FunctionNode functionNode; + private final PropertyMap allocatorMap; + private final CodeInstaller installer; + private final String allocatorClassName; + + /** lazily generated allocator */ + private MethodHandle allocator; + + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + + /** + * Constructor - public as scripts use it + * + * @param functionNode functionNode that represents this function code + * @param installer installer for code regeneration versions of this function + * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor + * @param allocatorMap allocator map to seed instances with, when constructing + */ + public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller installer, final String allocatorClassName, final PropertyMap allocatorMap) { + super(functionNode.isAnonymous() ? + "" : + functionNode.getIdent().getName(), + functionNode.getParameters().size(), + functionNode.isStrictMode(), + false, + true); + + this.functionNode = functionNode; + this.installer = installer; + this.allocatorClassName = allocatorClassName; + this.allocatorMap = allocatorMap; + } + + @Override + String toSource() { + final Source source = functionNode.getSource(); + final long token = tokenFor(functionNode); + + if (source != null && token != 0) { + return source.getString(Token.descPosition(token), Token.descLength(token)); + } + + return "function " + (name == null ? "" : name) + "() { [native code] }"; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + final Source source = functionNode.getSource(); + final long token = tokenFor(functionNode); + + if (source != null) { + sb.append(source.getName()) + .append(':') + .append(source.getLine(Token.descPosition(token))) + .append(' '); + } + + return sb.toString() + super.toString(); + } + + private static long tokenFor(final FunctionNode fn) { + final int position = Token.descPosition(fn.getFirstToken()); + final int length = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken()); + + return Token.toDesc(TokenType.FUNCTION, position, length); + } + + @Override + ScriptObject allocate() { + try { + ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try + return allocator == null ? null : (ScriptObject)allocator.invokeExact(allocatorMap); + } catch (final RuntimeException | Error e) { + throw e; + } catch (final Throwable t) { + throw new RuntimeException(t); + } + } + + private void ensureHasAllocator() throws ClassNotFoundException { + if (allocator == null && allocatorClassName != null) { + this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.tag(), MH.type(ScriptObject.class, PropertyMap.class)); + } + } + + @Override + protected void ensureCodeGenerated() { + if (!code.isEmpty()) { + return; // nothing to do, we have code, at least some. + } + + // check if function node is lazy, need to compile it. + // note that currently function cloning is not working completely, which + // means that the compiler will mutate the function node it has been given + // once it has been compiled, it cannot be recompiled. This means that + // lazy compilation works (not compiled yet) but e.g. specializations won't + // until the copy-on-write changes for IR are in, making cloning meaningless. + // therefore, currently method specialization is disabled. TODO + + if (functionNode.isLazy()) { + Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '" + functionNode.getName() + "'"); + new Compiler(installer, functionNode).compile().install(); + + // we don't need to update any flags - varArgs and needsCallee are instrincic + // in the function world we need to get a destination node from the compile instead + // and replace it with our function node. TODO + } + + // we can't get here unless we have bytecode, either from eager compilation or from + // running a lazy compile on the lines above + + assert functionNode.hasState(CompilationState.INSTALLED); + + // code exists - look it up and add it into the automatically sorted invoker list + code.add( + new CompiledFunction( + MH.findStatic( + LOOKUP, + functionNode.getCompileUnit().getCode(), + functionNode.getName(), + new FunctionSignature(functionNode). + getMethodType()))); + } + + @Override + MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { + final MethodHandle mh = super.getBestInvoker(callSiteType, args); + if (code.isLessSpecificThan(callSiteType)) { + // opportunity for code specialization - we can regenerate a better version of this method + } + return mh; + } + +} + diff --git a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java index def0313f..36a1d2ac 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java @@ -82,6 +82,9 @@ public final class ScriptEnvironment { /** Show full Nashorn version */ public final boolean _fullversion; + /** Should lazy compilation take place */ + public final boolean _lazy_compilation; + /** Create a new class loaded for each compilation */ public final boolean _loader_per_compile; @@ -155,6 +158,7 @@ public final class ScriptEnvironment { _early_lvalue_error = options.getBoolean("early.lvalue.error"); _empty_statements = options.getBoolean("empty.statements"); _fullversion = options.getBoolean("fullversion"); + _lazy_compilation = options.getBoolean("lazy.compilation"); _loader_per_compile = options.getBoolean("loader.per.compile"); _no_syntax_extensions = options.getBoolean("no.syntax.extensions"); _package = options.getString("package"); diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java index 2d28f91e..512a0b16 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java @@ -33,11 +33,11 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; + import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.codegen.CompilerConstants.Call; -import jdk.nashorn.internal.objects.annotations.SpecializedFunction; import jdk.nashorn.internal.lookup.MethodHandleFactory; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornGuards; @@ -48,16 +48,16 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards; public abstract class ScriptFunction extends ScriptObject { /** Method handle for prototype getter for this ScriptFunction */ - public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class); + public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class); /** Method handle for prototype setter for this ScriptFunction */ - public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class); + public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class); /** Method handle for length getter for this ScriptFunction */ - public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class); + public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class); /** Method handle for name getter for this ScriptFunction */ - public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class); + public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class); /** Method handle for allocate function for this ScriptFunction */ static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class); @@ -67,7 +67,9 @@ public abstract class ScriptFunction extends ScriptObject { /** method handle to scope getter for this ScriptFunction */ public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); - private final ScriptFunctionData data; + private static final MethodHandle IS_FUNCTION_MH = findOwnMH("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); + + private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class); /** Reference to constructor prototype. */ protected Object prototype; @@ -75,6 +77,8 @@ public abstract class ScriptFunction extends ScriptObject { /** The parent scope. */ private final ScriptObject scope; + private final ScriptFunctionData data; + /** * Constructor * @@ -97,7 +101,7 @@ public abstract class ScriptFunction extends ScriptObject { final boolean builtin, final boolean isConstructor) { - this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope); + this(new FinalScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope); } /** @@ -118,8 +122,8 @@ public abstract class ScriptFunction extends ScriptObject { constructorCount++; } - this.data = data; - this.scope = scope; + this.data = data; + this.scope = scope; } @Override @@ -295,20 +299,20 @@ public abstract class ScriptFunction extends ScriptObject { /** * Return the most appropriate invoke handle if there are specializations * @param type most specific method type to look for invocation with + * @param callsite args for trampoline invocation * @return invoke method handle */ - private final MethodHandle getBestInvoker(final MethodType type) { - return data.getBestInvoker(type); + private MethodHandle getBestInvoker(final MethodType type, final Object[] args) { + return data.getBestInvoker(type, args); } /** - * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation - * method handle for this ScriptFunction - * @see SpecializedFunction - * @return invokeHandle + * Return the most appropriate invoke handle if there are specializations + * @param type most specific method type to look for invocation with + * @return invoke method handle */ - public final MethodHandle getInvokeHandle() { - return data.getInvoker(); + public MethodHandle getBestInvoker(final MethodType type) { + return getBestInvoker(type, null); } /** @@ -319,7 +323,7 @@ public abstract class ScriptFunction extends ScriptObject { * @return bound invoke handle */ public final MethodHandle getBoundInvokeHandle(final ScriptObject self) { - return MH.bindTo(bindToCalleeIfNeeded(getInvokeHandle()), self); + return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self); } /** @@ -329,7 +333,8 @@ public abstract class ScriptFunction extends ScriptObject { * @return the potentially bound method handle */ private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) { - return data.needsCallee() ? MH.bindTo(methodHandle, this) : methodHandle; + return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle; + } /** @@ -340,15 +345,6 @@ public abstract class ScriptFunction extends ScriptObject { return data.getName(); } - /** - * Does this script function need to be compiled. This determined by - * null checking invokeHandle - * - * @return true if this needs compilation - */ - public final boolean needsCompilation() { - return data.getInvoker() == null; - } /** * Get the scope for this function @@ -358,15 +354,6 @@ public abstract class ScriptFunction extends ScriptObject { return scope; } - /** - * Reset the invoker handle. This is used by trampolines for - * lazy code generation - * @param invoker new invoker - */ - protected void resetInvoker(final MethodHandle invoker) { - data.resetInvoker(invoker); - } - /** * Prototype getter for this ScriptFunction - follows the naming convention * used by Nasgen and the code generator @@ -464,7 +451,7 @@ public abstract class ScriptFunction extends ScriptObject { @Override protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) { final MethodType type = desc.getMethodType(); - return new GuardedInvocation(pairArguments(data.getBestConstructor(type), type), null, NashornGuards.getFunctionGuard(this)); + return new GuardedInvocation(pairArguments(data.getBestConstructor(type.changeParameterType(0, ScriptFunction.class), null), type), null, getFunctionGuard(this)); } @SuppressWarnings("unused") @@ -472,7 +459,7 @@ public abstract class ScriptFunction extends ScriptObject { if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) { return obj; } - return ((GlobalObject) Context.getGlobalTrusted()).wrapAsObject(obj); + return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(obj); } /** @@ -506,8 +493,7 @@ public abstract class ScriptFunction extends ScriptObject { MethodHandle guard = null; if (data.needsCallee()) { - final MethodHandle callHandle = getBestInvoker(type); - + final MethodHandle callHandle = getBestInvoker(type, request.getArguments()); if (NashornCallSiteDescriptor.isScope(desc)) { // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined // (callee, this, args...) => (callee, args...) @@ -525,13 +511,12 @@ public abstract class ScriptFunction extends ScriptObject { if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) { boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER); } else { - guard = NashornGuards.getNonStrictFunctionGuard(this); + guard = getNonStrictFunctionGuard(this); } } } } else { - final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1)); - + final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments()); if (NashornCallSiteDescriptor.isScope(desc)) { // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined // (this, args...) => (args...) @@ -545,7 +530,8 @@ public abstract class ScriptFunction extends ScriptObject { } boundHandle = pairArguments(boundHandle, type); - return new GuardedInvocation(boundHandle, guard == null ? NashornGuards.getFunctionGuard(this) : guard); + + return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard); } /** @@ -554,13 +540,50 @@ public abstract class ScriptFunction extends ScriptObject { * 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(getBestInvoker(type)), bindName), type); + return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type, null)), bindName), type); } private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) { return bindName == null ? methodHandle : MH.insertArguments(methodHandle, 1, bindName); } + /** + * 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 + * + * @return method handle for guard + */ + private static MethodHandle getFunctionGuard(final ScriptFunction function) { + assert function.data != null; + return MH.insertArguments(IS_FUNCTION_MH, 1, function.data); + } + + /** + * 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 + * + * @return method handle for guard + */ + private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) { + assert function.data != null; + return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data); + } + + @SuppressWarnings("unused") + private static boolean isFunctionMH(final Object self, final ScriptFunctionData 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; + } + private static MethodHandle findOwnMH(final String name, final Class rtype, final Class... types) { final Class own = ScriptFunction.class; final MethodType mt = MH.type(rtype, types); diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java index 8f8193e4..f83cfa2c 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java @@ -32,151 +32,109 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.parser.Token; -import jdk.nashorn.internal.parser.TokenType; + +import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; /** * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime. * Instances of this class are created during codegen and stored in script classes' * constants array to reduce function instantiation overhead during runtime. */ -public final class ScriptFunctionData { - private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class); - private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); +public abstract class ScriptFunctionData { - // per-function object flags - private static final int IS_STRICT = 0b0000_0001; - private static final int IS_BUILTIN = 0b0000_0010; - private static final int HAS_CALLEE = 0b0000_0100; - private static final int IS_VARARGS = 0b0000_1000; - private static final int IS_CONSTRUCTOR = 0b0001_0000; - - /** Name of the function or "" */ - private final String name; - /** Source of this function, or null */ - private final Source source; - /** Map for new instance constructor */ - private PropertyMap allocatorMap; - /** Start position and length in source */ - private final long token; - /** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/ - private int arity; - private final int flags; - - /** Reference to code for this method. */ - private MethodHandle invoker; - /** Reference to code for this method when called to create "new" object. This must always be populated with a - * result of calling {@link #composeConstructor(MethodHandle)} on the value of the {@link #invoker} field. */ - private MethodHandle constructor; - /** Constructor to create a new instance. */ - private MethodHandle allocator; - /** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */ - private MethodHandle genericInvoker; - /** Specializations - see @SpecializedFunction */ - private MethodHandle[] invokeSpecializations; - /** Specializations - see @SpecializedFunction. Same restrictions as for {@link #constructor} apply; only populate - * with method handles returned from {@link #composeConstructor(MethodHandle)}. */ - private MethodHandle[] constructSpecializations; + /** Name of the function or "" for anonynous functions */ + protected final String name; - /** - * Constructor - * @param fn the function node - * @param allocatorMap the allocator property map - */ - public ScriptFunctionData(final FunctionNode fn, final PropertyMap allocatorMap) { + /** All versions of this function that have been generated to code */ + protected final CompiledFunctions code; - final long firstToken = fn.getFirstToken(); - final long lastToken = fn.getLastToken(); - final int position = Token.descPosition(firstToken); - final int length = Token.descPosition(lastToken) - position + Token.descLength(lastToken); + private int arity; - this.name = fn.isAnonymous() ? "" : fn.getIdent().getName(); - this.source = fn.getSource(); - this.allocatorMap = allocatorMap; - this.token = Token.toDesc(TokenType.FUNCTION, position, length); - this.arity = fn.getParameters().size(); - this.flags = makeFlags(fn.needsCallee(), fn.isVarArg(), fn.isStrictMode(), false, true); - } + private final boolean isStrict; + + private final boolean isBuiltin; + + private final boolean isConstructor; + + private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); + private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class); /** * Constructor * - * @param name the function name - * @param methodHandle the method handle - * @param specs array of specialized method handles - * @param strict strict flag - * @param builtin builtin flag - * @param isConstructor constructor flags + * @param name script function name + * @param arity arity + * @param isStrict is the function strict + * @param isBuiltin is the function built in + * @param isConstructor is the function a constructor */ - public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) { - this(name, null, 0L, methodHandle, specs, strict, builtin, isConstructor); + protected ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { + this.name = name; + this.arity = arity; + this.code = new CompiledFunctions(); + this.isStrict = isStrict; + this.isBuiltin = isBuiltin; + this.isConstructor = isConstructor; } - private ScriptFunctionData(final String name, final Source source, final long token, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) { - this.name = name; - this.source = source; - this.token = token; + final int getArity() { + return arity; + } - final boolean isVarArg = isVarArg(methodHandle); - final boolean needsCallee = needsCallee(methodHandle); + /** + * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final + * @param arity new arity + */ + void setArity(final int arity) { + this.arity = arity; + } - this.flags = makeFlags(needsCallee, isVarArg, strict, builtin, isConstructor); - int lArity = isVarArg ? -1 : methodHandle.type().parameterCount() - 1; //drop the self param for arity + CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) { + final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args); - if (needsCallee && !isVarArg) { - lArity--; + if (isConstructor()) { + ensureConstructor(originalInv); + return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args)); } - if (isConstructor(methodHandle)) { - assert isConstructor; - if (!isVarArg) { - lArity--; // drop the boolean flag for arity - } - /* - * We insert a boolean argument to tell if the method was invoked as constructor or not if the method - * handle's first argument is boolean. - */ - this.invoker = MH.insertArguments(methodHandle, 0, false); - this.constructor = composeConstructor(MH.insertArguments(methodHandle, 0, true)); - - if (specs != null) { - this.invokeSpecializations = new MethodHandle[specs.length]; - this.constructSpecializations = new MethodHandle[specs.length]; - for (int i = 0; i < specs.length; i++) { - this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false); - this.constructSpecializations[i] = composeConstructor(MH.insertArguments(specs[i], 0, true)); - } - } - } else { - this.invoker = methodHandle; - this.constructor = null; // delay composition of the constructor - this.invokeSpecializations = specs; - this.constructSpecializations = null; // delay composition of the constructors - } - this.arity = lArity; + return new CompiledFunction(boundInvoker); } /** - * Get the arity of the function. - * @return the arity + * Is this a ScriptFunction generated with strict semantics? + * @return true if strict, false otherwise */ - int getArity() { - return arity; + public boolean isStrict() { + return isStrict; } - /** - * Set the arity of the function. - * @param arity the arity - */ - void setArity(int arity) { - this.arity = arity; + boolean isBuiltin() { + return isBuiltin; + } + + boolean isConstructor() { + return isConstructor; + } + + boolean needsCallee() { + // we don't know if we need a callee or not unless we are generated + ensureCodeGenerated(); + return code.needsCallee(); } /** - * Get the function name. - * @return function name + * 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 */ + boolean needsWrappedThis() { + return !isStrict && !isBuiltin; + } + + String toSource() { + return "function " + (name == null ? "" : name) + "() { [native code] }"; + } + String getName() { return name; } @@ -184,327 +142,178 @@ public final class ScriptFunctionData { /** * 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 + * + * @return string representation of this function */ - String toSource() { - if (source != null && token != 0) { - return source.getString(Token.descPosition(token), Token.descLength(token)); - } - - return "function " + (name == null ? "" : name) + "() { [native code] }"; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder(); - sb.append(super.toString()) - .append(" [ ") - .append(invoker) - .append(", ") - .append((name == null || name.isEmpty()) ? "" : name); - - if (source != null) { - sb.append(" @ ") - .append(source.getName()) - .append(':') - .append(source.getLine(Token.descPosition(token))); - } - sb.append(" ]"); + sb.append("name='"). + append(name.isEmpty() ? "" : name). + append("' "). + append(code.size()). + append(" invokers="). + append(code); return sb.toString(); } /** - * Returns true if the function needs a callee argument. - * @return the needsCallee flag + * Pick the best invoker, i.e. the one version of this method with as narrow and specific + * types as possible. If the call site arguments are objects, but boxed primitives we can + * also try to get a primitive version of the method and do an unboxing filter, but then + * we need to insert a guard that checks the argument is really always a boxed primitive + * and not suddenly a "real" object + * + * @param callSiteType callsite type + * @param args arguments at callsite on first trampoline invocation + * @return method handle to best invoker */ - boolean needsCallee() { - return (flags & HAS_CALLEE) != 0; + MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { + return getBest(callSiteType).getInvoker(); } - /** - * Returns true if this is a strict-mode function. - * @return the strict flag - */ - public boolean isStrict() { - return (flags & IS_STRICT) != 0; + MethodHandle getBestInvoker(final MethodType callSiteType) { + return getBestInvoker(callSiteType, null); } - /** - * Returns true if this is a built-in function. - * @return the built-in flag - */ - private boolean isBuiltin() { - return (flags & IS_BUILTIN) != 0; + MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) { + if (!isConstructor()) { + throw typeError("not.a.constructor", toSource()); + } + ensureCodeGenerated(); + + final CompiledFunction best = getBest(callSiteType); + ensureConstructor(best); + return best.getConstructor(); } - /** - * Returns true if this function can be used as a constructor. - * @return the constructor flag - */ - private boolean isConstructor() { - return (flags & IS_CONSTRUCTOR) != 0; + MethodHandle getBestConstructor(final MethodType callSiteType) { + return getBestConstructor(callSiteType, null); } /** - * Returns true if this is a var-arg function. - * @return the var-arg flag + * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that + * code exists before performing an operation. */ - private boolean isVarArg() { - return (flags & IS_VARARGS) != 0; + protected void ensureCodeGenerated() { + //empty } /** - * 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 + * Return a generic Object/Object invoker for this method. It will ensure code + * is generated, get the most generic of all versions of this function and adapt it + * to Objects. + * + * TODO this is only public because {@link JavaAdapterFactory} can't supply us with + * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed + * + * @return generic invoker of this script function */ - boolean needsWrappedThis() { - return (flags & (IS_STRICT | IS_BUILTIN)) == 0; + public final MethodHandle getGenericInvoker() { + ensureCodeGenerated(); + return composeGenericMethod(code.mostGeneric().getInvoker()); } - /** - * Get the method handle used to invoke this function. - * @return the invoke handle - */ - MethodHandle getInvoker() { - return invoker; + private CompiledFunction getBest(final MethodType callSiteType) { + ensureCodeGenerated(); + return code.best(callSiteType); } - MethodHandle getBestInvoker(final MethodType type) { - return SpecializedMethodChooser.candidateWithLowestWeight(type, invoker, invokeSpecializations); + /** + * Allocates an object using this function's allocator. + * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator. + */ + ScriptObject allocate() { + return null; } /** - * Get the method handle used to invoke this function as a constructor. - * @return the constructor handle + * This method is used to create the immutable portion of a bound function. + * See {@link ScriptFunction#makeBoundFunction(Object, Object[])} + * + * @param fn the original function being bound + * @param self this reference to bind. Can be null. + * @param args additional arguments to bind. Can be null. */ - private MethodHandle getConstructor() { - if (constructor == null) { - constructor = composeConstructor(invoker); - } + ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) { + ensureCodeGenerated(); - return constructor; - } + final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args; + final int length = args == null ? 0 : args.length; - MethodHandle getBestConstructor(MethodType descType) { - if (!isConstructor()) { - throw typeError("not.a.constructor", toSource()); + CompiledFunctions boundList = new CompiledFunctions(); + for (final CompiledFunction inv : code) { + boundList.add(bind(inv, fn, self, allArgs)); } - return SpecializedMethodChooser.candidateWithLowestWeight(descType, getConstructor(), getConstructSpecializations()); + ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor()); + return boundData; } - private MethodHandle composeConstructor(MethodHandle ctor) { + /** + * Compose a constructor given a primordial constructor handle + * + * @param ctor primordial constructor handle + * @param needsCallee do we need to pass a callee + * + * @return the composed constructor + */ + protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) { // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having // "this" in the first argument position is what allows the elegant folded composition of // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor // always returns Object. - MethodHandle composedCtor = changeReturnTypeToObject(swapCalleeAndThis(ctor)); + MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor; + + composedCtor = changeReturnTypeToObject(composedCtor); final MethodType ctorType = composedCtor.type(); + // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it. // (this, [callee, ]args...) => ([callee, ]args...) final Class[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); + // Fold constructor into newFilter that replaces the return value from the constructor with the originally // allocated value when the originally allocated value is a primitive. // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...) composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor); // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject... - if (needsCallee()) { + if (needsCallee) { // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...), // or... return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE); } + // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee // (this, args...) filter (callee) => (callee, args...) return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE); } /** - * Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types. - * @return the generic invoke handle - */ - private MethodHandle getGenericInvoker() { - if (genericInvoker == null) { - assert invoker != null : "invoker is null"; - genericInvoker = makeGenericMethod(invoker); - } - return genericInvoker; - } - - /** - * Execute this script function. - * @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 - */ - Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable { - final MethodHandle genInvoker = getGenericInvoker(); - final Object selfObj = convertThisObject(self); - final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; - - if (isVarArg()) { - if (needsCallee()) { - return genInvoker.invokeExact(fn, selfObj, args); - } - return genInvoker.invokeExact(selfObj, args); - } - - final int paramCount = genInvoker.type().parameterCount(); - if (needsCallee()) { - switch (paramCount) { - case 2: - return genInvoker.invokeExact(fn, selfObj); - case 3: - return genInvoker.invokeExact(fn, selfObj, getArg(args, 0)); - case 4: - return genInvoker.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1)); - case 5: - return genInvoker.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); - default: - return genInvoker.invokeWithArguments(withArguments(fn, selfObj, paramCount, args)); - } - } - - switch (paramCount) { - case 1: - return genInvoker.invokeExact(selfObj); - case 2: - return genInvoker.invokeExact(selfObj, getArg(args, 0)); - case 3: - return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); - case 4: - return genInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); - default: - return genInvoker.invokeWithArguments(withArguments(null, selfObj, paramCount, args)); - } - } - - private static Object getArg(final Object[] args, final int i) { - return i < args.length ? args[i] : UNDEFINED; - } - - private Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) { - final Object[] finalArgs = new Object[argCount]; - - int nextArg = 0; - if (needsCallee()) { - assert fn != null; - finalArgs[nextArg++] = fn; - } else { - assert fn == null; - } - finalArgs[nextArg++] = self; - - // Don't add more args that there is argCount in the handle (including self and callee). - for (int i = 0; i < args.length && nextArg < argCount;) { - finalArgs[nextArg++] = args[i++]; - } - - // If we have fewer args than argCount, pad with undefined. - while (nextArg < argCount) { - finalArgs[nextArg++] = UNDEFINED; - } - - return finalArgs; - } - - /** - * Get the specialized construct handles for this function. - * @return array of specialized construct handles - */ - private MethodHandle[] getConstructSpecializations() { - if(constructSpecializations == null && invokeSpecializations != null) { - final MethodHandle[] ctors = new MethodHandle[invokeSpecializations.length]; - for(int i = 0; i < ctors.length; ++i) { - ctors[i] = composeConstructor(invokeSpecializations[i]); - } - constructSpecializations = ctors; - } - return constructSpecializations; - } - - /** - * Set the method handles for this function. - * @param invoker the invoker handle - * @param allocator the allocator handle - */ - public void setMethodHandles(final MethodHandle invoker, final MethodHandle allocator) { - // We can't make method handle fields final because they're not available during codegen - // and they're set when first called, so we enforce set-once here. - if (this.invoker == null) { - this.invoker = invoker; - this.constructor = null; // delay constructor composition - this.allocator = allocator; - } - } - - /** - * Used by the trampoline. Must not be any wider than package - * private - * @param invoker new invoker - */ - void resetInvoker(final MethodHandle invoker) { - this.invoker = invoker; - this.constructor = null; //delay constructor composition - } - - /** - * Allocates an object using this function's allocator. - * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator. - */ - ScriptObject allocate() { - if (allocator == null) { - return null; - } - - try { - return (ScriptObject)allocator.invokeExact(allocatorMap); - } catch (final RuntimeException | Error e) { - throw e; - } catch (final Throwable t) { - throw new RuntimeException(t); - } - } - - /** - * This method is used to create the immutable portion of a bound function. - * See {@link ScriptFunction#makeBoundFunction(Object, Object[])} + * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed + * method handle. If this function's method handles don't need a callee parameter, returns the original method + * handle unchanged. * - * @param fn the original function being bound - * @param self this reference to bind. Can be null. - * @param args additional arguments to bind. Can be null. + * @param mh a method handle with order of arguments {@code (callee, this, args...)} + * + * @return a method handle with order of arguments {@code (this, callee, args...)} */ - ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) { - final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args; - - final boolean isConstructor = isConstructor(); - // Note that the new ScriptFunctionData's method handle will not need a callee regardless of whether the - // original did. - final ScriptFunctionData boundData = new ScriptFunctionData(name, source, token, - bindInvokeHandle(invoker, fn, self, allArgs), bindInvokeSpecializations(fn, self, allArgs), isStrict(), isBuiltin(), isConstructor); - if(isConstructor) { - // Can't just rely on bound invoke as a basis for constructor, as it ignores the passed "this" in favor of the - // bound "this"; constructor on the other hand must see the actual "this" received from the allocator. - - // Binding a function will force constructor composition in getConstructor(); not really any way around that - // as it's the composed constructor that has to be bound to the function. - boundData.constructor = bindConstructHandle(getConstructor(), fn, allArgs); - boundData.constructSpecializations = bindConstructorSpecializations(fn, allArgs); - } - assert boundData.allocator == null; - final int thisArity = getArity(); - if(thisArity != -1) { - boundData.setArity(Math.max(0, thisArity - args.length)); - } else { - assert boundData.getArity() == -1; + private static MethodHandle swapCalleeAndThis(final MethodHandle mh) { + final MethodType type = mh.type(); + assert type.parameterType(0) == ScriptFunction.class : type; + assert type.parameterType(1) == Object.class : type; + final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class); + final int[] reorder = new int[type.parameterCount()]; + reorder[0] = 1; + assert reorder[1] == 0; + for (int i = 2; i < reorder.length; ++i) { + reorder[i] = i; } - return boundData; + return MethodHandles.permuteArguments(mh, newType, reorder); } /** @@ -514,7 +323,7 @@ public final class ScriptFunctionData { * * @return the converted this object */ - Object convertThisObject(final Object thiz) { + private Object convertThisObject(final Object thiz) { if (!(thiz instanceof ScriptObject) && needsWrappedThis()) { if (JSType.nullOrUndefined(thiz)) { return Context.getGlobalTrusted(); @@ -528,18 +337,20 @@ public final class ScriptFunctionData { return thiz; } - static boolean isPrimitiveThis(Object obj) { + static boolean isPrimitiveThis(final Object obj) { return obj instanceof String || obj instanceof ConsString || obj instanceof Number || obj instanceof Boolean; } /** * Creates an invoker method handle for a bound function. + * * @param targetFn the function being bound * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or * any of its specializations. * @param self the "this" value being bound * @param args additional arguments being bound + * * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed @@ -549,16 +360,22 @@ public final class ScriptFunctionData { // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound // in the target and will be ignored anyway. final boolean isTargetBound = targetFn.isBoundFunction(); - assert !(isTargetBound && needsCallee()); // already bound functions don't need a callee + + final boolean needsCallee = needsCallee(originalInvoker); + assert needsCallee == needsCallee() : "callee contract violation 2"; + assert !(isTargetBound && needsCallee); // already bound functions don't need a callee + final Object boundSelf = isTargetBound ? null : convertThisObject(self); final MethodHandle boundInvoker; - if(isVarArg(originalInvoker)) { + + if (isVarArg(originalInvoker)) { // First, bind callee and this without arguments final MethodHandle noArgBoundInvoker; - if(isTargetBound) { + + if (isTargetBound) { // Don't bind either callee or this noArgBoundInvoker = originalInvoker; - } else if(needsCallee()) { + } else if (needsCallee) { // Bind callee and this noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf); } else { @@ -566,17 +383,16 @@ public final class ScriptFunctionData { noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf); } // Now bind arguments - if(args.length > 0) { + if (args.length > 0) { boundInvoker = varArgBinder(noArgBoundInvoker, args); } else { boundInvoker = noArgBoundInvoker; } } else { - final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(), - args.length + (isTargetBound ? 0 : (needsCallee() ? 2 : 1)))]; + final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(), args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))]; int next = 0; - if(!isTargetBound) { - if(needsCallee()) { + if (!isTargetBound) { + if (needsCallee) { boundArgs[next++] = targetFn; } boundArgs[next++] = boundSelf; @@ -589,51 +405,44 @@ public final class ScriptFunctionData { // start at position 1. If the function is not bound, we start inserting arguments at position 0. boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs); } - if(isTargetBound) { + + if (isTargetBound) { return boundInvoker; } + // If the target is not already bound, add a dropArguments that'll throw away the passed this return MH.dropArguments(boundInvoker, 0, Object.class); } - private MethodHandle[] bindInvokeSpecializations(final ScriptFunction fn, final Object self, final Object[] args) { - if(invokeSpecializations == null) { - return null; - } - final MethodHandle[] boundSpecializations = new MethodHandle[invokeSpecializations.length]; - for(int i = 0; i < invokeSpecializations.length; ++i) { - boundSpecializations[i] = bindInvokeHandle(invokeSpecializations[i], fn, self, args); - } - return boundSpecializations; - } - /** * Creates a constructor method handle for a bound function using the passed constructor handle. + * * @param originalConstructor the constructor handle to bind. It must be a composed constructor. * @param fn the function being bound * @param args arguments being bound + * * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if * this script function data object has no constructor handle, null is returned. */ private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) { - if(originalConstructor == null) { - return null; - } + assert originalConstructor != null; // If target function is already bound, don't bother binding the callee. final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor : MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class); - if(args.length == 0) { + + if (args.length == 0) { return calleeBoundConstructor; } - if(isVarArg(calleeBoundConstructor)) { + if (isVarArg(calleeBoundConstructor)) { return varArgBinder(calleeBoundConstructor, args); } final Object[] boundArgs; + final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1; if (args.length <= maxArgCount) { boundArgs = args; @@ -641,27 +450,123 @@ public final class ScriptFunctionData { boundArgs = new Object[maxArgCount]; System.arraycopy(args, 0, boundArgs, 0, maxArgCount); } + return MH.insertArguments(calleeBoundConstructor, 1, boundArgs); } - private MethodHandle[] bindConstructorSpecializations(final ScriptFunction fn, final Object[] args) { - final MethodHandle[] ctorSpecs = getConstructSpecializations(); - if(ctorSpecs == null) { - return null; + /** + * Takes a method handle, and returns a potentially different method handle that can be used in + * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}. + * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into + * {@code Object} as well, except for the following ones: + *

    + *
  • a last parameter of type {@code Object[]} which is used for vararg functions,
  • + *
  • the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself + * (callee) as an argument.
  • + *
+ * + * @param mh the original method handle + * + * @return the new handle, conforming to the rules above. + */ + protected MethodHandle composeGenericMethod(final MethodHandle mh) { + final MethodType type = mh.type(); + MethodType newType = type.generic(); + if (isVarArg(mh)) { + newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); + } + if (needsCallee(mh)) { + newType = newType.changeParameterType(0, ScriptFunction.class); + } + return type.equals(newType) ? mh : mh.asType(newType); + } + + /** + * Execute this script function. + * + * @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 + */ + Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable { + final MethodHandle mh = getGenericInvoker(); + + final Object selfObj = convertThisObject(self); + final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; + + if (isVarArg(mh)) { + if (needsCallee(mh)) { + return mh.invokeExact(fn, selfObj, args); + } + return mh.invokeExact(selfObj, args); } - final MethodHandle[] boundSpecializations = new MethodHandle[ctorSpecs.length]; - for(int i = 0; i < ctorSpecs.length; ++i) { - boundSpecializations[i] = bindConstructHandle(ctorSpecs[i], fn, args); + + final int paramCount = mh.type().parameterCount(); + if (needsCallee(mh)) { + switch (paramCount) { + case 2: + return mh.invokeExact(fn, selfObj); + case 3: + return mh.invokeExact(fn, selfObj, getArg(args, 0)); + case 4: + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1)); + case 5: + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); + default: + return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args)); + } + } + + switch (paramCount) { + case 1: + return mh.invokeExact(selfObj); + case 2: + return mh.invokeExact(selfObj, getArg(args, 0)); + case 3: + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); + case 4: + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); + default: + return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args)); } - return boundSpecializations; } + private static Object getArg(final Object[] args, final int i) { + return i < args.length ? args[i] : UNDEFINED; + } + + private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) { + final Object[] finalArgs = new Object[argCount]; + + int nextArg = 0; + if (fn != null) { + //needs callee + finalArgs[nextArg++] = fn; + } + finalArgs[nextArg++] = self; + + // Don't add more args that there is argCount in the handle (including self and callee). + for (int i = 0; i < args.length && nextArg < argCount;) { + finalArgs[nextArg++] = args[i++]; + } + + // If we have fewer args than argCount, pad with undefined. + while (nextArg < argCount) { + finalArgs[nextArg++] = UNDEFINED; + } + + return finalArgs; + } /** * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on * invocation + * * @param mh the handle * @param args the bound arguments + * * @return the bound method handle */ private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) { @@ -671,41 +576,20 @@ public final class ScriptFunctionData { } /** - * Convert boolean flags to int. - * @param needsCallee needs-callee flag - * @param isVarArg var-arg flag - * @param isStrict strict flag - * @param isBuiltin builtin flag - * @return int flags + * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already + * {@code Object}, the handle is returned unchanged. + * + * @param mh the handle to adapt + * @return the adapted handle */ - private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { - int flags = 0; - if (needsCallee) { - flags |= HAS_CALLEE; - } - if (isVarArg) { - flags |= IS_VARARGS; - } - if (isStrict) { - flags |= IS_STRICT; - } - if (isBuiltin) { - flags |= IS_BUILTIN; - } - if (isConstructor) { - flags |= IS_CONSTRUCTOR; - } - - return flags; + private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) { + return MH.asType(mh, mh.type().changeReturnType(Object.class)); } - /** - * Test if a methodHandle refers to a constructor. - * @param methodHandle MethodHandle to test. - * @return True if method is a constructor. - */ - private static boolean isConstructor(final MethodHandle methodHandle) { - return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class; + private void ensureConstructor(final CompiledFunction inv) { + if (!inv.hasConstructor()) { + inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker()))); + } } /** @@ -713,102 +597,56 @@ public final class ScriptFunctionData { * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore - * they also always receive a callee. - * @param methodHandle the examined method handle + * they also always receive a callee). + * + * @param mh the examined method handle + * * @return true if the method handle expects a callee, false otherwise */ - private static boolean needsCallee(MethodHandle methodHandle) { - final MethodType type = methodHandle.type(); - final int len = type.parameterCount(); - if(len == 0) { + protected static boolean needsCallee(final MethodHandle mh) { + final MethodType type = mh.type(); + final int length = type.parameterCount(); + + if (length == 0) { return false; } - if(type.parameterType(0) == boolean.class) { - return len > 1 && type.parameterType(1) == ScriptFunction.class; - } - return type.parameterType(0) == ScriptFunction.class; - } - private static boolean isVarArg(MethodHandle methodHandle) { - final MethodType type = methodHandle.type(); - return type.parameterType(type.parameterCount() - 1).isArray(); - } - - /** - * Takes a method handle, and returns a potentially different method handle that can be used in - * {@link ScriptFunction#invoke(Object, Object...)} or {@link ScriptFunction#construct(Object, Object...)}. - * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into - * {@code Object} as well, except for the following ones: - *
    - *
  • a last parameter of type {@code Object[]} which is used for vararg functions,
  • - *
  • the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself - * (callee) as an argument.
  • - *
- * - * @param handle the original method handle - * @return the new handle, conforming to the rules above. - */ - private MethodHandle makeGenericMethod(final MethodHandle handle) { - final MethodType type = handle.type(); - MethodType newType = type.generic(); - if (isVarArg()) { - newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); - } - if (needsCallee()) { - newType = newType.changeParameterType(0, ScriptFunction.class); + if (type.parameterType(0) == boolean.class) { + return length > 1 && type.parameterType(1) == ScriptFunction.class; } - return type.equals(newType) ? handle : handle.asType(newType); - } - /** - * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already - * {@code Object}, the handle is returned unchanged. - * @param mh the handle to adapt - * @return the adapted handle - */ - private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) { - return MH.asType(mh, mh.type().changeReturnType(Object.class)); + return type.parameterType(0) == ScriptFunction.class; } - /** - * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed - * method handle. If this function's method handles don't need a callee parameter, returns the original method - * handle unchanged. - * @param mh a method handle with order of arguments {@code (callee, this, args...)} - * @return a method handle with order of arguments {@code (this, callee, args...)} + * Check if a javascript function methodhandle is a vararg handle + * + * @param mh method handle to check + * + * @return true if vararg */ - private MethodHandle swapCalleeAndThis(final MethodHandle mh) { - if (!needsCallee()) { - return mh; - } + protected static boolean isVarArg(final MethodHandle mh) { final MethodType type = mh.type(); - assert type.parameterType(0) == ScriptFunction.class; - assert type.parameterType(1) == Object.class; - final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class); - final int[] reorder = new int[type.parameterCount()]; - reorder[0] = 1; - assert reorder[1] == 0; - for (int i = 2; i < reorder.length; ++i) { - reorder[i] = i; - } - return MethodHandles.permuteArguments(mh, newType, reorder); + return type.parameterType(type.parameterCount() - 1).isArray(); } @SuppressWarnings("unused") private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) { - if(array2 == null) { + if (array2 == null) { // Must clone it, as we can't allow the receiving method to alter the array return array1.clone(); } + final int l2 = array2.length; - if(l2 == 0) { + if (l2 == 0) { return array1.clone(); } + final int l1 = array1.length; final Object[] concat = new Object[l1 + l2]; System.arraycopy(array1, 0, concat, 0, l1); System.arraycopy(array2, 0, concat, l1, l2); + return concat; } @@ -820,5 +658,4 @@ public final class ScriptFunctionData { private static MethodHandle findOwnMH(final String name, final Class rtype, final Class... types) { return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types)); } - } diff --git a/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java b/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java deleted file mode 100644 index 2853cdf2..00000000 --- a/src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java +++ /dev/null @@ -1,99 +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.runtime; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; -import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.runtime.options.Options; - -class SpecializedMethodChooser { - /** Should specialized function and specialized constructors for the builtin be used if available? */ - private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable"); - - static MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) { - if (DISABLE_SPECIALIZATION || specs == null) { - return initialCandidate; - } - - int minimumWeight = Integer.MAX_VALUE; - MethodHandle candidate = initialCandidate; - - for (final MethodHandle spec : specs) { - final MethodType specType = spec.type(); - - if (!typeCompatible(descType, specType)) { - continue; - } - - //return type is ok. we want a wider or equal one for our callsite. - final int specWeight = weigh(specType); - if (specWeight < minimumWeight) { - candidate = spec; - minimumWeight = specWeight; - } - } - - return candidate; - } - - private static boolean typeCompatible(final MethodType desc, final MethodType spec) { - //spec must fit in desc - final Class[] dparray = desc.parameterArray(); - final Class[] sparray = spec.parameterArray(); - - if (dparray.length != sparray.length) { - return false; - } - - for (int i = 0; i < dparray.length; i++) { - final Type dp = Type.typeFor(dparray[i]); - final Type sp = Type.typeFor(sparray[i]); - - if (dp.isBoolean()) { - return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution - } - - //specialization arguments must be at least as wide as dp, if not wider - if (Type.widest(dp, sp) != sp) { - //e.g. specialization takes double and callsite says "object". reject. - //but if specialization says double and callsite says "int" or "long" or "double", that's fine - return false; - } - } - - return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic. - } - - private static int weigh(final MethodType t) { - int weight = Type.typeFor(t.returnType()).getWeight(); - for (final Class paramType : t.parameterArray()) { - final int pweight = Type.typeFor(paramType).getWeight(); - weight += pweight; - } - return weight; - } -} diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java index 3d8363e1..834898af 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java @@ -39,7 +39,7 @@ import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; /** - * Utility class shared by {@link NashornLinker} and {@code NashornPrimitiveLinker} for converting JS values to Java + * Utility class shared by {@code NashornLinker} and {@code NashornPrimitiveLinker} for converting JS values to Java * types. */ public class JavaArgumentConverters { diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java b/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java index ed152485..a8e79184 100644 --- a/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java +++ b/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java @@ -40,8 +40,6 @@ public final class NashornGuards { private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class); private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class); private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class); - private static final MethodHandle IS_FUNCTION_MH = findOwnMH("isFunctionMH", boolean.class, Object.class, MethodHandle.class); - private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, MethodHandle.class); private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class); // don't create me! @@ -87,33 +85,6 @@ public final class NashornGuards { return MH.insertArguments(IS_INSTANCEOF_2, 1, class1, class2); } - /** - * 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 - * - * @return method handle for guard - */ - public static MethodHandle getFunctionGuard(final ScriptFunction function) { - assert function.getInvokeHandle() != null; - return MH.insertArguments(IS_FUNCTION_MH, 1, function.getInvokeHandle()); - } - - /** - * 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 - * - * @return method handle for guard - */ - public static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) { - assert function.getInvokeHandle() != null; - return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.getInvokeHandle()); - } - @SuppressWarnings("unused") private static boolean isScriptObject(final Object self) { return self instanceof ScriptObject; @@ -129,16 +100,6 @@ public final class NashornGuards { return self instanceof ScriptObject && ((ScriptObject)self).getMap() == map; } - @SuppressWarnings("unused") - private static boolean isFunctionMH(final Object self, final MethodHandle mh) { - return self instanceof ScriptFunction && ((ScriptFunction)self).getInvokeHandle() == mh; - } - - @SuppressWarnings("unused") - private static boolean isNonStrictFunction(final Object self, final Object arg, final MethodHandle mh) { - return self instanceof ScriptFunction && ((ScriptFunction)self).getInvokeHandle() == mh && arg instanceof ScriptObject; - } - @SuppressWarnings("unused") private static boolean isInstanceOf2(final Object self, final Class class1, final Class class2) { return class1.isInstance(self) || class2.isInstance(self); diff --git a/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java b/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java index 82b6edbc..16ff04ea 100644 --- a/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java +++ b/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java @@ -278,7 +278,7 @@ public class OptionTemplate implements Comparable { this.valueNextArg = Boolean.parseBoolean(arg); break; default: - throw new IllegalArgumentException(); + throw new IllegalArgumentException(keyToken); } } diff --git a/src/jdk/nashorn/internal/runtime/resources/Options.properties b/src/jdk/nashorn/internal/runtime/resources/Options.properties index 534eb177..e63f7a37 100644 --- a/src/jdk/nashorn/internal/runtime/resources/Options.properties +++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties @@ -165,6 +165,12 @@ nashorn.option.debug.locals = { \ desc="Generate local variable table in .class files." \ } +nashorn.option.lazy.compilation = { \ + name="--lazy-compilation", \ + is_undocumented=true, \ + desc="EXPERIMENTAL: Use lazy code generation strategies - do not compile the entire script at once." \ +} + nashorn.option.loader.per.compile = { \ name="--loader-per-compile", \ is_undocumented=true, \ diff --git a/test/script/currently-failing/JDK-8006529.js b/test/script/currently-failing/JDK-8006529.js index da08d2b5..ca21f0b9 100644 --- a/test/script/currently-failing/JDK-8006529.js +++ b/test/script/currently-failing/JDK-8006529.js @@ -39,12 +39,13 @@ * and FunctionNode because of package-access check and so reflective calls. */ -var Parser = Java.type("jdk.nashorn.internal.parser.Parser") -var Compiler = Java.type("jdk.nashorn.internal.codegen.Compiler") -var Context = Java.type("jdk.nashorn.internal.runtime.Context") +var Parser = Java.type("jdk.nashorn.internal.parser.Parser") +var Compiler = Java.type("jdk.nashorn.internal.codegen.Compiler") +var Context = Java.type("jdk.nashorn.internal.runtime.Context") var ScriptEnvironment = Java.type("jdk.nashorn.internal.runtime.ScriptEnvironment") -var Source = Java.type("jdk.nashorn.internal.runtime.Source") -var FunctionNode = Java.type("jdk.nashorn.internal.ir.FunctionNode") +var Source = Java.type("jdk.nashorn.internal.runtime.Source") +var FunctionNode = Java.type("jdk.nashorn.internal.ir.FunctionNode") +var ThrowErrorManager = Java.type("jdk.nashorn.internal.runtime.Context$ThrowErrorManager"); // Compiler class methods and fields var parseMethod = Parser.class.getMethod("parse"); @@ -90,7 +91,7 @@ function getFirstFunction(functionNode) { // representing it. function compile(source) { var source = new Source("", source); - var parser = new Parser(Context.getContext().getEnv(), source, null); + var parser = new Parser(Context.getContext().getEnv(), source, new ThrowErrorManager()); var func = parseMethod.invoke(parser); var compiler = new Compiler(Context.getContext().getEnv(), func); diff --git a/test/script/currently-failing/clone_ir.js b/test/script/currently-failing/clone_ir.js new file mode 100644 index 00000000..3c1eccf2 --- /dev/null +++ b/test/script/currently-failing/clone_ir.js @@ -0,0 +1,100 @@ +/* + * 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. + * + * 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. + */ + +/** + * clone_ir : Check that functionNode.clone copies all nodes and that they + * are not the same references + * + * @test + * @run + */ + +var js1 = "var tuple = { func : function f(x) { if (x) { print('true'); { print('block_under-true'); } } else { print('false'); } } }"; + +var Parser = Java.type("jdk.nashorn.internal.parser.Parser"); +var ASTWriter = Java.type("jdk.nashorn.internal.ir.debug.ASTWriter"); +var Context = Java.type("jdk.nashorn.internal.runtime.Context"); +var ScriptEnvironment = Java.type("jdk.nashorn.internal.runtime.ScriptEnvironment"); +var Source = Java.type("jdk.nashorn.internal.runtime.Source"); +var FunctionNode = Java.type("jdk.nashorn.internal.ir.FunctionNode"); +var ThrowErrorManager = Java.type("jdk.nashorn.internal.runtime.Context$ThrowErrorManager"); +var System = Java.type("java.lang.System"); + +var toArrayMethod = ASTWriter.class.getMethod("toArray"); +var parseMethod = Parser.class.getMethod("parse"); + +function toString(obj) { + var output = "{ "; + for (property in obj) { + output += property + ': ' + obj[property]+'; '; + } + return output + '}' +} + +function flatten(func) { + var writer = new ASTWriter(func); + var funcList = toArrayMethod.invoke(writer); + + var res = []; + for each (x in funcList) { + res.push({ name: x.getClass().getName(), id: System.identityHashCode(x) }); + } + return res; +} + +function check(contents) { + return check_src(new Source("", contents)); +} + +function check_src(src) { + var parser = new Parser(Context.getContext().getEnv(), src, new ThrowErrorManager()); + + var func = parseMethod.invoke(parser); + print(func); + var func2 = func.clone(); + + var f1 = flatten(func); + var f2 = flatten(func2); + + print(f1.map(toString)); + print(f2.map(toString)); + + if (f1.length != f2.length) { + print("length difference between original and clone " + f1.length + " != " + f2.length); + return false; + } + + for (var i = 0; i < f1.length; i++) { + if (f1[i].name !== f2[i].name) { + print("name conflict at " + i + " " + f1[i].name + " != " + f2[i].name); + return false; + } else if (f1[i].id === f2[i].id) { + print("id problem at " + i + " " + toString(f1[i]) + " was not deep copied to " + toString(f2[i]) + " became " + f1[i].id + " != " + f2[i].id); + return false; + } + } + + return true; +} + +print(check(js1)); -- cgit v1.2.3 From 06532275f21e4eab399a085a26dbd184772a34ac Mon Sep 17 00:00:00 2001 From: sundar Date: Tue, 12 Mar 2013 21:17:47 +0530 Subject: 8009868: For loop with "true" as condition results in AssertionError in codegen Reviewed-by: jlaskey, hannesw, lagergren --- src/jdk/nashorn/internal/codegen/Lower.java | 1 + test/script/basic/JDK-8009868.js | 35 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/script/basic/JDK-8009868.js diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java index 4be28193..5baa7af5 100644 --- a/src/jdk/nashorn/internal/codegen/Lower.java +++ b/src/jdk/nashorn/internal/codegen/Lower.java @@ -237,6 +237,7 @@ final class Lower extends NodeOperatorVisitor { if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { forNode.setTest(null); + setHasGoto(forNode); setTerminal(forNode, !escapes); } diff --git a/test/script/basic/JDK-8009868.js b/test/script/basic/JDK-8009868.js new file mode 100644 index 00000000..ffbc8c21 --- /dev/null +++ b/test/script/basic/JDK-8009868.js @@ -0,0 +1,35 @@ +/* + * 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. + * + * 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-8009868: For loop with "true" as condition results in AssertionError in codegen + * + * @test + * @run + */ + +// This used to crash with AssertionError in codegen +for(; true;) { + break; +} -- cgit v1.2.3 From f18063f790ca70f41d79ea528a5f2a9087282df2 Mon Sep 17 00:00:00 2001 From: lagergren Date: Thu, 14 Mar 2013 14:49:55 +0100 Subject: 8009982: Lazy execution bugfix. Added lazy sunspider unit test. Added mandreel to compile-octane test. Fixed warnings Reviewed-by: sundar, jlaskey --- .../api/scripting/NashornScriptEngineFactory.java | 2 +- .../nashorn/internal/codegen/CodeGenerator.java | 5 ++- src/jdk/nashorn/internal/codegen/Compiler.java | 36 ++++++++++++++++++---- src/jdk/nashorn/internal/ir/FunctionNode.java | 12 +++----- src/jdk/nashorn/internal/objects/Global.java | 3 +- src/jdk/nashorn/internal/parser/Parser.java | 2 +- src/jdk/nashorn/internal/runtime/Context.java | 1 + src/jdk/nashorn/internal/runtime/ScriptLoader.java | 3 +- .../runtime/linker/JavaAdapterFactory.java | 1 + .../internal/runtime/regexp/DefaultRegExp.java | 10 +++--- .../internal/runtime/regexp/JoniRegExp.java | 10 +++--- .../nashorn/internal/runtime/regexp/RegExp.java | 2 +- .../internal/runtime/regexp/RegExpFactory.java | 9 +++--- .../internal/runtime/regexp/RegExpResult.java | 6 ++-- .../internal/runtime/regexp/RegExpScanner.java | 2 -- test/script/basic/compile-octane.js.EXPECTED | 3 ++ test/script/basic/run-octane.js | 23 +++++++++++--- test/script/basic/runsunspider-eager.js | 33 ++++++++++++++++++++ test/script/basic/runsunspider-lazy.js | 34 ++++++++++++++++++++ test/script/basic/runsunspider.js | 30 +----------------- 20 files changed, 152 insertions(+), 75 deletions(-) create mode 100644 test/script/basic/runsunspider-eager.js create mode 100644 test/script/basic/runsunspider-lazy.js diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java index 4672f6a0..e38284da 100644 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java @@ -176,7 +176,7 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory { // -- Internals only below this point - private void checkConfigPermission() { + private static void checkConfigPermission() { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("nashorn.setConfig")); diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 71a2eb0f..ce6a1fcd 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -3230,8 +3230,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } private void newFunctionObject(final FunctionNode functionNode) { - final boolean isLazy = functionNode.isLazy(); - final Class[] cparams = new Class[] { RecompilableScriptFunctionData.class, ScriptObject.class }; + final boolean isLazy = functionNode.isLazy(); new ObjectCreator(this, new ArrayList(), new ArrayList(), false, false) { @Override @@ -3246,7 +3245,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } else { m.loadNull(); } - m.invoke(constructorNoLookup(className, cparams)); + m.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class)); } }.makeObject(method); } diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java index a799ee05..87d26c1a 100644 --- a/src/jdk/nashorn/internal/codegen/Compiler.java +++ b/src/jdk/nashorn/internal/codegen/Compiler.java @@ -217,8 +217,6 @@ public final class Compiler { append(safeSourceName(functionNode.getSource())); this.scriptName = sb.toString(); - - LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "' lazy=" + functionNode.isLazy()); } /** @@ -361,7 +359,10 @@ public final class Compiler { final Map> installedClasses = new HashMap<>(); final String rootClassName = firstCompileUnitName(); - final Class rootClass = install(rootClassName, bytecode.get(rootClassName)); + final byte[] rootByteCode = bytecode.get(rootClassName); + final Class rootClass = install(rootClassName, rootByteCode); + + int length = rootByteCode.length; installedClasses.put(rootClassName, rootClass); @@ -370,7 +371,10 @@ public final class Compiler { if (className.equals(rootClassName)) { continue; } - installedClasses.put(className, install(className, entry.getValue())); + final byte[] code = entry.getValue(); + length += code.length; + + installedClasses.put(className, install(className, code)); } for (final CompileUnit unit : compileUnits) { @@ -388,12 +392,32 @@ public final class Compiler { } }); - LOG.info("Installed root class: " + rootClass + " and " + bytecode.size() + " compile unit classes"); + final StringBuilder sb; + if (LOG.isEnabled()) { + sb = new StringBuilder(); + sb.append("Installed class '"). + append(rootClass.getSimpleName()). + append('\''). + append(" bytes="). + append(length). + append('.'); + if (bytecode.size() > 1) { + sb.append(' ').append(bytecode.size()).append(" compile units."); + } + } else { + sb = null; + } if (Timing.isEnabled()) { final long duration = System.currentTimeMillis() - t0; Timing.accumulateTime("[Code Installation]", duration); - LOG.info("Installation time: " + duration + " ms"); + if (sb != null) { + sb.append(" Install time: ").append(duration).append(" ms"); + } + } + + if (sb != null) { + LOG.info(sb.toString()); } return rootClass; diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java index 63590932..7fd77cd0 100644 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java @@ -218,8 +218,9 @@ public class FunctionNode extends Block { private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN; /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; - /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval. */ - private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL; + /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval. + * We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */ + private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL | HAS_LAZY_CHILDREN; /** What is the return type of this function? */ private Type returnType = Type.UNKNOWN; @@ -724,13 +725,10 @@ public class FunctionNode extends Block { * their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object * (since it exposes {@code arguments.callee} property) will need to have a callee parameter. * - * We also conservatively need a callee if we have lazy children, i.e. nested function nodes that have not yet - * been evaluated. _They_ may need the callee and we don't know it - * * @return true if the function's generated Java method needs a {@code callee} parameter. */ public boolean needsCallee() { - return hasLazyChildren() || needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode()); + return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode()); } /** @@ -1076,7 +1074,7 @@ public class FunctionNode extends Block { } else { this.flags |= USES_ANCESTOR_SCOPE; final FunctionNode parentFn = findParentFunction(); - if(parentFn != null) { + if (parentFn != null) { parentFn.setUsesScopeSymbol(symbol); } } diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java index a9835f6f..e0bb9ece 100644 --- a/src/jdk/nashorn/internal/objects/Global.java +++ b/src/jdk/nashorn/internal/objects/Global.java @@ -1528,8 +1528,9 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { addOwnProperty(ScriptingFunctions.EXIT_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED); } - private void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) { + private static void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) { AccessController.doPrivileged(new PrivilegedAction() { + @Override public Void run() { for (Field f : scriptEnv.getClass().getFields()) { try { diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java index bfc96abf..166fed91 100644 --- a/src/jdk/nashorn/internal/parser/Parser.java +++ b/src/jdk/nashorn/internal/parser/Parser.java @@ -126,7 +126,7 @@ public class Parser extends AbstractParser { /** Namespace for function names where not explicitly given */ private final Namespace namespace; - private static DebugLogger LOG = new DebugLogger("parser"); + private static final DebugLogger LOG = new DebugLogger("parser"); /** * Constructor diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java index 151615c0..0ca2c47a 100644 --- a/src/jdk/nashorn/internal/runtime/Context.java +++ b/src/jdk/nashorn/internal/runtime/Context.java @@ -744,6 +744,7 @@ public final class Context { global = (GlobalObject)Context.getGlobalTrusted(); script = global.findCachedClass(source); if (script != null) { + Compiler.LOG.fine("Code cache hit for " + source + " avoiding recompile."); return script; } } diff --git a/src/jdk/nashorn/internal/runtime/ScriptLoader.java b/src/jdk/nashorn/internal/runtime/ScriptLoader.java index adecd8a3..370faf31 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptLoader.java +++ b/src/jdk/nashorn/internal/runtime/ScriptLoader.java @@ -60,8 +60,7 @@ final class ScriptLoader extends NashornLoader { synchronized Class installClass(final String name, final byte[] data, final CodeSource cs) { if (cs == null) { return defineClass(name, data, 0, data.length, new ProtectionDomain(null, getPermissions(null))); - } else { - return defineClass(name, data, 0, data.length, cs); } + return defineClass(name, data, 0, data.length, cs); } } diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java index ba599da1..7b0d6d76 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java @@ -412,6 +412,7 @@ public final class JavaAdapterFactory { public static MethodHandle getConstructor(final Class sourceType, final Class targetType) throws Exception { final StaticClass adapterClass = getAdapterClassFor(new Class[] { targetType }); return AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override public MethodHandle run() throws Exception { return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(NashornCallSiteDescriptor.get( "dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false, diff --git a/src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java b/src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java index 7c1dd8b4..ecbc8bc9 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java +++ b/src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java @@ -95,14 +95,14 @@ public class DefaultRegExp extends RegExp { return null; // never matches or similar, e.g. a[] } - RegExpMatcher matcher = this.matcher; + RegExpMatcher currentMatcher = this.matcher; - if (matcher == null || matcher.getInput() != str) { - matcher = new DefaultMatcher(str); - this.matcher = matcher; + if (currentMatcher == null || matcher.getInput() != str) { + currentMatcher = new DefaultMatcher(str); + this.matcher = currentMatcher; } - return matcher; + return currentMatcher; } class DefaultMatcher implements RegExpMatcher { diff --git a/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java b/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java index f8c35bfe..719f6c39 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java +++ b/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java @@ -97,14 +97,14 @@ public class JoniRegExp extends RegExp { return null; } - RegExpMatcher matcher = this.matcher; + RegExpMatcher currentMatcher = this.matcher; - if (matcher == null || input != matcher.getInput()) { - matcher = new JoniMatcher(input); - this.matcher = matcher; + if (currentMatcher == null || input != currentMatcher.getInput()) { + currentMatcher = new JoniMatcher(input); + this.matcher = currentMatcher; } - return matcher; + return currentMatcher; } /** diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExp.java b/src/jdk/nashorn/internal/runtime/regexp/RegExp.java index a4274f6a..ff694b90 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/RegExp.java +++ b/src/jdk/nashorn/internal/runtime/regexp/RegExp.java @@ -156,7 +156,7 @@ public abstract class RegExp { * * @param key the message key * @param str string argument - * @throws jdk.nashorn.internal.runtime.ParserException + * @throws jdk.nashorn.internal.runtime.ParserException unconditionally */ protected static void throwParserException(final String key, final String str) throws ParserException { throw new ParserException(ECMAErrors.getMessage("parser.error.regex." + key, str)); diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java index 367cc85b..6ff66f21 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java +++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.runtime.regexp; -import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.runtime.ParserException; import jdk.nashorn.internal.runtime.options.Options; @@ -35,7 +34,6 @@ import jdk.nashorn.internal.runtime.options.Options; */ public class RegExpFactory { - private final static RegExpFactory instance; private final static String JDK = "jdk"; @@ -60,7 +58,8 @@ public class RegExpFactory { * Creates a Regular expression from the given {@code pattern} and {@code flags} strings. * * @param pattern RegExp pattern string - * @param flags RegExp flags string + * @param flags RegExp flags string + * @return new RegExp * @throws ParserException if flags is invalid or pattern string has syntax error. */ protected RegExp compile(final String pattern, final String flags) throws ParserException { @@ -71,8 +70,8 @@ public class RegExpFactory { * Compile a regexp with the given {@code source} and {@code flags}. * * @param pattern RegExp pattern string - * @param flags flag string - * + * @param flags flag string + * @return new RegExp * @throws ParserException if invalid source or flags */ public static RegExp create(final String pattern, final String flags) { diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExpResult.java b/src/jdk/nashorn/internal/runtime/regexp/RegExpResult.java index ff838b6f..cd81be3c 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/RegExpResult.java +++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpResult.java @@ -80,11 +80,11 @@ public final class RegExpResult { /** * Get the group with the given index or the empty string if group index is not valid. - * @param index the group index + * @param groupIndex the group index * @return the group or "" */ - public Object getGroup(int index) { - return index >= 0 && index < groups.length ? groups[index] : ""; + public Object getGroup(final int groupIndex) { + return groupIndex >= 0 && groupIndex < groups.length ? groups[groupIndex] : ""; } /** diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java b/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java index b579865b..e8c60c4a 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java +++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java @@ -182,8 +182,6 @@ final class RegExpScanner extends Scanner { * @return Committed token */ private boolean commit(final int n) { - final int startIn = position; - switch (n) { case 1: sb.append(ch0); diff --git a/test/script/basic/compile-octane.js.EXPECTED b/test/script/basic/compile-octane.js.EXPECTED index dab419e6..39f866c2 100644 --- a/test/script/basic/compile-octane.js.EXPECTED +++ b/test/script/basic/compile-octane.js.EXPECTED @@ -16,6 +16,9 @@ Compiled OK: earley-boyer.js Compiling... gbemu.js Compiled OK: gbemu.js +Compiling... mandreel.js +Compiled OK: mandreel.js + Compiling... navier-stokes.js Compiled OK: navier-stokes.js diff --git a/test/script/basic/run-octane.js b/test/script/basic/run-octane.js index 42bf77fa..315451cb 100644 --- a/test/script/basic/run-octane.js +++ b/test/script/basic/run-octane.js @@ -31,7 +31,8 @@ var tests = [ "crypto.js", "deltablue.js", "earley-boyer.js", - "gbemu.js", + "gbemu.js", + "mandreel.js", "navier-stokes.js", "pdfjs.js", "raytrace.js", @@ -49,6 +50,12 @@ var ignoreTeardown = [ { name: "gbemu.js" }, ]; + +//TODO mandreel can be compiled as a test, but not run multiple times unless modified to not have global state +var compileOnly = { + "mandreel.js" : true +}; + var dir = (typeof(__DIR__) == 'undefined') ? "test/script/basic/" : __DIR__; // TODO: why is this path hard coded when it's defined in project properties? @@ -63,6 +70,10 @@ function endsWith(str, suffix) { return str.indexOf(suffix, str.length - suffix.length) !== -1; } +function should_compile_only(name) { + return (typeof compile_only !== 'undefined') || compileOnly[name] === true; +} + function run_one_benchmark(arg, iters) { var file_name; @@ -77,14 +88,18 @@ function run_one_benchmark(arg, iters) { } file_name = file[file.length - 1]; - if (typeof compile_only !== 'undefined') { + var compile_and_return = should_compile_only(file_name); + if (compile_and_return) { + if (typeof compile_only === 'undefined') { //for a run, skip compile onlies, don't even compile them + return; + } print("Compiling... " + file_name); } load(path + 'base.js'); load(arg); - if (typeof compile_only !== 'undefined') { + if (compile_and_return) { print("Compiled OK: " + file_name); print(""); return; @@ -164,7 +179,7 @@ function run_one_benchmark(arg, iters) { function run_suite(tests, iters) { for (var idx = 0; idx < tests.length; idx++) { - run_one_benchmark(tests[idx], iters, false); + run_one_benchmark(tests[idx], iters); } } diff --git a/test/script/basic/runsunspider-eager.js b/test/script/basic/runsunspider-eager.js new file mode 100644 index 00000000..db358d28 --- /dev/null +++ b/test/script/basic/runsunspider-eager.js @@ -0,0 +1,33 @@ +/* + * 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. + * + * 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. + */ + +/** + * runsunspider : runs the sunspider tests and checks for compliance + * + * @test + * @option -timezone=PST + * @runif external.sunspider + */ + +load(__DIR__ + "runsunspider.js"); + diff --git a/test/script/basic/runsunspider-lazy.js b/test/script/basic/runsunspider-lazy.js new file mode 100644 index 00000000..6e24c0c5 --- /dev/null +++ b/test/script/basic/runsunspider-lazy.js @@ -0,0 +1,34 @@ +/* + * 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. + * + * 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. + */ + +/** + * runsunspider : runs the sunspider tests and checks for compliance + * + * @test + * @option -timezone=PST + * @option --lazy-compilation + * @runif external.sunspider + */ + +load(__DIR__ + "runsunspider.js"); + diff --git a/test/script/basic/runsunspider.js b/test/script/basic/runsunspider.js index 7b0d732c..7f787975 100644 --- a/test/script/basic/runsunspider.js +++ b/test/script/basic/runsunspider.js @@ -24,39 +24,11 @@ /** * runsunspider : runs the sunspider tests and checks for compliance * - * @test - * @option -timezone=PST - * @runif external.sunspider - */ - -/* - * Copyright (c) 2010-2011, 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. + * @subtest */ /** * This is not a test, but a test "framework" for running sunspider tests. - * */ function assertEq(a, b) { -- cgit v1.2.3 From e4ac7309883cd8bb06fd6b2f594354605fe166fc Mon Sep 17 00:00:00 2001 From: lagergren Date: Fri, 15 Mar 2013 16:07:13 +0100 Subject: 8010147: Forgot to add EXPECTED files for lazy and eager sunspider test Reviewed-by: sundar, jlaskey --- test/script/basic/runsunspider-eager.js.EXPECTED | 1 + test/script/basic/runsunspider-lazy.js.EXPECTED | 1 + test/script/basic/runsunspider.js.EXPECTED | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test/script/basic/runsunspider-eager.js.EXPECTED create mode 100644 test/script/basic/runsunspider-lazy.js.EXPECTED delete mode 100644 test/script/basic/runsunspider.js.EXPECTED diff --git a/test/script/basic/runsunspider-eager.js.EXPECTED b/test/script/basic/runsunspider-eager.js.EXPECTED new file mode 100644 index 00000000..dd360839 --- /dev/null +++ b/test/script/basic/runsunspider-eager.js.EXPECTED @@ -0,0 +1 @@ +Sunspider finished! diff --git a/test/script/basic/runsunspider-lazy.js.EXPECTED b/test/script/basic/runsunspider-lazy.js.EXPECTED new file mode 100644 index 00000000..dd360839 --- /dev/null +++ b/test/script/basic/runsunspider-lazy.js.EXPECTED @@ -0,0 +1 @@ +Sunspider finished! diff --git a/test/script/basic/runsunspider.js.EXPECTED b/test/script/basic/runsunspider.js.EXPECTED deleted file mode 100644 index dd360839..00000000 --- a/test/script/basic/runsunspider.js.EXPECTED +++ /dev/null @@ -1 +0,0 @@ -Sunspider finished! -- cgit v1.2.3 From 8288cf0ce86d56f7eebf2b712262e9e48b168e17 Mon Sep 17 00:00:00 2001 From: sundar Date: Fri, 15 Mar 2013 21:52:40 +0530 Subject: 8010145: removed workaround "init.js" in nashorn repo Reviewed-by: jlaskey, lagergren --- src/jdk/nashorn/api/scripting/Formatter.java | 10 +- .../nashorn/api/scripting/NashornScriptEngine.java | 8 - src/jdk/nashorn/api/scripting/ScriptUtils.java | 12 + src/jdk/nashorn/api/scripting/resources/engine.js | 46 + src/jdk/nashorn/api/scripting/resources/init.js | 939 --------------------- 5 files changed, 63 insertions(+), 952 deletions(-) delete mode 100644 src/jdk/nashorn/api/scripting/resources/init.js diff --git a/src/jdk/nashorn/api/scripting/Formatter.java b/src/jdk/nashorn/api/scripting/Formatter.java index 3b47d347..5cb19ed4 100644 --- a/src/jdk/nashorn/api/scripting/Formatter.java +++ b/src/jdk/nashorn/api/scripting/Formatter.java @@ -46,7 +46,7 @@ import java.util.regex.Pattern; *

Pattern and the logic for parameter position: java.util.Formatter * */ -public final class Formatter { +final class Formatter { private Formatter() { } @@ -59,8 +59,8 @@ public final class Formatter { * @param args arguments referenced by the format specifiers in format * @return a formatted string */ - public static String format(final String format, final Object[] args) { - Matcher m = FS_PATTERN.matcher(format); + static String format(final String format, final Object[] args) { + final Matcher m = FS_PATTERN.matcher(format); int positionalParameter = 1; while (m.find()) { @@ -143,7 +143,7 @@ public final class Formatter { /** * Method to parse the integer of the argument index. * - * @param s + * @param s string to parse * @return -1 if parsing failed, 0 if string is null, > 0 integer */ private static int index(final String s) { @@ -166,7 +166,7 @@ public final class Formatter { * Method to check if a string contains '<'. This is used to find out if * previous parameter is used. * - * @param s + * @param s string to check * @return true if '<' is in the string, else false */ private static boolean isPreviousArgument(final String s) { diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java index ce16a7e8..4fa6033c 100644 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java @@ -394,14 +394,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C setContextVariables(ctxt); final Object val = ctxt.getAttribute(ScriptEngine.FILENAME); final String fileName = (val != null) ? val.toString() : ""; - - // NOTE: FIXME: If this is jrunscript's init.js, we want to run the replacement. - // This should go away once we fix jrunscript's copy of init.js. - if ("".equals(fileName)) { - evalSupportScript("resources/init.js", "nashorn:engine/resources/init.js"); - return null; - } - Object res = ScriptRuntime.apply(script, ctxtGlobal); return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal)); } catch (final Exception e) { diff --git a/src/jdk/nashorn/api/scripting/ScriptUtils.java b/src/jdk/nashorn/api/scripting/ScriptUtils.java index 63358bd7..ccd5879b 100644 --- a/src/jdk/nashorn/api/scripting/ScriptUtils.java +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java @@ -45,4 +45,16 @@ public final class ScriptUtils { public static String parse(final String code, final String name, final boolean includeLoc) { return ScriptRuntime.parse(code, name, includeLoc); } + + /** + * Method which converts javascript types to java types for the + * String.format method (jrunscript function sprintf). + * + * @param format a format string + * @param args arguments referenced by the format specifiers in format + * @return a formatted string + */ + public static String format(final String format, final Object[] args) { + return Formatter.format(format, args); + } } diff --git a/src/jdk/nashorn/api/scripting/resources/engine.js b/src/jdk/nashorn/api/scripting/resources/engine.js index 65b82dfe..e9560728 100644 --- a/src/jdk/nashorn/api/scripting/resources/engine.js +++ b/src/jdk/nashorn/api/scripting/resources/engine.js @@ -46,3 +46,49 @@ function print(str) { } writer.println(String(str)); } + +/** + * This is C-like printf + * + * @param format string to format the rest of the print items + * @param args variadic argument list + */ +Object.defineProperty(this, "printf", { + configurable: true, + enumerable: false, + writable: true, + value: function (format, args/*, more args*/) { + print(sprintf.apply(this, arguments)); + } +}); + +/** + * This is C-like sprintf + * + * @param format string to format the rest of the print items + * @param args variadic argument list + */ +Object.defineProperty(this, "sprintf", { + configurable: true, + enumerable: false, + writable: true, + value: function (format, args/*, more args*/) { + var len = arguments.length - 1; + var array = []; + + if (len < 0) { + return ""; + } + + for (var i = 0; i < len; i++) { + if (arguments[i+1] instanceof Date) { + array[i] = arguments[i+1].getTime(); + } else { + array[i] = arguments[i+1]; + } + } + + array = Java.toJavaArray(array); + return Packages.jdk.nashorn.api.scripting.ScriptUtils.format(format, array); + } +}); diff --git a/src/jdk/nashorn/api/scripting/resources/init.js b/src/jdk/nashorn/api/scripting/resources/init.js deleted file mode 100644 index 18cde929..00000000 --- a/src/jdk/nashorn/api/scripting/resources/init.js +++ /dev/null @@ -1,939 +0,0 @@ -/* - * Copyright (c) 2005, 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. - */ - -/** - * jrunscript JavaScript built-in functions and objects. - */ - -/** - * Creates an object that delegates all method calls on - * it to the 'invoke' method on the given delegate object.
- * - * Example: - *

- * 
- *     var x  = { invoke: function(name, args) { //code...}
- *     var y = new JSInvoker(x);
- *     y.func(3, 3); // calls x.invoke('func', args); where args is array of arguments
- * 
- * 
- * @param obj object to be wrapped by JSInvoker - * @constructor - */ -function JSInvoker(obj) { - return new JSAdapter({ - __get__ : function(name) { - return function() { - return obj.invoke(name, arguments); - } - } - }); -} - -/** - * This variable represents OS environment. Environment - * variables can be accessed as fields of this object. For - * example, env.PATH will return PATH value configured. - */ -var env = new JSAdapter({ - __get__ : function (name) { - return java.lang.System.getenv(name); - }, - __has__ : function (name) { - return java.lang.System.getenv().containsKey(name); - }, - __getIds__ : function() { - return java.lang.System.getenv().keySet().toArray(); - }, - __delete__ : function(name) { - println("can't delete env item"); - }, - __put__ : function (name, value) { - println("can't change env item"); - }, - toString: function() { - return java.lang.System.getenv().toString(); - } -}); - -/** - * Creates a convenient script object to deal with java.util.Map instances. - * The result script object's field names are keys of the Map. For example, - * scriptObj.keyName can be used to access value associated with given key.
- * Example: - *
- * 
- *     var x = java.lang.SystemProperties();
- *     var y = jmap(x);
- *     println(y['java.class.path']); // prints java.class.path System property
- *     delete y['java.class.path']; // remove java.class.path System property
- * 
- * 
- * - * @param map java.util.Map instance that will be wrapped - * @constructor - */ -function jmap(map) { - return new JSAdapter({ - __get__ : function(name) { - if (map.containsKey(name)) { - return map.get(name); - } else { - return undefined; - } - }, - __has__ : function(name) { - return map.containsKey(name); - }, - - __delete__ : function (name) { - return map.remove(name); - }, - __put__ : function(name, value) { - map.put(name, value); - }, - __getIds__ : function() { - return map.keySet().toArray(); - }, - toString: function() { - return map.toString(); - } - }); -} - -/** - * Creates a convenient script object to deal with java.util.List instances. - * The result script object behaves like an array. For example, - * scriptObj[index] syntax can be used to access values in the List instance. - * 'length' field gives size of the List.
- * - * Example: - *
- * 
- *    var x = new java.util.ArrayList(4);
- *    x.add('Java');
- *    x.add('JavaScript');
- *    x.add('SQL');
- *    x.add('XML');
- *
- *    var y = jlist(x);
- *    println(y[2]); // prints third element of list
- *    println(y.length); // prints size of the list
- *
- * @param map java.util.List instance that will be wrapped
- * @constructor
- */
-function jlist(list) {
-    function isValid(index) {
-        return typeof(index) == 'number' &&
-            index > -1 && index < list.size();
-    }
-    return new JSAdapter({
-        __get__ :  function(name) {
-            if (isValid(name)) {
-                return list.get(name);
-            } else if (name == 'length') {
-                return list.size();
-            } else {
-                return undefined;
-            }
-        },
-        __has__ : function (name) {
-            return isValid(name) || name == 'length';
-        },
-        __delete__ : function(name) {
-            if (isValid(name)) {
-                list.remove(name);
-            }
-        },
-        __put__ : function(name, value) {
-            if (isValid(name)) {
-                list.set(name, value);
-            }
-        },
-        __getIds__: function() {
-            var res = new Array(list.size());
-            for (var i = 0; i < res.length; i++) {
-                res[i] = i;
-            }
-            return res;
-        },
-        toString: function() {
-            return list.toString();
-        }
-    });
-}
-
-/**
- * This is java.lang.System properties wrapped by JSAdapter.
- * For eg. to access java.class.path property, you can use
- * the syntax sysProps["java.class.path"]
- */
-var sysProps = new JSAdapter({
-    __get__ : function (name) {
-        return java.lang.System.getProperty(name);
-    },
-    __has__ : function (name) {
-        return java.lang.System.getProperty(name) != null;
-    },
-    __getIds__ : function() {
-        return java.lang.System.getProperties().keySet().toArray();
-    },
-    __delete__ : function(name) {
-        java.lang.System.clearProperty(name);
-        return true;
-    },
-    __put__ : function (name, value) {
-        java.lang.System.setProperty(name, value);
-    },
-    toString: function() {
-        return "";
-    }
-});
-
-// stdout, stderr & stdin
-var out = java.lang.System.out;
-var err = java.lang.System.err;
-// can't use 'in' because it is a JavaScript keyword :-(
-var inp = java.lang.System["in"];
-
-var BufferedInputStream = java.io.BufferedInputStream;
-var BufferedOutputStream = java.io.BufferedOutputStream;
-var BufferedReader = java.io.BufferedReader;
-var DataInputStream = java.io.DataInputStream;
-var File = java.io.File;
-var FileInputStream = java.io.FileInputStream;
-var FileOutputStream = java.io.FileOutputStream;
-var InputStream = java.io.InputStream;
-var InputStreamReader = java.io.InputStreamReader;
-var OutputStream = java.io.OutputStream;
-var Reader = java.io.Reader;
-var URL = java.net.URL;
-
-/**
- * Generic any object to input stream mapper
- * @param str input file name, URL or InputStream
- * @return InputStream object
- * @private
- */
-function inStream(str) {
-    if (typeof(str) == "string") {
-        // '-' means standard input
-        if (str == '-') {
-            return java.lang.System["in"];
-        }
-        // try file first
-        var file = null;
-        try {
-            file = pathToFile(str);
-        } catch (e) {
-        }
-        if (file && file.exists()) {
-            return new FileInputStream(file);
-        } else {
-            try {
-                // treat the string as URL
-                return new URL(str).openStream();
-            } catch (e) {
-                throw 'file or URL ' + str + ' not found';
-            }
-        }
-    } else {
-        if (str instanceof InputStream) {
-            return str;
-        } else if (str instanceof URL) {
-            return str.openStream();
-        } else if (str instanceof File) {
-            return new FileInputStream(str);
-        }
-    }
-    // everything failed, just give input stream
-    return java.lang.System["in"];
-}
-
-/**
- * Generic any object to output stream mapper
- *
- * @param out output file name or stream
- * @return OutputStream object
- * @private
- */
-function outStream(out) {
-    if (typeof(out) == "string") {
-        if (out == '>') {
-            return java.lang.System.out;
-        } else {
-            // treat it as file
-            return new FileOutputStream(pathToFile(out));
-        }
-    } else {
-        if (out instanceof OutputStream) {
-            return out;
-        } else if (out instanceof File) {
-            return new FileOutputStream(out);
-        }
-    }
-
-    // everything failed, just return System.out
-    return java.lang.System.out;
-}
-
-/**
- * stream close takes care not to close stdin, out & err.
- * @private
- */
-function streamClose(stream) {
-    if (stream) {
-        if (stream != java.lang.System["in"] &&
-            stream != java.lang.System.out &&
-            stream != java.lang.System.err) {
-            try {
-                stream.close();
-            } catch (e) {
-                println(e);
-            }
-        }
-    }
-}
-
-/**
- * Loads and evaluates JavaScript code from a stream or file or URL
- * - * Examples: - *
- * 
- *    load('test.js'); // load script file 'test.js'
- *    load('http://java.sun.com/foo.js'); // load from a URL
- * 
- * 
- * - * @param str input from which script is loaded and evaluated - */ -if (typeof(load) == 'undefined') { - var load = function(str) { - var stream = inStream(str); - var bstream = new BufferedInputStream(stream); - var reader = new BufferedReader(new InputStreamReader(bstream)); - var oldFilename = engine.get(engine.FILENAME); - engine.put(engine.FILENAME, str); - try { - engine.eval(reader); - } finally { - engine.put(engine.FILENAME, oldFilename); - streamClose(stream); - } - } -} - -// file system utilities - -/** - * Creates a Java byte[] of given length - * @param len size of the array to create - * @private - */ -function javaByteArray(len) { - return java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len); -} - -var curDir = new File('.'); - -/** - * Print present working directory - */ -function pwd() { - println(curDir.getAbsolutePath()); -} - -/** - * Changes present working directory to given directory - * @param target directory to change to. optional, defaults to user's HOME - */ -function cd(target) { - if (target == undefined) { - target = sysProps["user.home"]; - } - if (!(target instanceof File)) { - target = pathToFile(target); - } - if (target.exists() && target.isDirectory()) { - curDir = target; - } else { - println(target + " is not a directory"); - } -} - -/** - * Converts path to java.io.File taking care of shell present working dir - * - * @param pathname file path to be converted - * @private - */ -function pathToFile(pathname) { - var tmp = pathname; - if (!(tmp instanceof File)) { - tmp = new File(tmp); - } - if (!tmp.isAbsolute()) { - return new File(curDir, pathname); - } else { - return tmp; - } -} - -/** - * Copies a file or URL or stream to another file or stream - * - * @param from input file or URL or stream - * @param to output stream or file - */ -function cp(from, to) { - if (from == to) { - println("file " + from + " cannot be copied onto itself!"); - return; - } - var inp = inStream(from); - var out = outStream(to); - var binp = new BufferedInputStream(inp); - var bout = new BufferedOutputStream(out); - var buff = javaByteArray(1024); - var len; - while ((len = binp.read(buff)) > 0 ) - bout.write(buff, 0, len); - - bout.flush(); - streamClose(inp); - streamClose(out); -} - -/** - * Shows the content of a file or URL or any InputStream
- * Examples: - *
- * 
- *    cat('test.txt'); // show test.txt file contents
- *    cat('http://java.net'); // show the contents from the URL http://java.net
- * 
- * 
- * @param obj input to show - * @param pattern optional. show only the lines matching the pattern - */ -function cat(obj, pattern) { - if (obj instanceof File && obj.isDirectory()) { - ls(obj); - return; - } - - var inp = null; - if (!(obj instanceof Reader)) { - inp = inStream(obj); - obj = new BufferedReader(new InputStreamReader(inp)); - } - var line; - if (pattern) { - var count = 1; - while ((line=obj.readLine()) != null) { - if (line.match(pattern)) { - println(count + "\t: " + line); - } - count++; - } - } else { - while ((line=obj.readLine()) != null) { - println(line); - } - } -} - -/** - * Returns directory part of a filename - * - * @param pathname input path name - * @return directory part of the given file name - */ -function dirname(pathname) { - var dirName = "."; - // Normalize '/' to local file separator before work. - var i = pathname.replace('/', File.separatorChar ).lastIndexOf( - File.separator ); - if ( i != -1 ) - dirName = pathname.substring(0, i); - return dirName; -} - -/** - * Creates a new dir of given name - * - * @param dir name of the new directory - */ -function mkdir(dir) { - dir = pathToFile(dir); - println(dir.mkdir()? "created" : "can not create dir"); -} - -/** - * Creates the directory named by given pathname, including - * any necessary but nonexistent parent directories. - * - * @param dir input path name - */ -function mkdirs(dir) { - dir = pathToFile(dir); - println(dir.mkdirs()? "created" : "can not create dirs"); -} - -/** - * Removes a given file - * - * @param pathname name of the file - */ -function rm(pathname) { - var file = pathToFile(pathname); - if (!file.exists()) { - println("file not found: " + pathname); - return false; - } - // note that delete is a keyword in JavaScript! - println(file["delete"]()? "deleted" : "can not delete"); -} - -/** - * Removes a given directory - * - * @param pathname name of the directory - */ -function rmdir(pathname) { - rm(pathname); -} - -/** - * Synonym for 'rm' - */ -function del(pathname) { - rm(pathname); -} - -/** - * Moves a file to another - * - * @param from original name of the file - * @param to new name for the file - */ -function mv(from, to) { - println(pathToFile(from).renameTo(pathToFile(to))? - "moved" : "can not move"); -} - -/** - * Synonym for 'mv'. - */ -function ren(from, to) { - mv(from, to); -} - -var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; - -/** - * Helper function called by ls - * @private - */ -function printFile(f) { - var sb = new java.lang.StringBuffer(); - sb.append(f.isDirectory()? "d" : "-"); - sb.append(f.canRead() ? "r": "-" ); - sb.append(f.canWrite() ? "w": "-" ); - sb.append(" "); - - var d = new java.util.Date(f.lastModified()); - var c = new java.util.GregorianCalendar(); - c.setTime(d); - var day = c.get(java.util.Calendar.DAY_OF_MONTH); - sb.append(months[c.get(java.util.Calendar.MONTH)] - + " " + day ); - if (day < 10) { - sb.append(" "); - } - - // to get fixed length 'length' field - var fieldlen = 8; - var len = new java.lang.StringBuffer(); - for(var j=0; j - * - * Examples: - *
- * 
- *    find('.')
- *    find('.', '.*\.class', rm);  // remove all .class files
- *    find('.', '.*\.java');       // print fullpath of each .java file
- *    find('.', '.*\.java', cat);  // print all .java files
- * 
- * 
- * - * @param dir directory to search files - * @param pattern to search in the files - * @param callback function to call for matching files - */ -function find(dir, pattern, callback) { - dir = pathToFile(dir); - if (!callback) callback = print; - var files = dir.listFiles(); - for (var f in files) { - var file = files[f]; - if (file.isDirectory()) { - find(file, pattern, callback); - } else { - if (pattern) { - if (file.getName().match(pattern)) { - callback(file); - } - } else { - callback(file); - } - } - } -} - -// process utilities - -/** - * Exec's a child process, waits for completion & returns exit code - * - * @param cmd command to execute in child process - */ -function exec(cmd) { - var process = java.lang.Runtime.getRuntime().exec(cmd); - var inp = new DataInputStream(process.getInputStream()); - var line = null; - while ((line = inp.readLine()) != null) { - println(line); - } - process.waitFor(); - $exit = process.exitValue(); -} - -// XML utilities - -/** - * Converts input to DOM Document object - * - * @param inp file or reader. optional, without this param, - * this function returns a new DOM Document. - * @return returns a DOM Document object - */ -function XMLDocument(inp) { - var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance(); - var builder = factory.newDocumentBuilder(); - if (inp) { - if (typeof(inp) == "string") { - return builder.parse(pathToFile(inp)); - } else { - return builder.parse(inp); - } - } else { - return builder.newDocument(); - } -} - -/** - * Converts arbitrary stream, file, URL to XMLSource - * - * @param inp input stream or file or URL - * @return XMLSource object - */ -function XMLSource(inp) { - if (inp instanceof javax.xml.transform.Source) { - return inp; - } else if (inp instanceof Packages.org.w3c.dom.Document) { - return new javax.xml.transform.dom.DOMSource(inp); - } else { - inp = new BufferedInputStream(inStream(inp)); - return new javax.xml.transform.stream.StreamSource(inp); - } -} - -/** - * Converts arbitrary stream, file to XMLResult - * - * @param inp output stream or file - * @return XMLResult object - */ -function XMLResult(out) { - if (out instanceof javax.xml.transform.Result) { - return out; - } else if (out instanceof Packages.org.w3c.dom.Document) { - return new javax.xml.transform.dom.DOMResult(out); - } else { - out = new BufferedOutputStream(outStream(out)); - return new javax.xml.transform.stream.StreamResult(out); - } -} - -/** - * Perform XSLT transform - * - * @param inp Input XML to transform (URL, File or InputStream) - * @param style XSL Stylesheet to be used (URL, File or InputStream). optional. - * @param out Output XML (File or OutputStream - */ -function XSLTransform(inp, style, out) { - switch (arguments.length) { - case 2: - inp = arguments[0]; - out = arguments[1]; - break; - case 3: - inp = arguments[0]; - style = arguments[1]; - out = arguments[2]; - break; - default: - println("XSL tranform requires 2 or 3 arguments"); - return; - } - - var factory = javax.xml.transform.TransformerFactory.newInstance(); - var transformer; - if (style) { - transformer = factory.newTransformer(XMLSource(style)); - } else { - transformer = factory.newTransformer(); - } - var source = XMLSource(inp); - var result = XMLResult(out); - transformer.transform(source, result); - if (source.getInputStream) { - streamClose(source.getInputStream()); - } - if (result.getOutputStream) { - streamClose(result.getOutputStream()); - } -} - -// miscellaneous utilities - -/** - * Prints which command is selected from PATH - * - * @param cmd name of the command searched from PATH - */ -function which(cmd) { - var st = new java.util.StringTokenizer(env.PATH, File.pathSeparator); - while (st.hasMoreTokens()) { - var file = new File(st.nextToken(), cmd); - if (file.exists()) { - println(file.getAbsolutePath()); - return; - } - } -} - -/** - * Prints IP addresses of given domain name - * - * @param name domain name - */ -function ip(name) { - var addrs = InetAddress.getAllByName(name); - for (var i in addrs) { - println(addrs[i]); - } -} - -/** - * Prints current date in current locale - */ -function date() { - println(new Date().toLocaleString()); -} - -/** - * Echoes the given string arguments - */ -function echo(x) { - for (var i = 0; i < arguments.length; i++) { - println(arguments[i]); - } -} - -/** - * Reads one or more lines from stdin after printing prompt - * - * @param prompt optional, default is '>' - * @param multiline to tell whether to read single line or multiple lines - */ -function read(prompt, multiline) { - if (!prompt) { - prompt = '>'; - } - var inp = java.lang.System["in"]; - var reader = new BufferedReader(new InputStreamReader(inp)); - if (multiline) { - var line = ''; - while (true) { - java.lang.System.err.print(prompt); - java.lang.System.err.flush(); - var tmp = reader.readLine(); - if (tmp == '' || tmp == null) break; - line += tmp + '\n'; - } - return line; - } else { - java.lang.System.err.print(prompt); - java.lang.System.err.flush(); - return reader.readLine(); - } -} - -if (typeof(println) == 'undefined') { - var print = function(str, newline) { - if (typeof(str) == 'undefined') { - str = 'undefined'; - } else if (str == null) { - str = 'null'; - } - - if (!(out instanceof java.io.PrintWriter)) { - out = new java.io.PrintWriter(out); - } - - out.print(String(str)); - if (newline) { - out.print('\n'); - } - out.flush(); - } - - var println = function(str) { - print(str, true); - }; -} - -/** - * This is C-like printf - * - * @param format string to format the rest of the print items - * @param args variadic argument list - */ -function printf(format, args/*, more args*/) { - print(sprintf.apply(this, arguments)); -} - -/** - * This is C-like sprintf - * - * @param format string to format the rest of the print items - * @param args variadic argument list - */ -function sprintf(format, args/*, more args*/) { - var len = arguments.length - 1; - var array = []; - - if (len < 0) { - return ""; - } - - for (var i = 0; i < len; i++) { - if (arguments[i+1] instanceof Date) { - array[i] = arguments[i+1].getTime(); - } else { - array[i] = arguments[i+1]; - } - } - - array = Java.toJavaArray(array); - return Packages.jdk.nashorn.api.scripting.Formatter.format(format, array); -} -- cgit v1.2.3 From 987a9fa58f5b9bc46fce5becde032bc7d7ef6739 Mon Sep 17 00:00:00 2001 From: sundar Date: Mon, 18 Mar 2013 21:03:11 +0530 Subject: 8010199: javax.script.Invocable implementation for nashorn does not return null when matching functions are missing Reviewed-by: lagergren, jlaskey --- bin/jjs | 2 +- bin/jjssecure | 2 +- bin/nashorn | 2 +- bin/nashornsecure | 2 +- .../nashorn/api/scripting/NashornScriptEngine.java | 26 +++++++-- test/script/basic/JDK-8010199.js | 51 ++++++++++++++++++ .../nashorn/api/scripting/ScriptEngineTest.java | 62 ++++++++++++++++++++++ 7 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 test/script/basic/JDK-8010199.js diff --git a/bin/jjs b/bin/jjs index ca531f4d..fe6665c3 100644 --- a/bin/jjs +++ b/bin/jjs @@ -26,4 +26,4 @@ [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1; -$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $* +$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $* diff --git a/bin/jjssecure b/bin/jjssecure index 614ffd36..db6bdfc4 100644 --- a/bin/jjssecure +++ b/bin/jjssecure @@ -26,4 +26,4 @@ [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1; -$JAVA_HOME/bin/java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.security.properties=`dirname $0`/../make/java.security.override -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=true -Dnashorn.home=`dirname $0`/.. -Djava.security.manager jdk.nashorn.tools.Shell $* +$JAVA_HOME/bin/java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.security.properties=`dirname $0`/../make/java.security.override -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=true -Dnashorn.home=`dirname $0`/.. -Djava.security.manager jdk.nashorn.tools.Shell $* diff --git a/bin/nashorn b/bin/nashorn index 3fccdd04..da22be1f 100644 --- a/bin/nashorn +++ b/bin/nashorn @@ -26,4 +26,4 @@ [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1; -$JAVA_HOME/bin/jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $* +$JAVA_HOME/bin/jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $* diff --git a/bin/nashornsecure b/bin/nashornsecure index 3d02c529..77c7c529 100644 --- a/bin/nashornsecure +++ b/bin/nashornsecure @@ -26,4 +26,4 @@ [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1; -$JAVA_HOME/bin/jrunscript -J-Djava.security.properties=`dirname $0`/../make/java.security.override -J-Djava.security.manager -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $* +$JAVA_HOME/bin/jrunscript -J-Djava.security.properties=`dirname $0`/../make/java.security.override -J-Djava.security.manager -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $* diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java index 4fa6033c..55967bb0 100644 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.lang.reflect.Method; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; @@ -179,14 +180,14 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } private T getInterfaceInner(final Object self, final Class clazz) { - final Object realSelf; + final ScriptObject realSelf; final ScriptObject ctxtGlobal = getNashornGlobalFrom(context); if(self == null) { realSelf = ctxtGlobal; } else if (!(self instanceof ScriptObject)) { - realSelf = ScriptObjectMirror.unwrap(self, ctxtGlobal); + realSelf = (ScriptObject)ScriptObjectMirror.unwrap(self, ctxtGlobal); } else { - realSelf = self; + realSelf = (ScriptObject)self; } try { final ScriptObject oldGlobal = getNashornGlobal(); @@ -194,6 +195,10 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C if(oldGlobal != ctxtGlobal) { setNashornGlobal(ctxtGlobal); } + + if (! isInterfaceImplemented(clazz, realSelf)) { + return null; + } return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz).invoke(realSelf)); } finally { if(oldGlobal != ctxtGlobal) { @@ -463,6 +468,21 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } } + private static boolean isInterfaceImplemented(final Class iface, final ScriptObject sobj) { + for (final Method method : iface.getMethods()) { + // ignore methods of java.lang.Object class + if (method.getDeclaringClass() == Object.class) { + continue; + } + + Object obj = sobj.get(method.getName()); + if (! (obj instanceof ScriptFunction)) { + return false; + } + } + return true; + } + // don't make this public!! static ScriptObject getNashornGlobal() { return Context.getGlobal(); diff --git a/test/script/basic/JDK-8010199.js b/test/script/basic/JDK-8010199.js new file mode 100644 index 00000000..ddd3ba0f --- /dev/null +++ b/test/script/basic/JDK-8010199.js @@ -0,0 +1,51 @@ +/* + * 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. + * + * 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-8010199: javax.script.Invocable implementation for nashorn does not return null when matching functions are missing + * + * @test + * @run + */ + +var m = new javax.script.ScriptEngineManager(); +var e = m.getEngineByName("nashorn"); + +var iface = e.getInterface(java.lang.Runnable.class); + +if (iface != null) { + fail("Expected interface object to be null"); +} + +e.eval("var runcalled = false; function run() { runcalled = true }"); + +iface = e.getInterface(java.lang.Runnable.class); +if (iface == null) { + fail("Expected interface object to be non-null"); +} + +iface.run(); + +if (e.get("runcalled") != true) { + fail("runcalled is not true"); +} diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java index 0994b0d2..48277aa8 100644 --- a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java +++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java @@ -283,6 +283,68 @@ public class ScriptEngineTest { } } + public interface Foo { + public void bar(); + } + + public interface Foo2 extends Foo { + public void bar2(); + } + + @Test + public void getInterfaceMissingTest() { + final ScriptEngineManager manager = new ScriptEngineManager(); + final ScriptEngine engine = manager.getEngineByName("nashorn"); + + // don't define any function. + try { + engine.eval(""); + } catch (final Exception exp) { + exp.printStackTrace(); + fail(exp.getMessage()); + } + + Runnable runnable = ((Invocable)engine).getInterface(Runnable.class); + if (runnable != null) { + fail("runnable is not null!"); + } + + // now define "run" + try { + engine.eval("function run() { print('this is run function'); }"); + } catch (final Exception exp) { + exp.printStackTrace(); + fail(exp.getMessage()); + } + runnable = ((Invocable)engine).getInterface(Runnable.class); + // should not return null now! + runnable.run(); + + // define only one method of "Foo2" + try { + engine.eval("function bar() { print('bar function'); }"); + } catch (final Exception exp) { + exp.printStackTrace(); + fail(exp.getMessage()); + } + + Foo2 foo2 = ((Invocable)engine).getInterface(Foo2.class); + if (foo2 != null) { + throw new RuntimeException("foo2 is not null!"); + } + + // now define other method of "Foo2" + try { + engine.eval("function bar2() { print('bar2 function'); }"); + } catch (final Exception exp) { + exp.printStackTrace(); + fail(exp.getMessage()); + } + foo2 = ((Invocable)engine).getInterface(Foo2.class); + foo2.bar(); + foo2.bar2(); + } + @Test public void accessGlobalTest() { final ScriptEngineManager m = new ScriptEngineManager(); -- cgit v1.2.3 From 41e989d90bb27a388391bc687caa6174f71b77a0 Mon Sep 17 00:00:00 2001 From: jlaskey Date: Tue, 19 Mar 2013 11:03:24 -0300 Subject: 8009969: CodeCoverage should use template Reviewed-by: jlaskey, sundar Contributed-by: pavel.stepanov@oracle.com --- make/build.xml | 2 +- make/code_coverage.xml | 60 +++++++++++++++++++++++++++++++++++++++++++------ make/project.properties | 4 +++- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/make/build.xml b/make/build.xml index e2d8f0cc..945ccaa2 100644 --- a/make/build.xml +++ b/make/build.xml @@ -124,7 +124,7 @@ - + diff --git a/make/code_coverage.xml b/make/code_coverage.xml index 41d85ff3..33980bdf 100644 --- a/make/code_coverage.xml +++ b/make/code_coverage.xml @@ -36,7 +36,12 @@ + + + + + @@ -51,25 +56,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + - + + + + - + @@ -81,12 +127,12 @@ - + - + diff --git a/make/project.properties b/make/project.properties index c4d0b943..60444de7 100644 --- a/make/project.properties +++ b/make/project.properties @@ -235,10 +235,12 @@ jcov=dynamic #naming of CC results #NB directory specified in the cc.dir will be cleaned up!!! cc.dir=${basedir}/../Codecoverage_Nashorn -cc.result.file.name=cc_nashorn.xml +cc.result.file.name=CC_${jcov}_nashorn.xml #dynamic CC parameters; please redefine in the ${user.home}/.nashorn.project.local.properties jcov2.lib.dir=${basedir}/../jcov2/lib jcov.jar=${jcov2.lib.dir}/jcov.jar cc.include=jdk\.nashorn\.* cc.exclude=jdk\.nashorn\.internal\.scripts\.* +cc.dynamic.genereate.template=true +cc.template=${cc.dir}/CC_template.xml cc.dynamic.args=-javaagent:${jcov.jar}=include=${cc.include},exclude=${cc.exclude},type=all,verbose=0,file=${cc.dir}/${cc.result.file.name} -- cgit v1.2.3 From ebc14e4a45a24a142e037257044489785adf14e4 Mon Sep 17 00:00:00 2001 From: attila Date: Sat, 23 Mar 2013 00:58:39 +0100 Subject: 8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable Reviewed-by: jlaskey, lagergren --- make/project.properties | 2 +- src/jdk/nashorn/internal/codegen/Attr.java | 443 +++++++++++++-------- src/jdk/nashorn/internal/codegen/ClassEmitter.java | 12 +- .../nashorn/internal/codegen/CodeGenerator.java | 232 ++++++----- .../nashorn/internal/codegen/CompilationPhase.java | 49 ++- src/jdk/nashorn/internal/codegen/Compiler.java | 3 +- .../nashorn/internal/codegen/FinalizeTypes.java | 164 ++++---- .../nashorn/internal/codegen/FoldConstants.java | 14 +- .../internal/codegen/FunctionSignature.java | 2 +- src/jdk/nashorn/internal/codegen/Lower.java | 247 ++++++------ .../nashorn/internal/codegen/MethodEmitter.java | 5 +- src/jdk/nashorn/internal/codegen/Splitter.java | 81 ++-- src/jdk/nashorn/internal/codegen/WeighNodes.java | 81 ++-- src/jdk/nashorn/internal/ir/AccessNode.java | 15 +- src/jdk/nashorn/internal/ir/Assignment.java | 7 + src/jdk/nashorn/internal/ir/BaseNode.java | 13 +- src/jdk/nashorn/internal/ir/BinaryNode.java | 22 +- src/jdk/nashorn/internal/ir/Block.java | 360 +++-------------- src/jdk/nashorn/internal/ir/BreakNode.java | 4 +- src/jdk/nashorn/internal/ir/CallNode.java | 12 +- src/jdk/nashorn/internal/ir/CaseNode.java | 4 +- src/jdk/nashorn/internal/ir/CatchNode.java | 4 +- src/jdk/nashorn/internal/ir/ContinueNode.java | 4 +- src/jdk/nashorn/internal/ir/DoWhileNode.java | 4 +- src/jdk/nashorn/internal/ir/EmptyNode.java | 4 +- src/jdk/nashorn/internal/ir/ExecuteNode.java | 4 +- src/jdk/nashorn/internal/ir/ForNode.java | 4 +- src/jdk/nashorn/internal/ir/FunctionNode.java | 328 +++++---------- src/jdk/nashorn/internal/ir/IdentNode.java | 63 ++- src/jdk/nashorn/internal/ir/IfNode.java | 4 +- src/jdk/nashorn/internal/ir/IndexNode.java | 9 +- src/jdk/nashorn/internal/ir/LabelNode.java | 4 +- src/jdk/nashorn/internal/ir/LexicalContext.java | 198 +++++++++ src/jdk/nashorn/internal/ir/LineNumberNode.java | 4 +- src/jdk/nashorn/internal/ir/LiteralNode.java | 34 +- src/jdk/nashorn/internal/ir/Location.java | 6 +- src/jdk/nashorn/internal/ir/Node.java | 18 +- src/jdk/nashorn/internal/ir/ObjectNode.java | 4 +- src/jdk/nashorn/internal/ir/PropertyNode.java | 4 +- src/jdk/nashorn/internal/ir/ReferenceNode.java | 91 ----- src/jdk/nashorn/internal/ir/ReturnNode.java | 4 +- src/jdk/nashorn/internal/ir/RuntimeNode.java | 9 +- src/jdk/nashorn/internal/ir/SplitNode.java | 4 +- src/jdk/nashorn/internal/ir/SwitchNode.java | 4 +- src/jdk/nashorn/internal/ir/Symbol.java | 60 +-- src/jdk/nashorn/internal/ir/TernaryNode.java | 18 +- src/jdk/nashorn/internal/ir/ThrowNode.java | 4 +- src/jdk/nashorn/internal/ir/TryNode.java | 13 +- src/jdk/nashorn/internal/ir/TypeOverride.java | 6 +- src/jdk/nashorn/internal/ir/UnaryNode.java | 22 +- src/jdk/nashorn/internal/ir/VarNode.java | 88 ++-- src/jdk/nashorn/internal/ir/WhileNode.java | 4 +- src/jdk/nashorn/internal/ir/WithNode.java | 4 +- src/jdk/nashorn/internal/ir/debug/JSONWriter.java | 79 ++-- .../nashorn/internal/ir/debug/PrintVisitor.java | 78 ++-- .../internal/ir/visitor/NodeOperatorVisitor.java | 16 +- .../nashorn/internal/ir/visitor/NodeVisitor.java | 151 +++---- src/jdk/nashorn/internal/parser/Parser.java | 217 +++++----- src/jdk/nashorn/internal/runtime/Context.java | 2 +- .../internal/runtime/resources/Messages.properties | 4 +- test/script/basic/JDK-8006755.js | 2 +- test/script/basic/NASHORN-837.js | 24 +- .../jdk/nashorn/internal/codegen/CompilerTest.java | 12 +- 63 files changed, 1669 insertions(+), 1719 deletions(-) create mode 100644 src/jdk/nashorn/internal/ir/LexicalContext.java delete mode 100644 src/jdk/nashorn/internal/ir/ReferenceNode.java diff --git a/make/project.properties b/make/project.properties index 60444de7..58da977d 100644 --- a/make/project.properties +++ b/make/project.properties @@ -210,7 +210,7 @@ run.test.xms=2G # add '-Dtest.js.outofprocess' to run each test in a new sub-process run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8 #-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M -run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs} +run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main} run.test.jvmsecurityargs=-Xverify:all -Djava.security.properties=${basedir}/make/java.security.override -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java index a441ef4d..93805598 100644 --- a/src/jdk/nashorn/internal/codegen/Attr.java +++ b/src/jdk/nashorn/internal/codegen/Attr.java @@ -37,14 +37,16 @@ import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; import static jdk.nashorn.internal.ir.Symbol.IS_LET; import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; +import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE; import static jdk.nashorn.internal.ir.Symbol.IS_THIS; import static jdk.nashorn.internal.ir.Symbol.IS_VAR; +import static jdk.nashorn.internal.ir.Symbol.KINDMASK; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.Set; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; @@ -52,19 +54,19 @@ import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CallNode.EvalArgs; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; 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; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; @@ -117,6 +119,8 @@ final class Attr extends NodeOperatorVisitor { */ private Set localUses; + private final LexicalContext lexicalContext = new LexicalContext(); + private static final DebugLogger LOG = new DebugLogger("attr"); private static final boolean DEBUG = LOG.isEnabled(); @@ -137,14 +141,15 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final AccessNode accessNode) { + public Node leaveAccessNode(final AccessNode accessNode) { newTemporary(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this end(accessNode); return accessNode; } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { + lexicalContext.push(block); start(block); final Set savedLocalDefs = localDefs; @@ -160,9 +165,7 @@ final class Attr extends NodeOperatorVisitor { localDefs = new HashSet<>(savedLocalDefs); localUses = new HashSet<>(savedLocalUses); - for (final Node statement : block.getStatements()) { - statement.accept(this); - } + block.visitStatements(this); } finally { localDefs = savedLocalDefs; localUses = savedLocalUses; @@ -172,11 +175,12 @@ final class Attr extends NodeOperatorVisitor { end(block); + lexicalContext.pop(block); return null; } @Override - public Node enter(final CallNode callNode) { + public Node enterCallNode(final CallNode callNode) { start(callNode); callNode.getFunction().accept(this); @@ -197,8 +201,7 @@ final class Attr extends NodeOperatorVisitor { evalArgs.setThis(thisNode); } - newTemporary(Type.OBJECT, callNode); // object type here, access specialization in FinalizeTypes may narrow it later - newType(callNode.getFunction().getSymbol(), Type.OBJECT); + newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later end(callNode); @@ -206,29 +209,106 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final CatchNode catchNode) { + public Node enterCatchNode(final CatchNode catchNode) { final IdentNode exception = catchNode.getException(); final Block block = getCurrentBlock(); start(catchNode); // define block-local exception variable - final Symbol def = block.defineSymbol(exception.getName(), IS_VAR | IS_LET, exception); + final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception); newType(def, Type.OBJECT); addLocalDef(exception.getName()); return catchNode; } + /** + * Declare the definition of a new symbol. + * + * @param name Name of symbol. + * @param symbolFlags Symbol flags. + * @param node Defining Node. + * + * @return Symbol for given name or null for redefinition. + */ + private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) { + int flags = symbolFlags; + Symbol symbol = findSymbol(block, name); // Locate symbol. + + if ((flags & KINDMASK) == IS_GLOBAL) { + flags |= IS_SCOPE; + } + + final FunctionNode function = lexicalContext.getFunction(block); + if (symbol != null) { + // Symbol was already defined. Check if it needs to be redefined. + if ((flags & KINDMASK) == IS_PARAM) { + if (!isLocal(function, symbol)) { + // Not defined in this function. Create a new definition. + symbol = null; + } else if (symbol.isParam()) { + // Duplicate parameter. Null return will force an error. + assert false : "duplicate parameter"; + return null; + } + } else if ((flags & KINDMASK) == IS_VAR) { + if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) { + assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block"; + // Always create a new definition. + symbol = null; + } else { + // Not defined in this function. Create a new definition. + if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { + symbol = null; + } + } + } + } + + if (symbol == null) { + // If not found, then create a new one. + Block symbolBlock; + + // Determine where to create it. + if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { + symbolBlock = block; + } else { + symbolBlock = function; + } + + // Create and add to appropriate block. + symbol = new Symbol(name, flags, node, symbolBlock); + symbolBlock.putSymbol(name, symbol); + + if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { + symbolBlock.getFrame().addSymbol(symbol); + symbol.setNeedsSlot(true); + } + } else if (symbol.less(flags)) { + symbol.setFlags(flags); + } + + if (node != null) { + node.setSymbol(symbol); + } + + return symbol; + } + @Override - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { start(functionNode, false); if (functionNode.isLazy()) { - LOG.info("LAZY: " + functionNode.getName()); + LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT"); + newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode); + functionNode.setReturnType(Type.OBJECT); end(functionNode); return null; } + lexicalContext.push(functionNode); + clearLocalDefs(); clearLocalUses(); @@ -244,24 +324,36 @@ final class Attr extends NodeOperatorVisitor { initScope(functionNode); initReturn(functionNode); - // Add all nested functions as symbols in this function - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { + // Add all nested declared functions as symbols in this function + for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) { final IdentNode ident = nestedFunction.getIdent(); - if (ident != null && nestedFunction.isStatement()) { - final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction); + if (ident != null) { + assert nestedFunction.isDeclared(); + final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction); newType(functionSymbol, Type.typeFor(ScriptFunction.class)); } } - if (functionNode.isScript()) { + if (functionNode.isProgram()) { initFromPropertyMap(functionNode); } // Add function name as local symbol - if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) { - final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode); - newType(selfSymbol, Type.OBJECT); - selfSymbol.setNode(functionNode); + if (!functionNode.isDeclared() && !functionNode.isProgram()) { + if(functionNode.getSymbol() != null) { + // a temporary left over from an earlier pass when the function was lazy + assert functionNode.getSymbol().isTemp(); + // remove it + functionNode.setSymbol(null); + } + final Symbol selfSymbol; + if(functionNode.isAnonymous()) { + selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode); + } else { + selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode); + newType(selfSymbol, Type.OBJECT); + selfSymbol.setNode(functionNode); + } } /* @@ -282,32 +374,26 @@ final class Attr extends NodeOperatorVisitor { */ final List declaredSymbols = new ArrayList<>(); - for (final VarNode decl : functionNode.getDeclarations()) { - final IdentNode ident = decl.getName(); - // any declared symbols that aren't visited need to be typed as well, hence the list - declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident))); - } - - // Every nested function needs a definition in the outer function with its name. Add these. - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { - final VarNode varNode = nestedFunction.getFunctionVarNode(); - if (varNode != null) { - varNode.accept(this); - assert varNode.isFunctionVarNode() : varNode + " should be function var node"; + // This visitor will assign symbol to all declared variables, except function declarations (which are taken care + // in a separate step above) and "var" declarations in for loop initializers. + functionNode.accept(new NodeOperatorVisitor() { + @Override + public Node enterFunctionNode(FunctionNode nestedFn) { + // Don't descend into nested functions + return nestedFn == functionNode ? nestedFn : null; } - } - - for (final Node statement : functionNode.getStatements()) { - if (statement instanceof VarNode && ((VarNode)statement).isFunctionVarNode()) { - continue; //var nodes have already been processed, skip or they will generate additional defs/uses and false "can be undefined" + @Override + public Node enterVarNode(VarNode varNode) { + if(varNode.isStatement() && !varNode.isFunctionDeclaration()) { + final IdentNode ident = varNode.getName(); + // any declared symbols that aren't visited need to be typed as well, hence the list + declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident))); + } + return null; } - statement.accept(this); - } + }); - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { - LOG.info("Going into nested function " + functionNode.getName() + " -> " + nestedFunction.getName()); - nestedFunction.accept(this); - } + visitFunctionStatements(functionNode); //unknown parameters are promoted to object type. finalizeParameters(functionNode); @@ -343,10 +429,19 @@ final class Attr extends NodeOperatorVisitor { functionNode.setState(CompilationState.ATTR); end(functionNode, false); + lexicalContext.pop(functionNode); return null; } + private void visitFunctionStatements(final FunctionNode functionNode) { + final List newStatements = new ArrayList<>(functionNode.getStatements()); + for(ListIterator stmts = newStatements.listIterator(); stmts.hasNext();) { + stmts.set(stmts.next().accept(this)); + } + functionNode.setStatements(newStatements); + } + @Override public Node leaveCONVERT(final UnaryNode unaryNode) { assert false : "There should be no convert operators in IR during Attribution"; @@ -355,7 +450,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final IdentNode identNode) { + public Node enterIdentNode(final IdentNode identNode) { final String name = identNode.getName(); start(identNode); @@ -372,7 +467,7 @@ final class Attr extends NodeOperatorVisitor { final Block block = getCurrentBlock(); final Symbol oldSymbol = identNode.getSymbol(); - Symbol symbol = block.findSymbol(name); + Symbol symbol = findSymbol(block, name); //If an existing symbol with the name is found, use that otherwise, declare a new one if (symbol != null) { @@ -396,22 +491,13 @@ final class Attr extends NodeOperatorVisitor { } identNode.setSymbol(symbol); - if (!getCurrentFunctionNode().isLocal(symbol)) { - // non-local: we need to put symbol in scope (if it isn't already) - if (!symbol.isScope()) { - final List lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction()); - for (final Block lookupBlock : lookupBlocks) { - final Symbol refSymbol = lookupBlock.findSymbol(name); - if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f() - LOG.finest("Found a ref symbol that must be scope " + refSymbol); - refSymbol.setIsScope(); - } - } - } + // non-local: we need to put symbol in scope (if it isn't already) + if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) { + symbol.setIsScope(); } } else { LOG.info("No symbol exists. Declare undefined: " + symbol); - symbol = block.useSymbol(name, identNode); + symbol = useSymbol(block, name, identNode); // we have never seen this before, it can be undefined newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway? symbol.setCanBeUndefined(); @@ -420,9 +506,10 @@ final class Attr extends NodeOperatorVisitor { assert symbol != null; if(symbol.isGlobal()) { - getCurrentFunctionNode().setUsesGlobalSymbol(); + setUsesGlobalSymbol(); } else if(symbol.isScope()) { - getCurrentFunctionNode().setUsesScopeSymbol(symbol); + final Iterator blocks = lexicalContext.getBlocks(); + blocks.next().setUsesScopeSymbol(symbol, blocks); } if (symbol != oldSymbol && !identNode.isInitializedHere()) { @@ -435,15 +522,68 @@ final class Attr extends NodeOperatorVisitor { return null; } + /** + * Marks the current function as one using any global symbol. The function and all its parent functions will all be + * marked as needing parent scope. + * @see #needsParentScope() + */ + private void setUsesGlobalSymbol() { + for(final Iterator fns = lexicalContext.getFunctions(); fns.hasNext();) { + fns.next().setUsesAncestorScope(); + } + } + + /** + * Declare the use of a symbol in a block. + * + * @param block block in which the symbol is used + * @param name Name of symbol. + * @param node Using node + * + * @return Symbol for given name. + */ + private Symbol useSymbol(final Block block, final String name, final Node node) { + Symbol symbol = findSymbol(block, name); + + if (symbol == null) { + // If not found, declare as a free var. + symbol = defineSymbol(block, name, IS_GLOBAL, node); + } else { + node.setSymbol(symbol); + } + + return symbol; + } + + + /** + * Search for symbol in the lexical context starting from the given block. + * @param name Symbol name. + * @return Found symbol or null if not found. + */ + private Symbol findSymbol(final Block block, final String name) { + // Search up block chain to locate symbol. + + for(final Iterator blocks = lexicalContext.getBlocks(block); blocks.hasNext();) { + // Find name. + final Symbol symbol = blocks.next().getExistingSymbol(name); + // If found then we are good. + if(symbol != null) { + return symbol; + } + } + return null; + } + @Override - public Node leave(final IndexNode indexNode) { + public Node leaveIndexNode(final IndexNode indexNode) { newTemporary(Type.OBJECT, indexNode); //TORO return indexNode; } @SuppressWarnings("rawtypes") @Override - public Node enter(final LiteralNode literalNode) { + public Node enterLiteralNode(final LiteralNode literalNode) { try { start(literalNode); assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens @@ -472,14 +612,14 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final ObjectNode objectNode) { + public Node leaveObjectNode(final ObjectNode objectNode) { newTemporary(Type.OBJECT, objectNode); end(objectNode); return objectNode; } @Override - public Node enter(final PropertyNode propertyNode) { + public Node enterPropertyNode(final PropertyNode propertyNode) { // assign a pseudo symbol to property name, see NASHORN-710 propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); end(propertyNode); @@ -487,31 +627,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final ReferenceNode referenceNode) { - final FunctionNode functionNode = referenceNode.getReference(); - if (functionNode != null) { - functionNode.addReferencingParentBlock(getCurrentBlock()); - } - return referenceNode; - } - - @Override - public Node leave(final ReferenceNode referenceNode) { - newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though - - final FunctionNode functionNode = referenceNode.getReference(); - //assert !functionNode.getType().isUnknown() || functionNode.isLazy() : functionNode.getType(); - if (functionNode.isLazy()) { - LOG.info("Lazy function node call reference: " + functionNode.getName() + " => Promoting to OBJECT"); - functionNode.setReturnType(Type.OBJECT); - } - end(referenceNode); - - return referenceNode; - } - - @Override - public Node leave(final ReturnNode returnNode) { + public Node leaveReturnNode(final ReturnNode returnNode) { final Node expr = returnNode.getExpression(); if (expr != null) { @@ -530,7 +646,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final SwitchNode switchNode) { + public Node leaveSwitchNode(final SwitchNode switchNode) { Type type = Type.UNKNOWN; for (final CaseNode caseNode : switchNode.getCases()) { @@ -567,7 +683,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final TryNode tryNode) { + public Node leaveTryNode(final TryNode tryNode) { tryNode.setException(exceptionSymbol()); if (tryNode.getFinallyBody() != null) { @@ -580,13 +696,13 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final VarNode varNode) { + public Node enterVarNode(final VarNode varNode) { start(varNode); final IdentNode ident = varNode.getName(); final String name = ident.getName(); - final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident); + final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident); assert symbol != null; LOG.info("VarNode " + varNode + " set symbol " + symbol); @@ -598,23 +714,15 @@ final class Attr extends NodeOperatorVisitor { symbol.setCanBeUndefined(); } - if (varNode.getInit() != null) { - varNode.getInit().accept(this); - } - return varNode; } @Override - public Node leave(final VarNode varNode) { + public Node leaveVarNode(final VarNode varNode) { final Node init = varNode.getInit(); final IdentNode ident = varNode.getName(); final String name = ident.getName(); - if (init != null) { - addLocalDef(name); - } - if (init == null) { // var x; with no init will be treated like a use of x by // visit(IdentNode) unless we remove the name @@ -623,8 +731,10 @@ final class Attr extends NodeOperatorVisitor { return varNode; } + addLocalDef(name); + final Symbol symbol = varNode.getSymbol(); - final boolean isScript = symbol.getBlock().getFunction().isScript(); //see NASHORN-56 + final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56 if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) { // Forbid integers as local vars for now as we have no way to treat them as undefined newType(symbol, init.getType()); @@ -718,11 +828,9 @@ final class Attr extends NodeOperatorVisitor { runtimeNode = new RuntimeNode(unaryNode, request, args); assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this - runtimeNode.accept(this); - return runtimeNode; + return leaveRuntimeNode(runtimeNode); } - @Override public Node leaveNEW(final UnaryNode unaryNode) { newTemporary(Type.OBJECT, unaryNode); @@ -755,7 +863,7 @@ final class Attr extends NodeOperatorVisitor { runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); assert runtimeNode.getSymbol() == unaryNode.getSymbol(); - runtimeNode.accept(this); + runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode); end(unaryNode); @@ -763,7 +871,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final RuntimeNode runtimeNode) { + public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode); return runtimeNode; } @@ -823,12 +931,12 @@ final class Attr extends NodeOperatorVisitor { final IdentNode ident = (IdentNode)lhs; final String name = ident.getName(); - Symbol symbol = getCurrentBlock().findSymbol(name); + Symbol symbol = findSymbol(getCurrentBlock(), name); if (symbol == null) { - symbol = block.defineSymbol(name, IS_GLOBAL, ident); + symbol = defineSymbol(block, name, IS_GLOBAL, ident); binaryNode.setSymbol(symbol); - } else if (!getCurrentFunctionNode().isLocal(symbol)) { + } else if (!isLocal(getCurrentFunctionNode(), symbol)) { symbol.setIsScope(); } @@ -838,6 +946,12 @@ final class Attr extends NodeOperatorVisitor { return binaryNode; } + private boolean isLocal(FunctionNode function, Symbol symbol) { + final Block block = symbol.getBlock(); + // some temp symbols have no block, so can be assumed local + return block == null || lexicalContext.getFunction(block) == function; + } + @Override public Node enterASSIGN(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); @@ -995,7 +1109,7 @@ final class Attr extends NodeOperatorVisitor { return leaveBinaryArithmetic(binaryNode); } - private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { + private Node leaveCmp(final BinaryNode binaryNode) { final Node lhs = binaryNode.lhs(); final Node rhs = binaryNode.rhs(); @@ -1033,37 +1147,38 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveEQ(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ); + return leaveCmp(binaryNode); } @Override public Node leaveEQ_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ_STRICT); + return leaveCmp(binaryNode); } @Override public Node leaveGE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GE); + return leaveCmp(binaryNode); } @Override public Node leaveGT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GT); + return leaveCmp(binaryNode); } @Override public Node leaveIN(final BinaryNode binaryNode) { - try { - return new RuntimeNode(binaryNode, Request.IN).accept(this); - } finally { - end(binaryNode); - } + return leaveBinaryRuntimeOperator(binaryNode, Request.IN); } @Override public Node leaveINSTANCEOF(final BinaryNode binaryNode) { + return leaveBinaryRuntimeOperator(binaryNode, Request.INSTANCEOF); + } + + private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) { try { - return new RuntimeNode(binaryNode, Request.INSTANCEOF).accept(this); + // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands + return leaveRuntimeNode(new RuntimeNode(binaryNode, request)); } finally { end(binaryNode); } @@ -1071,12 +1186,12 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveLE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LE); + return leaveCmp(binaryNode); } @Override public Node leaveLT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LT); + return leaveCmp(binaryNode); } @Override @@ -1091,12 +1206,12 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveNE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE); + return leaveCmp(binaryNode); } @Override public Node leaveNE_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE_STRICT); + return leaveCmp(binaryNode); } @Override @@ -1127,9 +1242,9 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final ForNode forNode) { + public Node leaveForNode(final ForNode forNode) { if (forNode.isForIn()) { - forNode.setIterator(newInternal(getCurrentFunctionNode(), getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73 + forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73 /* * Iterators return objects, so we need to widen the scope of the * init variable if it, for example, has been assigned double type @@ -1144,7 +1259,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node leave(final TernaryNode ternaryNode) { + public Node leaveTernaryNode(final TernaryNode ternaryNode) { final Node lhs = ternaryNode.rhs(); final Node rhs = ternaryNode.third(); @@ -1159,24 +1274,24 @@ final class Attr extends NodeOperatorVisitor { return ternaryNode; } - private static void initThis(final FunctionNode functionNode) { - final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null); + private void initThis(final FunctionNode functionNode) { + final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null); newType(thisSymbol, Type.OBJECT); thisSymbol.setNeedsSlot(true); functionNode.getThisNode().setSymbol(thisSymbol); LOG.info("Initialized scope symbol: " + thisSymbol); } - private static void initScope(final FunctionNode functionNode) { - final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null); + private void initScope(final FunctionNode functionNode) { + final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null); newType(scopeSymbol, Type.typeFor(ScriptObject.class)); scopeSymbol.setNeedsSlot(true); functionNode.getScopeNode().setSymbol(scopeSymbol); LOG.info("Initialized scope symbol: " + scopeSymbol); } - private static void initReturn(final FunctionNode functionNode) { - final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); + private void initReturn(final FunctionNode functionNode) { + final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); newType(returnSymbol, Type.OBJECT); returnSymbol.setNeedsSlot(true); functionNode.getResultNode().setSymbol(returnSymbol); @@ -1186,7 +1301,7 @@ final class Attr extends NodeOperatorVisitor { private void initVarArg(final FunctionNode functionNode) { if (functionNode.isVarArg()) { - final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); + final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY); varArgsSymbol.setNeedsSlot(true); functionNode.getVarArgsNode().setSymbol(varArgsSymbol); @@ -1194,7 +1309,7 @@ final class Attr extends NodeOperatorVisitor { if (functionNode.needsArguments()) { final String argumentsName = functionNode.getArgumentsNode().getName(); - final Symbol argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_VAR | IS_INTERNAL, null); + final Symbol argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null); newType(argumentsSymbol, Type.typeFor(ScriptObject.class)); argumentsSymbol.setNeedsSlot(true); functionNode.getArgumentsNode().setSymbol(argumentsSymbol); @@ -1204,9 +1319,9 @@ final class Attr extends NodeOperatorVisitor { } } - private static void initCallee(final FunctionNode functionNode) { + private void initCallee(final FunctionNode functionNode) { assert functionNode.getCalleeNode() != null : functionNode + " has no callee"; - final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); + final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); newType(calleeSymbol, Type.typeFor(ScriptFunction.class)); calleeSymbol.setNeedsSlot(true); functionNode.getCalleeNode().setSymbol(calleeSymbol); @@ -1226,7 +1341,7 @@ final class Attr extends NodeOperatorVisitor { for (final IdentNode param : functionNode.getParameters()) { addLocalDef(param.getName()); - final Symbol paramSymbol = functionNode.defineSymbol(param.getName(), IS_PARAM, param); + final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param); if (paramSymbol != null) { final Type callSiteParamType = functionNode.getSpecializedType(param); if (callSiteParamType != null) { @@ -1279,15 +1394,15 @@ final class Attr extends NodeOperatorVisitor { * Move any properties from a global map into the scope of this method * @param functionNode the function node for which to init scope vars */ - private static void initFromPropertyMap(final FunctionNode functionNode) { + private void initFromPropertyMap(final FunctionNode functionNode) { // For a script, add scope symbols as defined in the property map - assert functionNode.isScript(); + assert functionNode.isProgram(); final PropertyMap map = Context.getGlobalMap(); for (final Property property : map.getProperties()) { final String key = property.getKey(); - final Symbol symbol = functionNode.defineSymbol(key, IS_GLOBAL, null); + final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null); newType(symbol, Type.OBJECT); LOG.info("Added global symbol from property map " + symbol); } @@ -1354,7 +1469,7 @@ final class Attr extends NodeOperatorVisitor { private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) { assignmentDest.accept(new NodeVisitor() { @Override - public Node leave(final IndexNode indexNode) { + public Node leaveIndexNode(final IndexNode indexNode) { final Node index = indexNode.getIndex(); index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant()); return indexNode; @@ -1399,7 +1514,7 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final FunctionNode node) { + public Node enterFunctionNode(final FunctionNode node) { return node.isLazy() ? null : node; } @@ -1419,7 +1534,7 @@ final class Attr extends NodeOperatorVisitor { */ @SuppressWarnings("fallthrough") @Override - public Node leave(final BinaryNode binaryNode) { + public Node leaveBinaryNode(final BinaryNode binaryNode) { final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); switch (binaryNode.tokenType()) { default: @@ -1477,22 +1592,6 @@ final class Attr extends NodeOperatorVisitor { return binaryNode; } - private static List findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) { - if (currentFunction.findParentFunction() == topFunction) { - final List blocks = new LinkedList<>(); - - blocks.add(currentFunction.getParent()); - blocks.addAll(currentFunction.getReferencingParentBlocks()); - return blocks; - } - /* - * assumption: all parent blocks of an inner function will always be in the same outer function; - * therefore we can simply skip through intermediate functions. - * @see FunctionNode#addReferencingParentBlock(Block) - */ - return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction); - } - private static boolean isFunctionExpressionSelfReference(final Symbol symbol) { if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) { return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName()); @@ -1509,16 +1608,12 @@ final class Attr extends NodeOperatorVisitor { return newTemporary(getCurrentFunctionNode(), type, node); } - private Symbol newInternal(final FunctionNode functionNode, final String name, final Type type) { - final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); + private Symbol newInternal(final String name, final Type type) { + final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null); iter.setType(type); // NASHORN-73 return iter; } - private Symbol newInternal(final String name, final Type type) { - return newInternal(getCurrentFunctionNode(), name, type); - } - private static void newType(final Symbol symbol, final Type type) { final Type oldType = symbol.getSymbolType(); symbol.setType(type); @@ -1577,13 +1672,13 @@ final class Attr extends NodeOperatorVisitor { } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { toObject(block); return block; } @Override - public Node enter(final FunctionNode node) { + public Node enterFunctionNode(final FunctionNode node) { toObject(node); if (node.isLazy()) { return null; diff --git a/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/src/jdk/nashorn/internal/codegen/ClassEmitter.java index 35e74824..7ca7f994 100644 --- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java @@ -194,6 +194,14 @@ public class ClassEmitter implements Emitter { defineCommonStatics(strictMode); } + /** + * Returns the name of the compile unit class name. + * @return the name of the compile unit class name. + */ + String getUnitClassName() { + return unitClassName; + } + /** * Convert a binary name to a package/class name. * @@ -244,7 +252,7 @@ public class ClassEmitter implements Emitter { // $getMap - get the ith entry from the constants table and cast to PropertyMap. final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class); getMapMethod.begin(); - getMapMethod.loadConstants(unitClassName) + getMapMethod.loadConstants() .load(Type.INT, 0) .arrayload() .checkcast(PropertyMap.class) @@ -254,7 +262,7 @@ public class ClassEmitter implements Emitter { // $setMap - overwrite an existing map. final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class); setMapMethod.begin(); - setMapMethod.loadConstants(unitClassName) + setMapMethod.loadConstants() .load(Type.INT, 0) .load(Type.OBJECT, 1) .arraystore(); diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index ce6a1fcd..1d09e9c9 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -76,18 +76,18 @@ import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ExecuteNode; 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; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; @@ -107,6 +107,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; @@ -156,12 +157,20 @@ final class CodeGenerator extends NodeOperatorVisitor { /** How many regexp fields have been emitted */ private int regexFieldCount; + /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling + * a just-defined anonymous function expression. */ + private boolean functionNodeIsCallee; + /** Map of shared scope call sites */ private final Map scopeCalls = new HashMap<>(); + private final LexicalContext lexicalContext = new LexicalContext(); + /** When should we stop caching regexp expressions in fields to limit bytecode size? */ private static final int MAX_REGEX_FIELDS = 2 * 1024; + private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug"); + /** * Constructor. * @@ -210,7 +219,7 @@ final class CodeGenerator extends NodeOperatorVisitor { final int flags = CALLSITE_SCOPE | getCallSiteFlags(); method.loadScope(); - if (symbol.isFastScope(getCurrentFunctionNode())) { + 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) { return loadSharedScopeVar(identNode.getType(), symbol, flags); @@ -221,8 +230,28 @@ final class CodeGenerator extends NodeOperatorVisitor { } } + /** + * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load + * + * @param function function to check for fast scope + * @return true if fast scope + */ + private boolean isFastScope(final Symbol symbol) { + if (!symbol.isScope() || !symbol.getBlock().needsScope()) { + return false; + } + // Allow fast scope access if no function contains with or eval + for(final Iterator it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) { + final FunctionNode func = it.next(); + if (func.hasWith() || func.hasEval()) { + return false; + } + } + return true; + } + private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) { - method.load(symbol.isFastScope(getCurrentFunctionNode()) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1); + method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1); final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE); scopeCall.generateInvoke(method); return method; @@ -240,30 +269,18 @@ final class CodeGenerator extends NodeOperatorVisitor { return method; } - private static int getScopeProtoDepth(final Block currentBlock, final Symbol symbol) { - if (currentBlock == symbol.getBlock()) { - return 0; - } - - final int delta = currentBlock.needsScope() ? 1 : 0; - final Block parentBlock = currentBlock.getParent(); - - if (parentBlock != null) { - final int result = getScopeProtoDepth(parentBlock, symbol); - if (result != -1) { - return delta + result; + private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) { + int depth = 0; + final Block definingBlock = symbol.getBlock(); + for(final Iterator blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) { + final Block currentBlock = blocks.next(); + if (currentBlock == definingBlock) { + return depth; } - } - - if (currentBlock instanceof FunctionNode) { - for (final Block lookupBlock : ((FunctionNode)currentBlock).getReferencingParentBlocks()) { - final int result = getScopeProtoDepth(lookupBlock, symbol); - if (result != -1) { - return delta + result; - } + if (currentBlock.needsScope()) { + ++depth; } } - return -1; } @@ -313,13 +330,13 @@ final class CodeGenerator extends NodeOperatorVisitor { node.accept(new NodeVisitor(getCurrentCompileUnit(), method) { @Override - public Node enter(final IdentNode identNode) { + public Node enterIdentNode(final IdentNode identNode) { loadIdent(identNode); return null; } @Override - public Node enter(final AccessNode accessNode) { + public Node enterAccessNode(final AccessNode accessNode) { if (!baseAlreadyOnStack) { load(accessNode.getBase()).convert(Type.OBJECT); } @@ -329,7 +346,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final IndexNode indexNode) { + public Node enterIndexNode(final IndexNode indexNode) { if (!baseAlreadyOnStack) { load(indexNode.getBase()).convert(Type.OBJECT); load(indexNode.getIndex()); @@ -338,6 +355,14 @@ final class CodeGenerator extends NodeOperatorVisitor { return null; } + @Override + public Node enterFunctionNode(FunctionNode functionNode) { + // function nodes will always leave a constructed function object on stack, no need to load the symbol + // separately as in enterDefault() + functionNode.accept(codegen); + return null; + } + @Override public Node enterDefault(final Node otherNode) { otherNode.accept(codegen); // generate code for whatever we are looking at. @@ -350,7 +375,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final AccessNode accessNode) { + public Node enterAccessNode(final AccessNode accessNode) { if (accessNode.testResolved()) { return null; } @@ -422,10 +447,11 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { if (block.testResolved()) { return null; } + lexicalContext.push(block); method.label(block.getEntryLabel()); initLocals(block); @@ -434,14 +460,14 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node leave(final Block block) { + public Node leaveBlock(final Block block) { method.label(block.getBreakLabel()); symbolInfo(block); if (block.needsScope()) { popBlockScope(block); } - + lexicalContext.pop(block); return block; } @@ -467,7 +493,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final BreakNode breakNode) { + public Node enterBreakNode(final BreakNode breakNode) { if (breakNode.testResolved()) { return null; } @@ -515,14 +541,13 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final CallNode callNode) { + public Node enterCallNode(final CallNode callNode) { if (callNode.testResolved()) { return null; } final List args = callNode.getArgs(); final Node function = callNode.getFunction(); - final FunctionNode currentFunction = getCurrentFunctionNode(); final Block currentBlock = getCurrentBlock(); function.accept(new NodeVisitor(getCurrentCompileUnit(), method) { @@ -531,7 +556,7 @@ final class CodeGenerator extends NodeOperatorVisitor { final Symbol symbol = identNode.getSymbol(); int scopeCallFlags = flags; method.loadScope(); - if (symbol.isFastScope(currentFunction)) { + if (isFastScope(symbol)) { method.load(getScopeProtoDepth(currentBlock, symbol)); scopeCallFlags |= CALLSITE_FAST_SCOPE; } else { @@ -593,7 +618,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final IdentNode node) { + public Node enterIdentNode(final IdentNode node) { final Symbol symbol = node.getSymbol(); if (symbol.isScope()) { @@ -606,7 +631,7 @@ final class CodeGenerator extends NodeOperatorVisitor { if (callNode.isEval()) { evalCall(node, flags); } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD - || (!symbol.isFastScope(currentFunction) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD) + || (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD) || callNode.inWithBlock()) { scopeCall(node, flags); } else { @@ -621,7 +646,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final AccessNode node) { + public Node enterAccessNode(final AccessNode node) { load(node.getBase()); method.convert(Type.OBJECT); method.dup(); @@ -634,8 +659,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final ReferenceNode node) { - final FunctionNode callee = node.getReference(); + public Node enterFunctionNode(final FunctionNode callee) { final boolean isVarArg = callee.isVarArg(); final int argCount = isVarArg ? -1 : callee.getParameters().size(); @@ -653,12 +677,13 @@ final class CodeGenerator extends NodeOperatorVisitor { loadArgs(args, signature, isVarArg, argCount); method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature); assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType(); - + functionNodeIsCallee = true; + callee.accept(CodeGenerator.this); return null; } @Override - public Node enter(final IndexNode node) { + public Node enterIndexNode(final IndexNode node) { load(node.getBase()); method.convert(Type.OBJECT); method.dup(); @@ -694,7 +719,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final ContinueNode continueNode) { + public Node enterContinueNode(final ContinueNode continueNode) { if (continueNode.testResolved()) { return null; } @@ -709,17 +734,17 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final DoWhileNode doWhileNode) { - return enter((WhileNode)doWhileNode); + public Node enterDoWhileNode(final DoWhileNode doWhileNode) { + return enterWhileNode(doWhileNode); } @Override - public Node enter(final EmptyNode emptyNode) { + public Node enterEmptyNode(final EmptyNode emptyNode) { return null; } @Override - public Node enter(final ExecuteNode executeNode) { + public Node enterExecuteNode(final ExecuteNode executeNode) { if (executeNode.testResolved()) { return null; } @@ -731,7 +756,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final ForNode forNode) { + public Node enterForNode(final ForNode forNode) { if (forNode.testResolved()) { return null; } @@ -813,7 +838,7 @@ final class CodeGenerator extends NodeOperatorVisitor { * @param block block with local vars. */ private void initLocals(final Block block) { - final FunctionNode function = block.getFunction(); + final FunctionNode function = lexicalContext.getFunction(block); final boolean isFunctionNode = block == function; /* @@ -915,7 +940,7 @@ final class CodeGenerator extends NodeOperatorVisitor { foc.makeObject(method); // runScript(): merge scope into global - if (isFunctionNode && function.isScript()) { + if (isFunctionNode && function.isProgram()) { method.invoke(ScriptRuntime.MERGE_SCOPE); } @@ -958,19 +983,29 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final FunctionNode functionNode) { - if (functionNode.isLazy()) { + public Node enterFunctionNode(final FunctionNode functionNode) { + final boolean isCallee = functionNodeIsCallee; + functionNodeIsCallee = false; + + if (functionNode.testResolved()) { return null; } - if (functionNode.testResolved()) { + if(!(isCallee || functionNode == compiler.getFunctionNode())) { + newFunctionObject(functionNode); + } + + if (functionNode.isLazy()) { return null; } + LOG.info("=== BEGIN " + functionNode.getName()); + lexicalContext.push(functionNode); + setCurrentCompileUnit(functionNode.getCompileUnit()); assert getCurrentCompileUnit() != null; - method = getCurrentCompileUnit().getClassEmitter().method(functionNode); + setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode)); functionNode.setMethodEmitter(method); // Mark end for variable tables. method.begin(); @@ -983,7 +1018,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node leave(final FunctionNode functionNode) { + public Node leaveFunctionNode(final FunctionNode functionNode) { // Mark end for variable tables. method.label(functionNode.getBreakLabel()); @@ -1001,16 +1036,18 @@ final class CodeGenerator extends NodeOperatorVisitor { throw e; } + lexicalContext.pop(functionNode); + LOG.info("=== END " + functionNode.getName()); return functionNode; } @Override - public Node enter(final IdentNode identNode) { + public Node enterIdentNode(final IdentNode identNode) { return null; } @Override - public Node enter(final IfNode ifNode) { + public Node enterIfNode(final IfNode ifNode) { if (ifNode.testResolved()) { return null; } @@ -1049,7 +1086,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final IndexNode indexNode) { + public Node enterIndexNode(final IndexNode indexNode) { if (indexNode.testResolved()) { return null; } @@ -1060,7 +1097,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final LineNumberNode lineNumberNode) { + public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { if (lineNumberNode.testResolved()) { return null; } @@ -1068,7 +1105,6 @@ final class CodeGenerator extends NodeOperatorVisitor { final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")"); method.label(label); method.lineNumber(lineNumberNode.getLineNumber(), label); - return null; } @@ -1106,7 +1142,7 @@ final class CodeGenerator extends NodeOperatorVisitor { final String name = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag()); final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type); - method = getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature); + setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature)); method.setFunctionNode(getCurrentFunctionNode()); method.begin(); @@ -1212,7 +1248,7 @@ final class CodeGenerator extends NodeOperatorVisitor { method.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class)); classEmitter.needGetConstantMethod(cls); } else { - method.loadConstants(unitClassName).load(index).arrayload(); + method.loadConstants().load(index).arrayload(); if (cls != Object.class) { method.checkcast(cls); } @@ -1292,14 +1328,14 @@ final class CodeGenerator extends NodeOperatorVisitor { @SuppressWarnings("rawtypes") @Override - public Node enter(final LiteralNode literalNode) { + public Node enterLiteralNode(final LiteralNode literalNode) { assert literalNode.getSymbol() != null : literalNode + " has no symbol"; load(literalNode).store(literalNode.getSymbol()); return null; } @Override - public Node enter(final ObjectNode objectNode) { + public Node enterObjectNode(final ObjectNode objectNode) { if (objectNode.testResolved()) { return null; } @@ -1372,10 +1408,10 @@ final class CodeGenerator extends NodeOperatorVisitor { } for (final Node element : elements) { - final PropertyNode propertyNode = (PropertyNode)element; - final Object key = propertyNode.getKey(); - final ReferenceNode getter = (ReferenceNode)propertyNode.getGetter(); - final ReferenceNode setter = (ReferenceNode)propertyNode.getSetter(); + final PropertyNode propertyNode = (PropertyNode)element; + final Object key = propertyNode.getKey(); + final FunctionNode getter = (FunctionNode)propertyNode.getGetter(); + final FunctionNode setter = (FunctionNode)propertyNode.getSetter(); if (getter == null && setter == null) { continue; @@ -1404,18 +1440,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final ReferenceNode referenceNode) { - if (referenceNode.testResolved()) { - return null; - } - - newFunctionObject(referenceNode.getReference()); - - return null; - } - - @Override - public Node enter(final ReturnNode returnNode) { + public Node enterReturnNode(final ReturnNode returnNode) { if (returnNode.testResolved()) { return null; } @@ -1556,7 +1581,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final RuntimeNode runtimeNode) { + public Node enterRuntimeNode(final RuntimeNode runtimeNode) { if (runtimeNode.testResolved()) { return null; } @@ -1637,7 +1662,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final SplitNode splitNode) { + public Node enterSplitNode(final SplitNode splitNode) { if (splitNode.testResolved()) { return null; } @@ -1706,7 +1731,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node leave(final SplitNode splitNode) { + public Node leaveSplitNode(final SplitNode splitNode) { try { // Wrap up this method. method.loadResult(); @@ -1763,7 +1788,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final SwitchNode switchNode) { + public Node enterSwitchNode(final SwitchNode switchNode) { if (switchNode.testResolved()) { return null; } @@ -1895,7 +1920,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final ThrowNode throwNode) { + public Node enterThrowNode(final ThrowNode throwNode) { if (throwNode.testResolved()) { return null; } @@ -1922,7 +1947,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final TryNode tryNode) { + public Node enterTryNode(final TryNode tryNode) { if (tryNode.testResolved()) { return null; } @@ -1955,7 +1980,7 @@ final class CodeGenerator extends NodeOperatorVisitor { setCurrentBlock(catchBlock); try { - enter(catchBlock); + enterBlock(catchBlock); final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0); final IdentNode exception = catchNode.getException(); @@ -1966,6 +1991,7 @@ final class CodeGenerator extends NodeOperatorVisitor { // Generate catch body (inlined finally) and rethrow exception catchBody.accept(this); method.load(symbol).athrow(); + lexicalContext.pop(catchBlock); continue; } @@ -2012,7 +2038,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } } - leave(catchBlock); + leaveBlock(catchBlock); } finally { setCurrentBlock(saveBlock); } @@ -2027,7 +2053,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final VarNode varNode) { + public Node enterVarNode(final VarNode varNode) { final Node init = varNode.getInit(); if (varNode.testResolved() || init == null) { @@ -2049,7 +2075,7 @@ final class CodeGenerator extends NodeOperatorVisitor { int flags = CALLSITE_SCOPE | getCallSiteFlags(); final IdentNode identNode = varNode.getName(); final Type type = identNode.getType(); - if (varSymbol.isFastScope(getCurrentFunctionNode())) { + if (isFastScope(varSymbol)) { storeFastScopeVar(type, varSymbol, flags); } else { method.dynamicSet(type, identNode.getName(), flags); @@ -2065,7 +2091,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final WhileNode whileNode) { + public Node enterWhileNode(final WhileNode whileNode) { if (whileNode.testResolved()) { return null; } @@ -2098,7 +2124,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final WithNode withNode) { + public Node enterWithNode(final WithNode withNode) { if (withNode.testResolved()) { return null; } @@ -2864,7 +2890,7 @@ final class CodeGenerator extends NodeOperatorVisitor { * Ternary visits. */ @Override - public Node enter(final TernaryNode ternaryNode) { + public Node enterTernaryNode(final TernaryNode ternaryNode) { if (ternaryNode.testResolved()) { return null; } @@ -3060,7 +3086,7 @@ final class CodeGenerator extends NodeOperatorVisitor { target.accept(new NodeVisitor(getCurrentCompileUnit(), method) { @Override - public Node enter(final IdentNode node) { + public Node enterIdentNode(final IdentNode node) { if (targetSymbol.isScope()) { method.load(scopeSymbol); depth++; @@ -3083,13 +3109,13 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final AccessNode node) { + public Node enterAccessNode(final AccessNode node) { enterBaseNode(); return null; } @Override - public Node enter(final IndexNode node) { + public Node enterIndexNode(final IndexNode node) { enterBaseNode(); final Node index = node.getIndex(); @@ -3155,8 +3181,6 @@ final class CodeGenerator extends NodeOperatorVisitor { } private void epilogue() { - final FunctionNode currentFunction = getCurrentFunctionNode(); - /** * Take the original target args from the stack and use them * together with the value to be stored to emit the store code @@ -3174,7 +3198,7 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final UnaryNode node) { + public Node enterUnaryNode(final UnaryNode node) { if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) { method.convert(node.rhs().getType()); } @@ -3182,11 +3206,11 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final IdentNode node) { + public Node enterIdentNode(final IdentNode node) { final Symbol symbol = node.getSymbol(); assert symbol != null; if (symbol.isScope()) { - if (symbol.isFastScope(currentFunction)) { + if (isFastScope(symbol)) { storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags()); } else { method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags()); @@ -3199,13 +3223,13 @@ final class CodeGenerator extends NodeOperatorVisitor { } @Override - public Node enter(final AccessNode node) { + public Node enterAccessNode(final AccessNode node) { method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags()); return null; } @Override - public Node enter(final IndexNode node) { + public Node enterIndexNode(final IndexNode node) { method.dynamicSetIndex(getCallSiteFlags()); return null; } diff --git a/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/src/jdk/nashorn/internal/codegen/CompilationPhase.java index 0125efff..8b905f87 100644 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java @@ -14,16 +14,16 @@ import java.io.IOException; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; - import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.ReferenceNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; +import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.ECMAErrors; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.Timing; @@ -65,17 +65,17 @@ enum CompilationPhase { outermostFunctionNode.accept(new NodeVisitor() { // self references are done with invokestatic and thus cannot have trampolines - never lazy @Override - public Node enter(final CallNode node) { + public Node enterCallNode(final CallNode node) { final Node callee = node.getFunction(); - if (callee instanceof ReferenceNode) { - neverLazy.add(((ReferenceNode)callee).getReference()); + if (callee instanceof FunctionNode) { + neverLazy.add(((FunctionNode)callee)); return null; } return node; } @Override - public Node enter(final FunctionNode node) { + public Node enterFunctionNode(final FunctionNode node) { if (node == outermostFunctionNode) { return node; } @@ -94,15 +94,24 @@ enum CompilationPhase { lazy.remove(node); } - for (final FunctionNode node : lazy) { - Compiler.LOG.fine("Marking " + node.getName() + " as lazy"); - node.setIsLazy(true); - final FunctionNode parent = node.findParentFunction(); - if (parent != null) { - Compiler.LOG.fine("Marking " + parent.getName() + " as having lazy children - it needs scope for all variables"); - parent.setHasLazyChildren(); + outermostFunctionNode.accept(new NodeOperatorVisitor() { + private final LexicalContext lexicalContext = new LexicalContext(); + @Override + public Node enterFunctionNode(FunctionNode functionNode) { + lexicalContext.push(functionNode); + if(lazy.contains(functionNode)) { + Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy"); + functionNode.setIsLazy(true); + lexicalContext.getParentFunction(functionNode).setHasLazyChildren(); + } + return functionNode; } - } + @Override + public Node leaveFunctionNode(FunctionNode functionNode) { + lexicalContext.pop(functionNode); + return functionNode; + } + }); } @Override @@ -241,6 +250,16 @@ enum CompilationPhase { final CodeGenerator codegen = new CodeGenerator(compiler); fn.accept(codegen); codegen.generateScopeCalls(); + fn.accept(new NodeOperatorVisitor() { + @Override + public Node enterFunctionNode(FunctionNode functionNode) { + if(functionNode.isLazy()) { + functionNode.resetResolved(); + return null; + } + return fn; + } + }); } catch (final VerifyError e) { if (env._verify_code || env._print_code) { diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java index 87d26c1a..397f39a5 100644 --- a/src/jdk/nashorn/internal/codegen/Compiler.java +++ b/src/jdk/nashorn/internal/codegen/Compiler.java @@ -46,7 +46,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; - import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; @@ -383,7 +382,7 @@ public final class Compiler { functionNode.accept(new NodeVisitor() { @Override - public Node enter(final FunctionNode node) { + public Node enterFunctionNode(final FunctionNode node) { if (node.isLazy()) { return null; } diff --git a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java index b302d6fe..cd185240 100644 --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java @@ -34,20 +34,20 @@ import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CallNode.EvalArgs; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.DoWhileNode; import jdk.nashorn.internal.ir.ExecuteNode; 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; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; @@ -85,11 +85,13 @@ final class FinalizeTypes extends NodeOperatorVisitor { private static final DebugLogger LOG = new DebugLogger("finalize"); + private final LexicalContext lexicalContext = new LexicalContext(); + FinalizeTypes() { } @Override - public Node leave(final CallNode callNode) { + public Node leaveCallNode(final CallNode callNode) { final EvalArgs evalArgs = callNode.getEvalArgs(); if (evalArgs != null) { evalArgs.setCode(evalArgs.getCode().accept(this)); @@ -97,15 +99,14 @@ final class FinalizeTypes extends NodeOperatorVisitor { // AccessSpecializer - call return type may change the access for this location final Node function = callNode.getFunction(); - if (function instanceof ReferenceNode) { - setTypeOverride(callNode, ((ReferenceNode)function).getReference().getType()); + if (function instanceof FunctionNode) { + return setTypeOverride(callNode, ((FunctionNode)function).getReturnType()); } return callNode; } private Node leaveUnary(final UnaryNode unaryNode) { - unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType())); - return unaryNode; + return unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType())); } @Override @@ -126,8 +127,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveDECINC(final UnaryNode unaryNode) { - specialize(unaryNode); - return unaryNode; + return specialize(unaryNode).node; } @Override @@ -159,9 +159,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } } - binaryNode.setLHS(convert(lhs, type)); - binaryNode.setRHS(convert(rhs, type)); - return binaryNode; + return binaryNode.setLHS(convert(lhs, type)).setRHS(convert(rhs, type)); } @Override @@ -171,12 +169,13 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveASSIGN(final BinaryNode binaryNode) { - Type destType = specialize(binaryNode); + final SpecializedNode specialized = specialize(binaryNode); + final BinaryNode specBinaryNode = (BinaryNode)specialized.node; + Type destType = specialized.type; if (destType == null) { - destType = binaryNode.getType(); + destType = specBinaryNode.getType(); } - binaryNode.setRHS(convert(binaryNode.rhs(), destType)); - return binaryNode; + return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType)); } @Override @@ -255,21 +254,21 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveCOMMALEFT(final BinaryNode binaryNode) { assert binaryNode.getSymbol() != null; - binaryNode.setRHS(discard(binaryNode.rhs())); + final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs())); // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed // in that case, update the node type as well - propagateType(binaryNode, binaryNode.lhs().getType()); - return binaryNode; + propagateType(newBinaryNode, newBinaryNode.lhs().getType()); + return newBinaryNode; } @Override public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { assert binaryNode.getSymbol() != null; - binaryNode.setLHS(discard(binaryNode.lhs())); + final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs())); // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed // in that case, update the node type as well - propagateType(binaryNode, binaryNode.rhs().getType()); - return binaryNode; + propagateType(newBinaryNode, newBinaryNode.rhs().getType()); + return newBinaryNode; } @Override @@ -355,13 +354,20 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { + lexicalContext.push(block); updateSymbols(block); return block; } @Override - public Node leave(final CatchNode catchNode) { + public Node leaveBlock(Block block) { + lexicalContext.pop(block); + return super.leaveBlock(block); + } + + @Override + public Node leaveCatchNode(final CatchNode catchNode) { final Node exceptionCondition = catchNode.getExceptionCondition(); if (exceptionCondition != null) { catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); @@ -370,23 +376,23 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node enter(final DoWhileNode doWhileNode) { - return enter((WhileNode)doWhileNode); + public Node enterDoWhileNode(final DoWhileNode doWhileNode) { + return enterWhileNode(doWhileNode); } @Override - public Node leave(final DoWhileNode doWhileNode) { - return leave((WhileNode)doWhileNode); + public Node leaveDoWhileNode(final DoWhileNode doWhileNode) { + return leaveWhileNode(doWhileNode); } @Override - public Node leave(final ExecuteNode executeNode) { + public Node leaveExecuteNode(final ExecuteNode executeNode) { executeNode.setExpression(discard(executeNode.getExpression())); return executeNode; } @Override - public Node leave(final ForNode forNode) { + public Node leaveForNode(final ForNode forNode) { final Node init = forNode.getInit(); final Node test = forNode.getTest(); final Node modify = forNode.getModify(); @@ -414,11 +420,12 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { if (functionNode.isLazy()) { return null; } + lexicalContext.push(functionNode); // If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do // this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the // need for the callee. @@ -439,14 +446,20 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node leave(final IfNode ifNode) { + public Node leaveFunctionNode(FunctionNode functionNode) { + lexicalContext.pop(functionNode); + return super.leaveFunctionNode(functionNode); + } + + @Override + public Node leaveIfNode(final IfNode ifNode) { ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN)); return ifNode; } @SuppressWarnings("rawtypes") @Override - public Node enter(final LiteralNode literalNode) { + public Node enterLiteralNode(final LiteralNode literalNode) { if (literalNode instanceof ArrayLiteralNode) { final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; final Node[] array = arrayLiteralNode.getValue(); @@ -464,7 +477,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node leave(final ReturnNode returnNode) { + public Node leaveReturnNode(final ReturnNode returnNode) { final Node expr = returnNode.getExpression(); if (expr != null) { returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType())); @@ -473,7 +486,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node leave(final RuntimeNode runtimeNode) { + public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { final List args = runtimeNode.getArgs(); for (final Node arg : args) { assert !arg.getType().isUnknown(); @@ -482,7 +495,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node leave(final SwitchNode switchNode) { + public Node leaveSwitchNode(final SwitchNode switchNode) { final Node expression = switchNode.getExpression(); final List cases = switchNode.getCases(); final boolean allInteger = switchNode.getTag().getSymbolType().isInteger(); @@ -501,33 +514,34 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node leave(final TernaryNode ternaryNode) { - ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN)); - return ternaryNode; + public Node leaveTernaryNode(final TernaryNode ternaryNode) { + return ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN)); } @Override - public Node leave(final ThrowNode throwNode) { + public Node leaveThrowNode(final ThrowNode throwNode) { throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT)); return throwNode; } @Override - public Node leave(final VarNode varNode) { + public Node leaveVarNode(final VarNode varNode) { final Node rhs = varNode.getInit(); if (rhs != null) { - Type destType = specialize(varNode); + final SpecializedNode specialized = specialize(varNode); + final VarNode specVarNode = (VarNode)specialized.node; + Type destType = specialized.type; if (destType == null) { - destType = varNode.getType(); + destType = specVarNode.getType(); } - assert varNode.hasType() : varNode + " doesn't have a type"; - varNode.setInit(convert(rhs, destType)); + assert specVarNode.hasType() : specVarNode + " doesn't have a type"; + return specVarNode.setInit(convert(rhs, destType)); } return varNode; } @Override - public Node leave(final WhileNode whileNode) { + public Node leaveWhileNode(final WhileNode whileNode) { final Node test = whileNode.getTest(); if (test != null) { whileNode.setTest(convert(test, Type.BOOLEAN)); @@ -536,7 +550,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node leave(final WithNode withNode) { + public Node leaveWithNode(final WithNode withNode) { withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT)); return withNode; } @@ -555,14 +569,14 @@ final class FinalizeTypes extends NodeOperatorVisitor { * that scope and slot information is correct for every symbol * @param block block for which to to finalize type info. */ - private static void updateSymbols(final Block block) { + private void updateSymbols(final Block block) { if (!block.needsScope()) { return; // nothing to do } - assert !(block instanceof FunctionNode) || block.getFunction() == block; + final FunctionNode functionNode = lexicalContext.getFunction(block); + assert !(block instanceof FunctionNode) || functionNode == block; - final FunctionNode functionNode = block.getFunction(); final List symbols = block.getFrame().getSymbols(); final boolean allVarsInScope = functionNode.allVarsInScope(); final boolean isVarArg = functionNode.isVarArg(); @@ -631,10 +645,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { break; } - binaryNode.setLHS(convert(lhs, widest)); - binaryNode.setRHS(convert(rhs, widest)); - - return binaryNode; + return binaryNode.setLHS(convert(lhs, widest)).setRHS(convert(rhs, widest)); } /** @@ -656,9 +667,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) { - binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)); - binaryNode.setRHS(convert(binaryNode.rhs(), rhsType)); - return binaryNode; + return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType)); } /** @@ -679,7 +688,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node enter(final IdentNode identNode) { + public Node enterIdentNode(final IdentNode identNode) { if (!exclude.contains(identNode)) { setCanBePrimitive(identNode.getSymbol()); } @@ -687,26 +696,36 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node enter(final AccessNode accessNode) { + public Node enterAccessNode(final AccessNode accessNode) { setCanBePrimitive(accessNode.getProperty().getSymbol()); return null; } @Override - public Node enter(final IndexNode indexNode) { + public Node enterIndexNode(final IndexNode indexNode) { exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine return indexNode; } }); } - private static Type specialize(final Assignment assignment) { + private static class SpecializedNode { + final Node node; + final Type type; + + SpecializedNode(Node node, Type type) { + this.node = node; + this.type = type; + } + } + + private static SpecializedNode specialize(final Assignment assignment) { final Node node = ((Node)assignment); - final Node lhs = assignment.getAssignmentDest(); + final T lhs = assignment.getAssignmentDest(); final Node rhs = assignment.getAssignmentSource(); if (!canHaveCallSiteType(lhs)) { - return null; + return new SpecializedNode(node, null); } final Type to; @@ -718,13 +737,13 @@ final class FinalizeTypes extends NodeOperatorVisitor { if (!isSupportedCallSiteType(to)) { //meaningless to specialize to boolean or object - return null; + return new SpecializedNode(node, null); } - setTypeOverride(lhs, to); - propagateType(node, to); + final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to)); + propagateType(newNode, to); - return to; + return new SpecializedNode(newNode, to); } @@ -736,7 +755,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { * @return true if node can have a callsite type */ private static boolean canHaveCallSiteType(final Node node) { - return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType(); + return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType(); } /** @@ -762,7 +781,8 @@ final class FinalizeTypes extends NodeOperatorVisitor { * @param node node for which to change type * @param to new type */ - private static void setTypeOverride(final Node node, final Type to) { + @SuppressWarnings("unchecked") + private static T setTypeOverride(final T node, final Type to) { final Type from = node.getType(); if (!node.getType().equals(to)) { LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to); @@ -771,7 +791,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { } } LOG.info("Type override for lhs in '" + node + "' => " + to); - ((TypeOverride)node).setType(to); + return ((TypeOverride)node).setType(to); } /** @@ -816,8 +836,8 @@ final class FinalizeTypes extends NodeOperatorVisitor { } } else { if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) { - setTypeOverride(node, to); - return resultNode; + assert node instanceof TypeOverride; + return setTypeOverride(node, to); } resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node); } diff --git a/src/jdk/nashorn/internal/codegen/FoldConstants.java b/src/jdk/nashorn/internal/codegen/FoldConstants.java index af2b02ab..fbc62644 100644 --- a/src/jdk/nashorn/internal/codegen/FoldConstants.java +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java @@ -31,12 +31,12 @@ import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.UnaryNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.JSType; @@ -54,7 +54,7 @@ final class FoldConstants extends NodeVisitor { } @Override - public Node leave(final UnaryNode unaryNode) { + public Node leaveUnaryNode(final UnaryNode unaryNode) { final LiteralNode literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval(); if (literalNode != null) { LOG.info("Unary constant folded " + unaryNode + " to " + literalNode); @@ -64,7 +64,7 @@ final class FoldConstants extends NodeVisitor { } @Override - public Node leave(final BinaryNode binaryNode) { + public Node leaveBinaryNode(final BinaryNode binaryNode) { final LiteralNode literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); if (literalNode != null) { LOG.info("Binary constant folded " + binaryNode + " to " + literalNode); @@ -74,7 +74,7 @@ final class FoldConstants extends NodeVisitor { } @Override - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { if (functionNode.isLazy()) { return null; } @@ -82,13 +82,13 @@ final class FoldConstants extends NodeVisitor { } @Override - public Node leave(final FunctionNode functionNode) { + public Node leaveFunctionNode(final FunctionNode functionNode) { functionNode.setState(CompilationState.CONSTANT_FOLDED); return functionNode; } @Override - public Node leave(final IfNode ifNode) { + public Node leaveIfNode(final IfNode ifNode) { final Node test = ifNode.getTest(); if (test instanceof LiteralNode) { final Block shortCut = ((LiteralNode)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); @@ -101,7 +101,7 @@ final class FoldConstants extends NodeVisitor { } @Override - public Node leave(final TernaryNode ternaryNode) { + public Node leaveTernaryNode(final TernaryNode ternaryNode) { final Node test = ternaryNode.lhs(); if (test instanceof LiteralNode) { return ((LiteralNode)test).isTrue() ? ternaryNode.rhs() : ternaryNode.third(); diff --git a/src/jdk/nashorn/internal/codegen/FunctionSignature.java b/src/jdk/nashorn/internal/codegen/FunctionSignature.java index 5f3740a7..057d2d4e 100644 --- a/src/jdk/nashorn/internal/codegen/FunctionSignature.java +++ b/src/jdk/nashorn/internal/codegen/FunctionSignature.java @@ -155,7 +155,7 @@ public final class FunctionSignature { true, functionNode.needsCallee(), functionNode.getReturnType(), - (functionNode.isVarArg() && !functionNode.isScript()) ? + (functionNode.isVarArg() && !functionNode.isProgram()) ? null : functionNode.getParameters()); } diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java index 5baa7af5..715ddc6f 100644 --- a/src/jdk/nashorn/internal/codegen/Lower.java +++ b/src/jdk/nashorn/internal/codegen/Lower.java @@ -37,8 +37,8 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; +import java.util.Iterator; import java.util.List; -import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; @@ -52,11 +52,12 @@ import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ExecuteNode; 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; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LabeledNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; @@ -69,7 +70,6 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Token; @@ -103,6 +103,8 @@ final class Lower extends NodeOperatorVisitor { private List statements; + private LexicalContext lexicalContext = new LexicalContext(); + /** * Constructor. * @@ -114,14 +116,15 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { final Node savedLastStatement = lastStatement; final List savedStatements = statements; - + lexicalContext.push(block); try { this.statements = new ArrayList<>(); + NodeVisitor visitor = this; for (final Node statement : block.getStatements()) { - statement.accept(this); + statement.accept(visitor); /* * This is slightly unsound, for example if we have a loop with * a guarded statement like if (x) continue in the body and the @@ -133,7 +136,7 @@ final class Lower extends NodeOperatorVisitor { */ if (lastStatement != null && lastStatement.isTerminal()) { copyTerminal(block, lastStatement); - break; + visitor = new DeadCodeVarDeclarationVisitor(); } } block.setStatements(statements); @@ -141,18 +144,19 @@ final class Lower extends NodeOperatorVisitor { } finally { this.statements = savedStatements; this.lastStatement = savedLastStatement; + lexicalContext.pop(block); } return null; } @Override - public Node enter(final BreakNode breakNode) { + public Node enterBreakNode(final BreakNode breakNode) { return enterBreakOrContinue(breakNode); } @Override - public Node enter(final CallNode callNode) { + public Node enterCallNode(final CallNode callNode) { final Node function = markerFunction(callNode.getFunction()); callNode.setFunction(function); checkEval(callNode); //check if this is an eval call and store the information @@ -160,44 +164,44 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node leave(final CaseNode caseNode) { + public Node leaveCaseNode(final CaseNode caseNode) { caseNode.copyTerminalFlags(caseNode.getBody()); return caseNode; } @Override - public Node leave(final CatchNode catchNode) { + public Node leaveCatchNode(final CatchNode catchNode) { catchNode.copyTerminalFlags(catchNode.getBody()); addStatement(catchNode); return catchNode; } @Override - public Node enter(final ContinueNode continueNode) { + public Node enterContinueNode(final ContinueNode continueNode) { return enterBreakOrContinue(continueNode); } @Override - public Node enter(final DoWhileNode doWhileNode) { - return enter((WhileNode)doWhileNode); + public Node enterDoWhileNode(final DoWhileNode doWhileNode) { + return enterWhileNode(doWhileNode); } @Override - public Node leave(final DoWhileNode doWhileNode) { - return leave((WhileNode)doWhileNode); + public Node leaveDoWhileNode(final DoWhileNode doWhileNode) { + return leaveWhileNode(doWhileNode); } @Override - public Node enter(final EmptyNode emptyNode) { + public Node enterEmptyNode(final EmptyNode emptyNode) { return null; } @Override - public Node leave(final ExecuteNode executeNode) { + public Node leaveExecuteNode(final ExecuteNode executeNode) { final Node expr = executeNode.getExpression(); - if (getCurrentFunctionNode().isScript()) { - if (!(expr instanceof Block)) { + if (getCurrentFunctionNode().isProgram()) { + if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) { executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN), getCurrentFunctionNode().getResultNode(), @@ -213,13 +217,13 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node enter(final ForNode forNode) { + public Node enterForNode(final ForNode forNode) { nest(forNode); return forNode; } @Override - public Node leave(final ForNode forNode) { + public Node leaveForNode(final ForNode forNode) { final Node test = forNode.getTest(); final Block body = forNode.getBody(); @@ -247,18 +251,16 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { LOG.info("START FunctionNode: " + functionNode.getName()); if (functionNode.isLazy()) { LOG.info("LAZY: " + functionNode.getName()); return null; } - + lexicalContext.push(functionNode); initFunctionNode(functionNode); - Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED); - nest(functionNode); /* @@ -272,60 +274,40 @@ final class Lower extends NodeOperatorVisitor { statements = new ArrayList<>(); lastStatement = null; - // for initial eval result is the last declared function - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { - final IdentNode ident = nestedFunction.getIdent(); - if (ident != null && nestedFunction.isStatement()) { - initialEvalResult = new IdentNode(ident); - } - } - if (functionNode.needsSelfSymbol()) { //function needs to start with var funcIdent = __callee_; statements.add(functionNode.getSelfSymbolInit().accept(this)); } + NodeVisitor visitor = this; try { - // Every nested function needs a definition in the outer function with its name. Add these. - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { - final VarNode varNode = nestedFunction.getFunctionVarNode(); - if (varNode != null) { - final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode(); - if (lineNumberNode != null) { - lineNumberNode.accept(this); - } - varNode.accept(this); - varNode.setIsFunctionVarNode(); - } - } - - if (functionNode.isScript()) { - new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this); - } - //do the statements - this fills the block with code + boolean needsInitialEvalResult = functionNode.isProgram(); for (final Node statement : functionNode.getStatements()) { - statement.accept(this); + // If this function is a program, then insert an assignment to the initial eval result after all + // function declarations. + if(needsInitialEvalResult && !(statement instanceof LineNumberNode || (statement instanceof VarNode && ((VarNode)statement).isFunctionDeclaration()))) { + addInitialEvalResult(functionNode); + needsInitialEvalResult = false; + } + statement.accept(visitor); //If there are unused terminated endpoints in the function, we need // to add a "return undefined" in those places for correct semantics LOG.info("Checking lastStatement="+lastStatement+" for terminal flags"); if (lastStatement != null && lastStatement.hasTerminalFlags()) { copyTerminal(functionNode, lastStatement); - break; + assert !needsInitialEvalResult; + visitor = new DeadCodeVarDeclarationVisitor(); } } - + if(needsInitialEvalResult) { + addInitialEvalResult(functionNode); + } functionNode.setStatements(statements); if (!functionNode.isTerminal()) { guaranteeReturn(functionNode); } - - //lower all nested functions - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { - nestedFunction.accept(this); - } - } finally { statements = savedStatements; lastStatement = savedLastStatement; @@ -333,19 +315,67 @@ final class Lower extends NodeOperatorVisitor { LOG.info("END FunctionNode: " + functionNode.getName()); unnest(functionNode); + lexicalContext.pop(functionNode); functionNode.setState(CompilationState.LOWERED); return null; } + /** + * This visitor is used to go over statements after a terminal statement. Those statements are dead code, but the + * var declarations in them still have the effect of declaring a local variable on the function level. Therefore, + * they aren't really dead code and must be preserved. Note that they're only preserved as no-op declarations; their + * initializers are wiped out as those are, in fact, dead code. + */ + private class DeadCodeVarDeclarationVisitor extends NodeOperatorVisitor { + DeadCodeVarDeclarationVisitor() { + } + + @Override + public Node enterVarNode(VarNode varNode) { + // Can't ever see a function declaration, as this visitor is only ever used after a terminal statement was + // encountered, and all function declarations precede any terminal statements. + assert !varNode.isFunctionDeclaration(); + if(varNode.getInit() == null) { + // No initializer, just pass it to Lower. + return varNode.accept(Lower.this); + } + // Wipe out the initializer and then pass it to Lower. + return varNode.setInit(null).accept(Lower.this); + } + } + + private void addInitialEvalResult(final FunctionNode functionNode) { + new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(), + getInitialEvalResult(functionNode)).accept(this); + } + + /** + * Result of initial result of evaluating a particular program, which is either the last function it declares, or + * undefined if it doesn't declare any functions. + * @param program + * @return the initial result of evaluating the program + */ + private static Node getInitialEvalResult(final FunctionNode program) { + IdentNode lastFnName = null; + for (final FunctionNode fn : program.getDeclaredFunctions()) { + assert fn.isDeclared(); + final IdentNode fnName = fn.getIdent(); + if(fnName != null) { + lastFnName = fnName; + } + } + return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED); + } + @Override - public Node enter(final IfNode ifNode) { + public Node enterIfNode(final IfNode ifNode) { return nest(ifNode); } @Override - public Node leave(final IfNode ifNode) { + public Node leaveIfNode(final IfNode ifNode) { final Node pass = ifNode.getPass(); final Node fail = ifNode.getFail(); @@ -360,7 +390,7 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node enter(LabelNode labelNode) { + public Node enterLabelNode(LabelNode labelNode) { final Block body = labelNode.getBody(); body.accept(this); copyTerminal(labelNode, body); @@ -369,13 +399,13 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node enter(final LineNumberNode lineNumberNode) { + public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { addStatement(lineNumberNode, false); // don't put it in lastStatement cache return null; } @Override - public Node enter(final ReturnNode returnNode) { + public Node enterReturnNode(final ReturnNode returnNode) { final TryNode tryNode = returnNode.getTryChain(); final Node expr = returnNode.getExpression(); @@ -413,19 +443,19 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node leave(final ReturnNode returnNode) { + public Node leaveReturnNode(final ReturnNode returnNode) { addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor return returnNode; } @Override - public Node enter(final SwitchNode switchNode) { + public Node enterSwitchNode(final SwitchNode switchNode) { nest(switchNode); return switchNode; } @Override - public Node leave(final SwitchNode switchNode) { + public Node leaveSwitchNode(final SwitchNode switchNode) { unnest(switchNode); final List cases = switchNode.getCases(); @@ -446,13 +476,13 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node leave(final ThrowNode throwNode) { + public Node leaveThrowNode(final ThrowNode throwNode) { addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor return throwNode; } @Override - public Node enter(final TryNode tryNode) { + public Node enterTryNode(final TryNode tryNode) { final Block finallyBody = tryNode.getFinallyBody(); final long token = tryNode.getToken(); final int finish = tryNode.getFinish(); @@ -538,26 +568,19 @@ final class Lower extends NodeOperatorVisitor { // set outer tryNode's body to innerTryNode final Block outerBody; - outerBody = new Block(source, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode()); + outerBody = new Block(source, token, finish); outerBody.setStatements(new ArrayList(Arrays.asList(innerTryNode))); tryNode.setBody(outerBody); tryNode.setCatchBlocks(null); - - // now before we go on, we have to fix the block parents - // (we repair the block tree after the insertion so that all references are intact) - innerTryNode.getBody().setParent(tryNode.getBody()); - for (final Block block : innerTryNode.getCatchBlocks()) { - block.setParent(tryNode.getBody()); - } } // create a catch-all that inlines finally and rethrows - final Block catchBlock = new Block(source, token, finish, getCurrentBlock(), getCurrentFunctionNode()); + final Block catchBlock = new Block(source, token, finish); //this catch block should get define symbol - final Block catchBody = new Block(source, token, finish, catchBlock, getCurrentFunctionNode()); - final Node catchAllFinally = finallyBody.clone(); + final Block catchBody = new Block(source, token, finish); + final Node catchAllFinally = finallyBody.copy(); catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally)); setTerminal(catchBody, true); @@ -584,7 +607,7 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node leave(final TryNode tryNode) { + public Node leaveTryNode(final TryNode tryNode) { final Block finallyBody = tryNode.getFinallyBody(); boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal()); @@ -608,18 +631,18 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node leave(final VarNode varNode) { + public Node leaveVarNode(final VarNode varNode) { addStatement(varNode); return varNode; } @Override - public Node enter(final WhileNode whileNode) { + public Node enterWhileNode(final WhileNode whileNode) { return nest(whileNode); } @Override - public Node leave(final WhileNode whileNode) { + public Node leaveWhileNode(final WhileNode whileNode) { final Node test = whileNode.getTest(); if (test == null) { @@ -653,7 +676,7 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node leave(final WithNode withNode) { + public Node leaveWithNode(final WithNode withNode) { if (withNode.getBody().isTerminal()) { setTerminal(withNode, true); } @@ -682,28 +705,10 @@ final class Lower extends NodeOperatorVisitor { */ private static Node markerFunction(final Node function) { if (function instanceof IdentNode) { - return new IdentNode((IdentNode)function) { - @Override - public boolean isFunction() { - return true; - } - }; - } else if (function instanceof AccessNode) { - return new AccessNode((AccessNode)function) { - @Override - public boolean isFunction() { - return true; - } - }; - } else if (function instanceof IndexNode) { - return new IndexNode((IndexNode)function) { - @Override - public boolean isFunction() { - return true; - } - }; + return ((IdentNode)function).setIsFunction(); + } else if (function instanceof BaseNode) { + return ((BaseNode)function).setIsFunction(); } - return function; } @@ -746,7 +751,7 @@ final class Lower extends NodeOperatorVisitor { if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) { final CallNode.EvalArgs evalArgs = new CallNode.EvalArgs( - args.get(0).clone().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case" + args.get(0).copy().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case" getCurrentFunctionNode().getThisNode(), evalLocation(callee), getCurrentFunctionNode().isStrictMode()); @@ -773,13 +778,13 @@ final class Lower extends NodeOperatorVisitor { loopBody.accept(new NodeVisitor() { @Override - public Node leave(final BreakNode node) { + public Node leaveBreakNode(final BreakNode node) { escapes.add(node); return node; } @Override - public Node leave(final ContinueNode node) { + public Node leaveContinueNode(final ContinueNode node) { // all inner loops have been popped. if (nesting.contains(node.getTargetNode())) { escapes.add(node); @@ -794,7 +799,7 @@ final class Lower extends NodeOperatorVisitor { private void guaranteeReturn(final FunctionNode functionNode) { Node resultNode; - if (functionNode.isScript()) { + if (functionNode.isProgram()) { resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr } else { if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) { @@ -859,18 +864,15 @@ final class Lower extends NodeOperatorVisitor { * @return true if try block is inside the target, false otherwise. */ private boolean isNestedTry(final TryNode tryNode, final Block target) { - for (Block current = getCurrentBlock(); current != target; current = current.getParent()) { - if (tryNode.getBody() == current) { - return true; + for(Iterator blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) { + final Block block = blocks.next(); + if(block == target) { + return false; } - - for (final Block catchBlock : tryNode.getCatchBlocks()) { - if (catchBlock == current) { - return true; - } + if(tryNode.isChildBlock(block)) { + return true; } } - return false; } @@ -899,7 +901,7 @@ final class Lower extends NodeOperatorVisitor { continue; } - finallyBody = (Block)finallyBody.clone(); + finallyBody = (Block)finallyBody.copy(); final boolean hasTerminalFlags = finallyBody.hasTerminalFlags(); new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this); @@ -974,6 +976,3 @@ final class Lower extends NodeOperatorVisitor { } } - - - diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java index 4cd0f524..ae40ed33 100644 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java @@ -651,11 +651,10 @@ public class MethodEmitter implements Emitter { /** * Load the constants array - * @param unitClassName name of the compile unit from which to load constants * @return this method emitter */ - MethodEmitter loadConstants(final String unitClassName) { - getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor()); + MethodEmitter loadConstants() { + getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor()); assert peekType().isArray() : peekType(); return this; } diff --git a/src/jdk/nashorn/internal/codegen/Splitter.java b/src/jdk/nashorn/internal/codegen/Splitter.java index b338ced5..f9a84f91 100644 --- a/src/jdk/nashorn/internal/codegen/Splitter.java +++ b/src/jdk/nashorn/internal/codegen/Splitter.java @@ -41,6 +41,7 @@ import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LabelNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; @@ -49,6 +50,7 @@ import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.WhileNode; +import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.Source; @@ -70,6 +72,8 @@ final class Splitter extends NodeVisitor { /** Cache for calculated block weights. */ private final Map weightCache = new HashMap<>(); + private final LexicalContext lexicalContext = new LexicalContext(); + /** Weight threshold for when to start a split. */ public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024); @@ -112,7 +116,7 @@ final class Splitter extends NodeVisitor { } if (weight >= SPLIT_THRESHOLD) { - weight = splitBlock(functionNode); + weight = splitBlock(functionNode, functionNode); } if (functionNode.isSplit()) { @@ -132,9 +136,20 @@ final class Splitter extends NodeVisitor { } // Recursively split nested functions - for (final FunctionNode function : functionNode.getFunctions()) { - new Splitter(compiler, function, outermostCompileUnit).split(); - } + functionNode.accept(new NodeOperatorVisitor() { + @Override + public Node enterFunctionNode(FunctionNode function) { + if(function == functionNode) { + // Don't process outermost function (it was already processed) but descend into it to find nested + // functions. + return function; + } + // Process a nested function + new Splitter(compiler, function, outermostCompileUnit).split(); + // Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions. + return null; + } + }); functionNode.setState(CompilationState.SPLIT); } @@ -155,7 +170,7 @@ final class Splitter extends NodeVisitor { * * @return new weight for the resulting block. */ - private long splitBlock(final Block block) { + private long splitBlock(final Block block, final FunctionNode function) { functionNode.setIsSplit(); final List splits = new ArrayList<>(); @@ -167,7 +182,7 @@ final class Splitter extends NodeVisitor { if (statementsWeight + weight >= SPLIT_THRESHOLD || statement.isTerminal()) { if (!statements.isEmpty()) { - splits.add(createBlockSplitNode(block, statements, statementsWeight)); + splits.add(createBlockSplitNode(block, function, statements, statementsWeight)); statements = new ArrayList<>(); statementsWeight = 0; } @@ -183,7 +198,7 @@ final class Splitter extends NodeVisitor { } if (!statements.isEmpty()) { - splits.add(createBlockSplitNode(block, statements, statementsWeight)); + splits.add(createBlockSplitNode(block, function, statements, statementsWeight)); } block.setStatements(splits); @@ -199,13 +214,13 @@ final class Splitter extends NodeVisitor { * * @return New split node. */ - private SplitNode createBlockSplitNode(final Block parent, final List statements, final long weight) { + private SplitNode createBlockSplitNode(final Block parent, final FunctionNode function, final List statements, final long weight) { final Source source = parent.getSource(); final long token = parent.getToken(); final int finish = parent.getFinish(); - final String name = parent.getFunction().uniqueName(SPLIT_PREFIX.tag()); + final String name = function.uniqueName(SPLIT_PREFIX.tag()); - final Block newBlock = new Block(source, token, finish, parent, functionNode); + final Block newBlock = new Block(source, token, finish); newBlock.setFrame(new Frame(parent.getFrame())); newBlock.setStatements(statements); @@ -217,15 +232,17 @@ final class Splitter extends NodeVisitor { } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { if (block.isCatchBlock()) { return null; } + lexicalContext.push(block); final long weight = WeighNodes.weigh(block, weightCache); if (weight < SPLIT_THRESHOLD) { weightCache.put(block, weight); + lexicalContext.pop(block); return null; } @@ -233,23 +250,24 @@ final class Splitter extends NodeVisitor { } @Override - public Node leave(final Block block) { + public Node leaveBlock(final Block block) { assert !block.isCatchBlock(); // Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have // been split already, so weigh again before splitting. long weight = WeighNodes.weigh(block, weightCache); if (weight >= SPLIT_THRESHOLD) { - weight = splitBlock(block); + weight = splitBlock(block, lexicalContext.getFunction(block)); } weightCache.put(block, weight); + lexicalContext.pop(block); return block; } @SuppressWarnings("rawtypes") @Override - public Node leave(final LiteralNode literal) { + public Node leaveLiteralNode(final LiteralNode literal) { long weight = WeighNodes.weigh(literal); if (weight < SPLIT_THRESHOLD) { @@ -294,17 +312,12 @@ final class Splitter extends NodeVisitor { } @Override - public Node enter(final FunctionNode node) { - if (node.isLazy()) { - return null; + public Node enterFunctionNode(final FunctionNode node) { + if(node == functionNode && !node.isLazy()) { + lexicalContext.push(node); + node.visitStatements(this); + lexicalContext.pop(node); } - - final List statements = node.getStatements(); - - for (final Node statement : statements) { - statement.accept(this); - } - return null; } @@ -321,38 +334,38 @@ final class Splitter extends NodeVisitor { } @Override - public Node enter(final LabelNode labelNode) { + public Node enterLabelNode(final LabelNode labelNode) { registerJumpTarget(labelNode.getBreakNode()); registerJumpTarget(labelNode.getContinueNode()); return labelNode; } @Override - public Node enter(final WhileNode whileNode) { + public Node enterWhileNode(final WhileNode whileNode) { registerJumpTarget(whileNode); return whileNode; } @Override - public Node enter(final DoWhileNode doWhileNode) { + public Node enterDoWhileNode(final DoWhileNode doWhileNode) { registerJumpTarget(doWhileNode); return doWhileNode; } @Override - public Node enter(final ForNode forNode) { + public Node enterForNode(final ForNode forNode) { registerJumpTarget(forNode); return forNode; } @Override - public Node enter(final SwitchNode switchNode) { + public Node enterSwitchNode(final SwitchNode switchNode) { registerJumpTarget(switchNode); return switchNode; } @Override - public Node enter(final ReturnNode returnNode) { + public Node enterReturnNode(final ReturnNode returnNode) { for (final SplitNode split : splitStack) { split.setHasReturn(true); } @@ -360,25 +373,25 @@ final class Splitter extends NodeVisitor { } @Override - public Node enter(final ContinueNode continueNode) { + public Node enterContinueNode(final ContinueNode continueNode) { searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel()); return continueNode; } @Override - public Node enter(final BreakNode breakNode) { + public Node enterBreakNode(final BreakNode breakNode) { searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel()); return breakNode; } @Override - public Node enter(final SplitNode splitNode) { + public Node enterSplitNode(final SplitNode splitNode) { splitStack.addFirst(splitNode); return splitNode; } @Override - public Node leave(final SplitNode splitNode) { + public Node leaveSplitNode(final SplitNode splitNode) { assert splitNode == splitStack.peekFirst(); splitStack.removeFirst(); return splitNode; diff --git a/src/jdk/nashorn/internal/codegen/WeighNodes.java b/src/jdk/nashorn/internal/codegen/WeighNodes.java index 9f2e8be4..18bd9552 100644 --- a/src/jdk/nashorn/internal/codegen/WeighNodes.java +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java @@ -47,7 +47,6 @@ import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.SplitNode; @@ -80,7 +79,7 @@ final class WeighNodes extends NodeOperatorVisitor { private static final long LITERAL_WEIGHT = 10; private static final long LOOP_WEIGHT = 4; private static final long NEW_WEIGHT = 6; - private static final long REFERENCE_WEIGHT = 20; + private static final long FUNC_EXPR_WEIGHT = 20; private static final long RETURN_WEIGHT = 2; private static final long SPLIT_WEIGHT = 40; private static final long SWITCH_WEIGHT = 8; @@ -94,36 +93,37 @@ final class WeighNodes extends NodeOperatorVisitor { /** Optional cache for weight of block nodes. */ private final Map weightCache; - /* + private final FunctionNode topFunction; + + /** * Constructor * * @param weightCache cache of already calculated block weights */ - private WeighNodes(final Map weightCache) { + private WeighNodes(FunctionNode topFunction, final Map weightCache) { super(null, null); + this.topFunction = topFunction; this.weightCache = weightCache; } static long weigh(final Node node) { - final WeighNodes weighNodes = new WeighNodes(null); - node.accept(weighNodes); - return weighNodes.weight; + return weigh(node, null); } static long weigh(final Node node, final Map weightCache) { - final WeighNodes weighNodes = new WeighNodes(weightCache); + final WeighNodes weighNodes = new WeighNodes(node instanceof FunctionNode ? (FunctionNode)node : null, weightCache); node.accept(weighNodes); return weighNodes.weight; } @Override - public Node leave(final AccessNode accessNode) { + public Node leaveAccessNode(final AccessNode accessNode) { weight += ACCESS_WEIGHT; return accessNode; } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { if (weightCache != null && weightCache.containsKey(block)) { weight += weightCache.get(block); return null; @@ -133,78 +133,79 @@ final class WeighNodes extends NodeOperatorVisitor { } @Override - public Node leave(final BreakNode breakNode) { + public Node leaveBreakNode(final BreakNode breakNode) { weight += BREAK_WEIGHT; return breakNode; } @Override - public Node leave(final CallNode callNode) { + public Node leaveCallNode(final CallNode callNode) { weight += CALL_WEIGHT; return callNode; } @Override - public Node leave(final CatchNode catchNode) { + public Node leaveCatchNode(final CatchNode catchNode) { weight += CATCH_WEIGHT; return catchNode; } @Override - public Node leave(final ContinueNode continueNode) { + public Node leaveContinueNode(final ContinueNode continueNode) { weight += CONTINUE_WEIGHT; return continueNode; } @Override - public Node leave(final DoWhileNode doWhileNode) { + public Node leaveDoWhileNode(final DoWhileNode doWhileNode) { weight += LOOP_WEIGHT; return doWhileNode; } @Override - public Node leave(final ExecuteNode executeNode) { + public Node leaveExecuteNode(final ExecuteNode executeNode) { return executeNode; } @Override - public Node leave(final ForNode forNode) { + public Node leaveForNode(final ForNode forNode) { weight += LOOP_WEIGHT; return forNode; } @Override - public Node enter(final FunctionNode functionNode) { - final List statements = functionNode.getStatements(); - - for (final Node statement : statements) { - statement.accept(this); + public Node enterFunctionNode(final FunctionNode functionNode) { + if(functionNode == topFunction) { + // the function being weighted; descend into its statements + functionNode.visitStatements(this); + } else { + // just a reference to inner function from outer function + weight += FUNC_EXPR_WEIGHT; } - return null; } @Override - public Node leave(final IdentNode identNode) { + public Node leaveIdentNode(final IdentNode identNode) { weight += ACCESS_WEIGHT + identNode.getName().length() * 2; return identNode; } @Override - public Node leave(final IfNode ifNode) { + public Node leaveIfNode(final IfNode ifNode) { weight += IF_WEIGHT; return ifNode; } @Override - public Node leave(final IndexNode indexNode) { + public Node leaveIndexNode(final IndexNode indexNode) { weight += ACCESS_WEIGHT; return indexNode; } @SuppressWarnings("rawtypes") @Override - public Node enter(final LiteralNode literalNode) { + public Node enterLiteralNode(final LiteralNode literalNode) { weight += LITERAL_WEIGHT; if (literalNode instanceof ArrayLiteralNode) { @@ -230,67 +231,61 @@ final class WeighNodes extends NodeOperatorVisitor { } @Override - public Node leave(final PropertyNode propertyNode) { + public Node leavePropertyNode(final PropertyNode propertyNode) { weight += LITERAL_WEIGHT; return propertyNode; } @Override - public Node leave(final ReferenceNode referenceNode) { - weight += REFERENCE_WEIGHT; - return referenceNode; - } - - @Override - public Node leave(final ReturnNode returnNode) { + public Node leaveReturnNode(final ReturnNode returnNode) { weight += RETURN_WEIGHT; return returnNode; } @Override - public Node leave(final RuntimeNode runtimeNode) { + public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { weight += CALL_WEIGHT; return runtimeNode; } @Override - public Node enter(final SplitNode splitNode) { + public Node enterSplitNode(final SplitNode splitNode) { weight += SPLIT_WEIGHT; return null; } @Override - public Node leave(final SwitchNode switchNode) { + public Node leaveSwitchNode(final SwitchNode switchNode) { weight += SWITCH_WEIGHT; return switchNode; } @Override - public Node leave(final ThrowNode throwNode) { + public Node leaveThrowNode(final ThrowNode throwNode) { weight += THROW_WEIGHT; return throwNode; } @Override - public Node leave(final TryNode tryNode) { + public Node leaveTryNode(final TryNode tryNode) { weight += THROW_WEIGHT; return tryNode; } @Override - public Node leave(final VarNode varNode) { + public Node leaveVarNode(final VarNode varNode) { weight += VAR_WEIGHT; return varNode; } @Override - public Node leave(final WhileNode whileNode) { + public Node leaveWhileNode(final WhileNode whileNode) { weight += LOOP_WEIGHT; return whileNode; } @Override - public Node leave(final WithNode withNode) { + public Node leaveWithNode(final WithNode withNode) { weight += WITH_WEIGHT; return withNode; } diff --git a/src/jdk/nashorn/internal/ir/AccessNode.java b/src/jdk/nashorn/internal/ir/AccessNode.java index 481e3855..b7b76684 100644 --- a/src/jdk/nashorn/internal/ir/AccessNode.java +++ b/src/jdk/nashorn/internal/ir/AccessNode.java @@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.Source; * IR representation of a property access (period operator.) * */ -public class AccessNode extends BaseNode implements TypeOverride { +public class AccessNode extends BaseNode implements TypeOverride { /** Property ident. */ private IdentNode property; @@ -56,9 +56,7 @@ public class AccessNode extends BaseNode implements TypeOverride { super(source, token, finish, base); this.start = base.getStart(); - this.property = property; - - this.property.setIsPropertyName(); + this.property = property.setIsPropertyName(); } /** @@ -106,10 +104,10 @@ public class AccessNode extends BaseNode implements TypeOverride { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterAccessNode(this) != null) { base = base.accept(visitor); property = (IdentNode)property.accept(visitor); - return visitor.leave(this); + return visitor.leaveAccessNode(this); } return this; @@ -150,13 +148,14 @@ public class AccessNode extends BaseNode implements TypeOverride { } @Override - public void setType(final Type type) { + public AccessNode setType(final Type type) { if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType()); } - property.setType(type); + property = property.setType(type); getSymbol().setTypeOverride(type); //always a temp so this is fine. hasCallSiteType = true; + return this; } @Override diff --git a/src/jdk/nashorn/internal/ir/Assignment.java b/src/jdk/nashorn/internal/ir/Assignment.java index 8107a872..0c531bc2 100644 --- a/src/jdk/nashorn/internal/ir/Assignment.java +++ b/src/jdk/nashorn/internal/ir/Assignment.java @@ -46,4 +46,11 @@ public interface Assignment { * @return get the assignment source node */ public Node getAssignmentSource(); + + /** + * Set assignment destination node. + * @param n the assignment destination node. + * @return a node equivalent to this one except for the requested change. + */ + public Node setAssignmentDest(D n); } diff --git a/src/jdk/nashorn/internal/ir/BaseNode.java b/src/jdk/nashorn/internal/ir/BaseNode.java index ea632d1b..26a28368 100644 --- a/src/jdk/nashorn/internal/ir/BaseNode.java +++ b/src/jdk/nashorn/internal/ir/BaseNode.java @@ -38,6 +38,8 @@ public abstract class BaseNode extends Node implements FunctionCall { /** Base Node. */ protected Node base; + private boolean function; + /** * Constructor * @@ -96,6 +98,15 @@ public abstract class BaseNode extends Node implements FunctionCall { @Override public boolean isFunction() { - return false; + return function; + } + + /** + * Mark this node as being the callee operand of a {@link CallNode}. + * @return a base node identical to this one in all aspects except with its function flag set. + */ + public BaseNode setIsFunction() { + function = true; + return this; } } diff --git a/src/jdk/nashorn/internal/ir/BinaryNode.java b/src/jdk/nashorn/internal/ir/BinaryNode.java index 26964ba5..42c7a6cc 100644 --- a/src/jdk/nashorn/internal/ir/BinaryNode.java +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java @@ -35,7 +35,7 @@ import jdk.nashorn.internal.runtime.Source; */ public class BinaryNode extends UnaryNode { /** Left hand side argument. */ - protected Node lhs; + private Node lhs; /** * Constructor @@ -139,6 +139,11 @@ public class BinaryNode extends UnaryNode { return isAssignment() ? lhs() : null; } + @Override + public Node setAssignmentDest(Node n) { + return setLHS(n); + } + @Override public Node getAssignmentSource() { return rhs(); @@ -163,10 +168,9 @@ public class BinaryNode extends UnaryNode { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - lhs = lhs.accept(visitor); - rhs = rhs.accept(visitor); - return visitor.leave(this); + if (visitor.enterBinaryNode(this) != null) { + // TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers + return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor))); } return this; @@ -229,8 +233,12 @@ public class BinaryNode extends UnaryNode { /** * Set the left hand side expression for this node * @param lhs new left hand side expression + * @return a node equivalent to this one except for the requested change. */ - public void setLHS(final Node lhs) { - this.lhs = lhs; + public BinaryNode setLHS(final Node lhs) { + if(this.lhs == lhs) return this; + final BinaryNode n = (BinaryNode)clone(); + n.lhs = lhs; + return n; } } diff --git a/src/jdk/nashorn/internal/ir/Block.java b/src/jdk/nashorn/internal/ir/Block.java index e729d307..6f138f85 100644 --- a/src/jdk/nashorn/internal/ir/Block.java +++ b/src/jdk/nashorn/internal/ir/Block.java @@ -25,14 +25,6 @@ package jdk.nashorn.internal.ir; -import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; -import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; -import static jdk.nashorn.internal.ir.Symbol.IS_LET; -import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; -import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE; -import static jdk.nashorn.internal.ir.Symbol.IS_VAR; -import static jdk.nashorn.internal.ir.Symbol.KINDMASK; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; @@ -40,9 +32,9 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import jdk.nashorn.internal.codegen.Frame; import jdk.nashorn.internal.codegen.Label; -import jdk.nashorn.internal.ir.annotations.Reference; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Source; @@ -51,14 +43,6 @@ import jdk.nashorn.internal.runtime.Source; * basis for script body. */ public class Block extends Node { - /** Parent context */ - @Reference - private Block parent; - - /** Owning function - a FunctionNode has itself as function */ - @Reference - protected FunctionNode function; - /** List of statements */ protected List statements; @@ -83,14 +67,10 @@ public class Block extends Node { * @param source source code * @param token token * @param finish finish - * @param parent reference to parent block - * @param function function node this block is in */ - public Block(final Source source, final long token, final int finish, final Block parent, final FunctionNode function) { + public Block(final Source source, final long token, final int finish) { super(source, token, finish); - this.parent = parent; - this.function = function; this.statements = new ArrayList<>(); this.symbols = new HashMap<>(); this.entryLabel = new Label("block_entry"); @@ -106,8 +86,6 @@ public class Block extends Node { protected Block(final Block block, final CopyState cs) { super(block); - this.parent = block.parent; - this.function = block.function; this.statements = new ArrayList<>(); for (final Node statement : block.getStatements()) { statements.add(cs.existingOrCopy(statement)); @@ -122,55 +100,7 @@ public class Block extends Node { @Override protected Node copy(final CopyState cs) { - return fixBlockChain(new Block(this, cs)); - } - - /** - * Whenever a clone that contains a hierarchy of blocks is created, - * this function has to be called to ensure that the parents point - * to the correct parent blocks or two different ASTs would not - * be completely separated. - * - * @return the argument - */ - static Block fixBlockChain(final Block root) { - root.accept(new NodeVisitor() { - private Block parent = root.getParent(); - private final FunctionNode function = root.getFunction(); - - @Override - public Node enter(final Block block) { - assert block.getFunction() == function; - block.setParent(parent); - parent = block; - - return block; - } - - @Override - public Node leave(final Block block) { - parent = block.getParent(); - - return block; - } - - @Override - public Node enter(final FunctionNode functionNode) { - assert functionNode.getFunction() == function; - - return enter((Block)functionNode); - } - - @Override - public Node leave(final FunctionNode functionNode) { - assert functionNode.getFunction() == function; - - return leave((Block)functionNode); - } - - }); - - return root; + return new Block(this, cs); } /** @@ -188,17 +118,12 @@ public class Block extends Node { } /** - * Prepend a statement to the statement list + * Prepend statements to the statement list * - * @param statement Statement node to add + * @param prepended statement to add */ - public void prependStatement(final Node statement) { - if (statement != null) { - final List newStatements = new ArrayList<>(); - newStatements.add(statement); - newStatements.addAll(statements); - setStatements(newStatements); - } + public void prependStatements(final List prepended) { + statements.addAll(0, prepended); } /** @@ -210,39 +135,6 @@ public class Block extends Node { statements.addAll(statementList); } - /** - * Add a new function to the function list. - * - * @param functionNode Function node to add. - */ - public void addFunction(final FunctionNode functionNode) { - assert parent != null : "Parent context missing."; - - parent.addFunction(functionNode); - } - - /** - * Add a list of functions to the function list. - * - * @param functionNodes Function nodes to add. - */ - public void addFunctions(final List functionNodes) { - assert parent != null : "Parent context missing."; - - parent.addFunctions(functionNodes); - } - - /** - * Set the function list to a new one - * - * @param functionNodes the nodes to set - */ - public void setFunctions(final List functionNodes) { - assert parent != null : "Parent context missing."; - - parent.setFunctions(functionNodes); - } - /** * Assist in IR navigation. * @@ -257,13 +149,9 @@ public class Block extends Node { try { // Ignore parent to avoid recursion. - if (visitor.enter(this) != null) { - for (int i = 0, count = statements.size(); i < count; i++) { - final Node statement = statements.get(i); - statements.set(i, statement.accept(visitor)); - } - - return visitor.leave(this); + if (visitor.enterBlock(this) != null) { + visitStatements(visitor); + return visitor.leaveBlock(this); } } finally { visitor.setCurrentBlock(saveBlock); @@ -281,51 +169,13 @@ public class Block extends Node { } /** - * Search for symbol. - * - * @param name Symbol name. - * - * @return Found symbol or null if not found. + * Retrieves an existing symbol defined in the current block. + * @param name the name of the symbol + * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't + * define a symbol with this name. */ - public Symbol findSymbol(final String name) { - // Search up block chain to locate symbol. - - for (Block block = this; block != null; block = block.getParent()) { - // Find name. - final Symbol symbol = block.symbols.get(name); - // If found then we are good. - if (symbol != null) { - return symbol; - } - } - return null; - } - - /** - * Search for symbol in current function. - * - * @param name Symbol name. - * - * @return Found symbol or null if not found. - */ - public Symbol findLocalSymbol(final String name) { - // Search up block chain to locate symbol. - for (Block block = this; block != null; block = block.getParent()) { - // Find name. - final Symbol symbol = block.symbols.get(name); - // If found then we are good. - if (symbol != null) { - return symbol; - } - - // If searched function then we are done. - if (block == block.function) { - break; - } - } - - // Not found. - return null; + public Symbol getExistingSymbol(final String name) { + return symbols.get(name); } /** @@ -338,122 +188,6 @@ public class Block extends Node { return statements.size() == 1 && statements.get(0) instanceof CatchNode; } - /** - * Test to see if a symbol is local to the function. - * - * @param symbol Symbol to test. - * @return True if a local symbol. - */ - public boolean isLocal(final Symbol symbol) { - // some temp symbols have no block, so can be assumed local - final Block block = symbol.getBlock(); - return block == null || block.getFunction() == function; - } - - /** - * Declare the definition of a new symbol. - * - * @param name Name of symbol. - * @param symbolFlags Symbol flags. - * @param node Defining Node. - * - * @return Symbol for given name or null for redefinition. - */ - public Symbol defineSymbol(final String name, final int symbolFlags, final Node node) { - int flags = symbolFlags; - Symbol symbol = findSymbol(name); // Locate symbol. - - if ((flags & KINDMASK) == IS_GLOBAL) { - flags |= IS_SCOPE; - } - - if (symbol != null) { - // Symbol was already defined. Check if it needs to be redefined. - if ((flags & KINDMASK) == IS_PARAM) { - if (!function.isLocal(symbol)) { - // Not defined in this function. Create a new definition. - symbol = null; - } else if (symbol.isParam()) { - // Duplicate parameter. Null return will force an error. - assert false : "duplicate parameter"; - return null; - } - } else if ((flags & KINDMASK) == IS_VAR) { - if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & Symbol.IS_LET) == Symbol.IS_LET) { - assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == this) : "duplicate let variable in block"; - // Always create a new definition. - symbol = null; - } else { - // Not defined in this function. Create a new definition. - if (!function.isLocal(symbol) || symbol.less(IS_VAR)) { - symbol = null; - } - } - } - } - - if (symbol == null) { - // If not found, then create a new one. - Block symbolBlock; - - // Determine where to create it. - if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { - symbolBlock = this; - } else { - symbolBlock = getFunction(); - } - - // Create and add to appropriate block. - symbol = new Symbol(name, flags, node, symbolBlock); - symbolBlock.putSymbol(name, symbol); - - if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { - symbolBlock.getFrame().addSymbol(symbol); - symbol.setNeedsSlot(true); - } - } else if (symbol.less(flags)) { - symbol.setFlags(flags); - } - - if (node != null) { - node.setSymbol(symbol); - } - - return symbol; - } - - /** - * Declare the use of a symbol. - * - * @param name Name of symbol. - * @param node Using node - * - * @return Symbol for given name. - */ - public Symbol useSymbol(final String name, final Node node) { - Symbol symbol = findSymbol(name); - - if (symbol == null) { - // If not found, declare as a free var. - symbol = defineSymbol(name, IS_GLOBAL, node); - } else { - node.setSymbol(symbol); - } - - return symbol; - } - - /** - * Add parent name to the builder. - * - * @param sb String bulder. - */ - public void addParentName(final StringBuilder sb) { - if (parent != null) { - parent.addParentName(sb); - } - } - @Override public void toString(final StringBuilder sb) { for (final Node statement : statements) { @@ -511,16 +245,6 @@ public class Block extends Node { return frame; } - /** - * Get the FunctionNode for this block, i.e. the function it - * belongs to - * - * @return the function node - */ - public FunctionNode getFunction() { - return function; - } - /** * Reset the frame for this block * @@ -530,24 +254,6 @@ public class Block extends Node { this.frame = frame; } - /** - * Get the parent block - * - * @return parent block, or null if none exists - */ - public Block getParent() { - return parent; - } - - /** - * Set the parent block - * - * @param parent the new parent block - */ - public void setParent(final Block parent) { - this.parent = parent; - } - /** * Get the list of statements in this block * @@ -557,6 +263,15 @@ public class Block extends Node { return Collections.unmodifiableList(statements); } + /** + * Applies the specified visitor to all statements in the block. + * @param visitor the visitor. + */ + public void visitStatements(NodeVisitor visitor) { + for (ListIterator stmts = statements.listIterator(); stmts.hasNext();) { + stmts.set(stmts.next().accept(visitor)); + } + } /** * Reset the statement list for this block * @@ -592,4 +307,29 @@ public class Block extends Node { needsScope = true; } + /** + * Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not + * including the block defining the symbol will be marked as needing parent scope. The block defining the symbol + * will be marked as one that needs to have its own scope. + * @param symbol the symbol being used. + * @param ancestors the iterator over block's containing lexical context + */ + public void setUsesScopeSymbol(final Symbol symbol, Iterator ancestors) { + if(symbol.getBlock() == this) { + setNeedsScope(); + } else { + setUsesParentScopeSymbol(symbol, ancestors); + } + } + + /** + * Invoked when this block uses a scope symbol defined in one of its ancestors. + * @param symbol the scope symbol being used + * @param ancestors iterator over ancestor blocks + */ + void setUsesParentScopeSymbol(final Symbol symbol, Iterator ancestors) { + if(ancestors.hasNext()) { + ancestors.next().setUsesScopeSymbol(symbol, ancestors); + } + } } diff --git a/src/jdk/nashorn/internal/ir/BreakNode.java b/src/jdk/nashorn/internal/ir/BreakNode.java index 81c572d4..7ad0dc6d 100644 --- a/src/jdk/nashorn/internal/ir/BreakNode.java +++ b/src/jdk/nashorn/internal/ir/BreakNode.java @@ -64,8 +64,8 @@ public class BreakNode extends LabeledNode { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - return visitor.leave(this); + if (visitor.enterBreakNode(this) != null) { + return visitor.leaveBreakNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/CallNode.java b/src/jdk/nashorn/internal/ir/CallNode.java index 387369e6..3410709c 100644 --- a/src/jdk/nashorn/internal/ir/CallNode.java +++ b/src/jdk/nashorn/internal/ir/CallNode.java @@ -37,7 +37,7 @@ import jdk.nashorn.internal.runtime.Source; * IR representation for a function call. * */ -public class CallNode extends Node implements TypeOverride { +public class CallNode extends Node implements TypeOverride { private Type type; @@ -176,13 +176,13 @@ public class CallNode extends Node implements TypeOverride { if (hasCallSiteType()) { return type; } - assert !function.getType().isUnknown(); - return function.getType(); + return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT; } @Override - public void setType(final Type type) { + public CallNode setType(final Type type) { this.type = type; + return this; } private boolean hasCallSiteType() { @@ -208,14 +208,14 @@ public class CallNode extends Node implements TypeOverride { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterCallNode(this) != null) { function = function.accept(visitor); for (int i = 0, count = args.size(); i < count; i++) { args.set(i, args.get(i).accept(visitor)); } - return visitor.leave(this); + return visitor.leaveCallNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/CaseNode.java b/src/jdk/nashorn/internal/ir/CaseNode.java index 928ba5be..61b89217 100644 --- a/src/jdk/nashorn/internal/ir/CaseNode.java +++ b/src/jdk/nashorn/internal/ir/CaseNode.java @@ -79,7 +79,7 @@ public class CaseNode extends BreakableNode { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterCaseNode(this) != null) { if (test != null) { test = test.accept(visitor); } @@ -87,7 +87,7 @@ public class CaseNode extends BreakableNode { body = (Block)body.accept(visitor); } - return visitor.leave(this); + return visitor.leaveCaseNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/CatchNode.java b/src/jdk/nashorn/internal/ir/CatchNode.java index 187c3949..005ffa8e 100644 --- a/src/jdk/nashorn/internal/ir/CatchNode.java +++ b/src/jdk/nashorn/internal/ir/CatchNode.java @@ -84,7 +84,7 @@ public class CatchNode extends Node { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterCatchNode(this) != null) { exception = (IdentNode)exception.accept(visitor); if (exceptionCondition != null) { @@ -92,7 +92,7 @@ public class CatchNode extends Node { } body = (Block)body.accept(visitor); - return visitor.leave(this); + return visitor.leaveCatchNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/ContinueNode.java b/src/jdk/nashorn/internal/ir/ContinueNode.java index 2063d6b2..cbc7bff2 100644 --- a/src/jdk/nashorn/internal/ir/ContinueNode.java +++ b/src/jdk/nashorn/internal/ir/ContinueNode.java @@ -61,8 +61,8 @@ public class ContinueNode extends LabeledNode { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - return visitor.leave(this); + if (visitor.enterContinueNode(this) != null) { + return visitor.leaveContinueNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/DoWhileNode.java b/src/jdk/nashorn/internal/ir/DoWhileNode.java index 476643a1..3939795e 100644 --- a/src/jdk/nashorn/internal/ir/DoWhileNode.java +++ b/src/jdk/nashorn/internal/ir/DoWhileNode.java @@ -63,11 +63,11 @@ public class DoWhileNode extends WhileNode { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterDoWhileNode(this) != null) { body = (Block)body.accept(visitor); test = test.accept(visitor); - return visitor.leave(this); + return visitor.leaveDoWhileNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/EmptyNode.java b/src/jdk/nashorn/internal/ir/EmptyNode.java index 0bfacd5d..15330a37 100644 --- a/src/jdk/nashorn/internal/ir/EmptyNode.java +++ b/src/jdk/nashorn/internal/ir/EmptyNode.java @@ -57,8 +57,8 @@ public class EmptyNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - return visitor.leave(this); + if (visitor.enterEmptyNode(this) != null) { + return visitor.leaveEmptyNode(this); } return this; } diff --git a/src/jdk/nashorn/internal/ir/ExecuteNode.java b/src/jdk/nashorn/internal/ir/ExecuteNode.java index 501468bb..8ae7d556 100644 --- a/src/jdk/nashorn/internal/ir/ExecuteNode.java +++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java @@ -85,9 +85,9 @@ public class ExecuteNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterExecuteNode(this) != null) { setExpression(expression.accept(visitor)); - return visitor.leave(this); + return visitor.leaveExecuteNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/ForNode.java b/src/jdk/nashorn/internal/ir/ForNode.java index 53b56752..e55054dd 100644 --- a/src/jdk/nashorn/internal/ir/ForNode.java +++ b/src/jdk/nashorn/internal/ir/ForNode.java @@ -76,7 +76,7 @@ public class ForNode extends WhileNode { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterForNode(this) != null) { if (init != null) { init = init.accept(visitor); } @@ -91,7 +91,7 @@ public class ForNode extends WhileNode { body = (Block)body.accept(visitor); - return visitor.leave(this); + return visitor.leaveForNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java index 7fd77cd0..2b0e109b 100644 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java @@ -34,7 +34,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; -import java.util.LinkedList; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; @@ -47,7 +47,7 @@ import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Parser; -import jdk.nashorn.internal.runtime.Debug; +import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.UserAccessorProperty; import jdk.nashorn.internal.runtime.linker.LinkerCallSite; @@ -57,6 +57,8 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite; */ public class FunctionNode extends Block { + private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class); + /** Function kinds */ public enum Kind { /** a normal function - nothing special */ @@ -112,9 +114,6 @@ public class FunctionNode extends Block { /** List of parameters. */ private List parameters; - /** List of nested functions. */ - private List functions; - /** First token of function. **/ private long firstToken; @@ -157,10 +156,6 @@ public class FunctionNode extends Block { /** Pending control list. */ private final Stack controlStack; - /** Variable declarations in the function's scope */ - @Ignore - private final List declarations; - /** VarNode for this function statement */ @Ignore //this is explicit code anyway and should not be traversed after lower private VarNode funcVarNode; @@ -184,33 +179,35 @@ public class FunctionNode extends Block { private int flags; /** Is anonymous function flag. */ - private static final int IS_ANONYMOUS = 0b0000_0000_0000_0001; - /** Is statement flag */ - private static final int IS_STATEMENT = 0b0000_0000_0000_0010; + private static final int IS_ANONYMOUS = 1 << 0; + /** Is the function created in a function declaration (as opposed to a function expression) */ + private static final int IS_DECLARED = 1 << 1; /** is this a strict mode function? */ - private static final int IS_STRICT_MODE = 0b0000_0000_0000_0100; + private static final int IS_STRICT_MODE = 1 << 2; /** Does the function use the "arguments" identifier ? */ - private static final int USES_ARGUMENTS = 0b0000_0000_0000_1000; + private static final int USES_ARGUMENTS = 1 << 3; /** Are we lowered ? */ - private static final int IS_LOWERED = 0b0000_0000_0001_0000; + private static final int IS_LOWERED = 1 << 4; /** Has this node been split because it was too large? */ - private static final int IS_SPLIT = 0b0000_0000_0010_0000; + private static final int IS_SPLIT = 1 << 5; /** Does the function call eval? */ - private static final int HAS_EVAL = 0b0000_0000_0100_0000; + private static final int HAS_EVAL = 1 << 6; /** Does the function contain a with block ? */ - private static final int HAS_WITH = 0b0000_0000_1000_0000; + private static final int HAS_WITH = 1 << 7; /** Does a descendant function contain a with or eval? */ - private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0001_0000_0000; + private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8; /** Does the function define "arguments" identifier as a parameter of nested function name? */ - private static final int DEFINES_ARGUMENTS = 0b0000_0010_0000_0000; + private static final int DEFINES_ARGUMENTS = 1 << 9; /** Does the function need a self symbol? */ - private static final int NEEDS_SELF_SYMBOL = 0b0000_0100_0000_0000; + private static final int NEEDS_SELF_SYMBOL = 1 << 10; /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */ - private static final int USES_ANCESTOR_SCOPE = 0b0000_1000_0000_0000; + private static final int USES_ANCESTOR_SCOPE = 1 << 11; /** Is this function lazily compiled? */ - private static final int IS_LAZY = 0b0001_0000_0000_0000; + private static final int IS_LAZY = 1 << 12; + /** Does this function have lazy, yet uncompiled children */ + private static final int HAS_LAZY_CHILDREN = 1 << 13; /** Does this function have lazy, yet uncompiled children */ - private static final int HAS_LAZY_CHILDREN = 0b0010_0000_0000_0000; + private static final int IS_PROGRAM = 1 << 14; /** Does this function or any nested functions contain a with or an eval? */ private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL; @@ -225,14 +222,6 @@ public class FunctionNode extends Block { /** What is the return type of this function? */ private Type returnType = Type.UNKNOWN; - /** - * Used to keep track of a function's parent blocks. - * This is needed when a (finally body) block is cloned than contains inner functions. - * Does not include function.getParent(). - */ - @Ignore - private List referencingParentBlocks; - /** * Constructor * @@ -240,42 +229,28 @@ public class FunctionNode extends Block { * @param token token * @param finish finish * @param namespace the namespace - * @param parent the parent block * @param ident the identifier * @param name the name of the function */ - @SuppressWarnings("LeakingThisInConstructor") - public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final Block parent, final IdentNode ident, final String name) { - super(source, token, finish, parent, null); + public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) { + super(source, token, finish); this.ident = ident; this.name = name; this.kind = Kind.NORMAL; this.parameters = new ArrayList<>(); - this.functions = new ArrayList<>(); this.firstToken = token; this.lastToken = token; this.namespace = namespace; this.labelStack = new Stack<>(); this.controlStack = new Stack<>(); - this.declarations = new ArrayList<>(); - // my block -> function is this. We added @SuppressWarnings("LeakingThisInConstructor") as NetBeans identifies - // it as such a leak - this is a false positive as we're setting this into a field of the object being - // constructed, so it can't be seen from other threads. - this.function = this; this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.specializedTypes = new HashMap<>(); } - @SuppressWarnings("LeakingThisInConstructor") private FunctionNode(final FunctionNode functionNode, final CopyState cs) { super(functionNode, cs); - this.functions = new ArrayList<>(); - for (final FunctionNode f : functionNode.getFunctions()) { - this.functions.add((FunctionNode)cs.existingOrCopy(f)); - } - this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident); this.name = functionNode.name; this.kind = functionNode.kind; @@ -296,22 +271,12 @@ public class FunctionNode extends Block { this.calleeNode = (IdentNode)cs.existingOrCopy(functionNode.calleeNode); this.labelStack = new Stack<>(); this.controlStack = new Stack<>(); - this.declarations = new ArrayList<>(); - - for (final VarNode decl : functionNode.getDeclarations()) { - declarations.add((VarNode) cs.existingOrCopy(decl)); //TODO same? - } this.flags = functionNode.flags; this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode); /** VarNode for this function statement */ - // my block -> function is this. We added @SuppressWarnings("LeakingThisInConstructor") as NetBeans identifies - // it as such a leak - this is a false positive as we're setting this into a field of the object being - // constructed, so it can't be seen from other threads. - this.function = this; - this.compilationState = EnumSet.copyOf(functionNode.compilationState); this.specializedTypes = new HashMap<>(); } @@ -319,21 +284,21 @@ public class FunctionNode extends Block { @Override protected Node copy(final CopyState cs) { // deep clone all parent blocks - return fixBlockChain(new FunctionNode(this, cs)); + return new FunctionNode(this, cs); } @Override public Node accept(final NodeVisitor visitor) { - final FunctionNode saveFunctionNode = visitor.getCurrentFunctionNode(); - final Block saveBlock = visitor.getCurrentBlock(); + final FunctionNode saveFunctionNode = visitor.getCurrentFunctionNode(); + final Block saveBlock = visitor.getCurrentBlock(); + final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter(); + final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit(); visitor.setCurrentFunctionNode(this); - visitor.setCurrentCompileUnit(getCompileUnit()); - visitor.setCurrentMethodEmitter(getMethodEmitter()); visitor.setCurrentBlock(this); try { - if (visitor.enter(this) != null) { + if (visitor.enterFunctionNode(this) != null) { if (ident != null) { ident = (IdentNode)ident.accept(visitor); } @@ -342,51 +307,25 @@ public class FunctionNode extends Block { parameters.set(i, (IdentNode)parameters.get(i).accept(visitor)); } - for (int i = 0, count = functions.size(); i < count; i++) { - functions.set(i, (FunctionNode)functions.get(i).accept(visitor)); - } - for (int i = 0, count = statements.size(); i < count; i++) { statements.set(i, statements.get(i).accept(visitor)); } - return visitor.leave(this); + return visitor.leaveFunctionNode(this); } } finally { visitor.setCurrentBlock(saveBlock); visitor.setCurrentFunctionNode(saveFunctionNode); - visitor.setCurrentCompileUnit(saveFunctionNode != null ? saveFunctionNode.getCompileUnit() : null); - visitor.setCurrentMethodEmitter(saveFunctionNode != null ? saveFunctionNode.getMethodEmitter() : null); + visitor.setCurrentCompileUnit(saveCompileUnit); + visitor.setCurrentMethodEmitter(saveMethodEmitter); } return this; } - /** - * Locate the parent function. - * - * @return Parent function. - */ - public FunctionNode findParentFunction() { - return getParent() != null ? getParent().getFunction() : null; - } - - /** - * Add parent name to the builder. - * - * @param sb String builder. - */ - @Override - public void addParentName(final StringBuilder sb) { - if (!isScript()) { - sb.append(getName()); - sb.append("$"); - } - } - @Override public boolean needsScope() { - return super.needsScope() || isScript(); + return super.needsScope() || isProgram(); } /** @@ -544,12 +483,18 @@ public class FunctionNode extends Block { } /** - * Determine if script function. - * - * @return True if script function. + * Returns true if the function is the top-level program. + * @return True if this function node represents the top-level program. */ - public boolean isScript() { - return getParent() == null; + public boolean isProgram() { + return (flags & IS_PROGRAM) != 0; + } + + /** + * Marks the function as representing the top-level program. + */ + public void setProgram() { + flags |= IS_PROGRAM; } /** @@ -589,31 +534,31 @@ public class FunctionNode extends Block { /** * Flag this function as using the {@code with} keyword + * @param ancestors the iterator over functions in this functions's containing lexical context */ - public void setHasWith() { + public void setHasWith(final Iterator ancestors) { if(!hasWith()) { this.flags |= HAS_WITH; // with requires scope in parents. // TODO: refine this. with should not force all variables in parents to be in scope, only those that are // actually referenced as identifiers by name - markParentForWithOrEval(); + markParentForWithOrEval(ancestors); } } - private void markParentForWithOrEval() { + private void markParentForWithOrEval(final Iterator ancestors) { // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope. setNeedsScope(); - final FunctionNode parentFunction = findParentFunction(); - if(parentFunction != null) { - parentFunction.setDescendantHasWithOrEval(); + if(ancestors.hasNext()) { + ancestors.next().setDescendantHasWithOrEval(ancestors); } } - private void setDescendantHasWithOrEval() { + private void setDescendantHasWithOrEval(final Iterator ancestors) { if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) { flags |= HAS_DESCENDANT_WITH_OR_EVAL; - markParentForWithOrEval(); + markParentForWithOrEval(ancestors); } } @@ -628,11 +573,12 @@ public class FunctionNode extends Block { /** * Flag this function as calling the {@code eval} function + * @param ancestors the iterator over functions in this functions's containing lexical context */ - public void setHasEval() { + public void setHasEval(final Iterator ancestors) { if(!hasEval()) { this.flags |= HAS_EVAL; - markParentForWithOrEval(); + markParentForWithOrEval(ancestors); } } @@ -665,11 +611,34 @@ public class FunctionNode extends Block { } /** - * Get all nested functions - * @return list of nested functions in this function - */ - public List getFunctions() { - return Collections.unmodifiableList(functions); + * Returns a list of functions declared by this function. Only includes declared functions, and does not include any + * function expressions that might occur in its body. + * @return a list of functions declared by this function. + */ + public List getDeclaredFunctions() { + // Note that the function does not have a dedicated list of declared functions, but rather relies on the + // invariant that all function declarations are at the beginning of the statement list as VarNode with a + // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a + // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors. + final List fns = new ArrayList<>(); + for (final Node stmt : statements) { + if(stmt instanceof LineNumberNode) { + continue; + } else if(stmt instanceof VarNode) { + final Node init = ((VarNode)stmt).getInit(); + if(init instanceof FunctionNode) { + final FunctionNode fn = (FunctionNode)init; + if(fn.isDeclared()) { + fns.add(fn); + continue; + } + } + } + // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are + // at the start of the function, we've reached the end of function declarations. + break; + } + return fns; } /** @@ -801,7 +770,7 @@ public class FunctionNode extends Block { public boolean needsArguments() { // uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since // for top-level script, "arguments" is picked up from Context by Global.init() instead. - return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isScript(); + return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram(); } /** @@ -820,7 +789,7 @@ public class FunctionNode extends Block { * @return true if the function needs parent scope. */ public boolean needsParentScope() { - return (flags & NEEDS_PARENT_SCOPE) != 0 || isScript(); + return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram(); } /** @@ -880,7 +849,7 @@ public class FunctionNode extends Block { * @return true if all variables should be in scope */ public boolean allVarsInScope() { - return isScript() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0; + return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0; } /** @@ -989,19 +958,19 @@ public class FunctionNode extends Block { } /** - * Check if this function is a statement - * @return true if function is a statement + * Check if this function is created as a function declaration (as opposed to function expression) + * @return true if function is declared. */ - public boolean isStatement() { - return (flags & IS_STATEMENT) != 0; + public boolean isDeclared() { + return (flags & IS_DECLARED) != 0; } /** - * Flag this function as a statement + * Flag this function as being created as a function declaration (as opposed to a function expression). * @see Parser */ - public void setIsStatement() { - this.flags |= IS_STATEMENT; + public void setIsDeclared() { + this.flags |= IS_DECLARED; } /** @@ -1049,35 +1018,16 @@ public class FunctionNode extends Block { } /** - * Marks this function as one using any global symbol. The function and all its parent functions will all be marked - * as needing parent scope. - * @see #needsParentScope() + * Marks this function as using any of its ancestors' scopes. */ - public void setUsesGlobalSymbol() { + public void setUsesAncestorScope() { this.flags |= USES_ANCESTOR_SCOPE; - final FunctionNode parentFn = findParentFunction(); - if(parentFn != null) { - parentFn.setUsesGlobalSymbol(); - } } - /** - * Marks this function as using a specified scoped symbol. The function and its parent functions up to but not - * including the function defining the symbol will be marked as needing parent scope. The function defining the - * symbol will be marked as one that needs to have its own scope. - * @param symbol the symbol being used. - * @see #needsParentScope() - */ - public void setUsesScopeSymbol(final Symbol symbol) { - if(symbol.getBlock() == this) { - setNeedsScope(); - } else { - this.flags |= USES_ANCESTOR_SCOPE; - final FunctionNode parentFn = findParentFunction(); - if (parentFn != null) { - parentFn.setUsesScopeSymbol(symbol); - } - } + @Override + void setUsesParentScopeSymbol(Symbol symbol, Iterator ancestors) { + setUsesAncestorScope(); + super.setUsesParentScopeSymbol(symbol, ancestors); } /** @@ -1152,7 +1102,7 @@ public class FunctionNode extends Block { @Override public Type getType() { - return getReturnType(); + return FUNCTION_TYPE; } /** @@ -1211,56 +1161,6 @@ public class FunctionNode extends Block { return (flags & IS_LOWERED) != 0; } - /** - * Add a new function to the function list. - * - * @param functionNode Function node to add. - */ - @Override - public void addFunction(final FunctionNode functionNode) { - assert functionNode != null; - functions.add(functionNode); - } - - /** - * Add a list of functions to the function list. - * - * @param functionNodes Function nodes to add. - */ - @Override - public void addFunctions(final List functionNodes) { - functions.addAll(functionNodes); - } - - /** - * Set a function list - * - * @param functionNodes to set - */ - @Override - public void setFunctions(final List functionNodes) { - this.functions = functionNodes; - } - - /** - * Add a variable declaration that should be visible to the entire function - * scope. Parser does this. - * - * @param varNode a var node - */ - public void addDeclaration(final VarNode varNode) { - declarations.add(varNode); - } - - /** - * Return all variable declarations from this function scope - * - * @return all VarNodes in scope - */ - public List getDeclarations() { - return Collections.unmodifiableList(declarations); - } - /** * Get the compile unit used to compile this function * @see Compiler @@ -1294,32 +1194,4 @@ public class FunctionNode extends Block { public void setMethodEmitter(final MethodEmitter method) { this.method = method; } - - /** - * Each FunctionNode maintains a list of reference to its parent blocks. - * Add a parent block to this function. - * - * @param parentBlock a block to remember as parent - */ - public void addReferencingParentBlock(final Block parentBlock) { - assert parentBlock.getFunction() == function.findParentFunction() : Debug.id(parentBlock.getFunction()) + "!=" + Debug.id(function.findParentFunction()); // all parent blocks must be in the same function - if (parentBlock != function.getParent()) { - if (referencingParentBlocks == null) { - referencingParentBlocks = new LinkedList<>(); - } - referencingParentBlocks.add(parentBlock); - } - } - - /** - * Get the known parent blocks to this function - * - * @return list of parent blocks - */ - public List getReferencingParentBlocks() { - if (referencingParentBlocks == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(referencingParentBlocks); - } } diff --git a/src/jdk/nashorn/internal/ir/IdentNode.java b/src/jdk/nashorn/internal/ir/IdentNode.java index b0570a25..889a8700 100644 --- a/src/jdk/nashorn/internal/ir/IdentNode.java +++ b/src/jdk/nashorn/internal/ir/IdentNode.java @@ -38,18 +38,18 @@ import jdk.nashorn.internal.runtime.Source; /** * IR representation for an identifier. */ -public class IdentNode extends Node implements PropertyKey, TypeOverride, FunctionCall { +public class IdentNode extends Node implements PropertyKey, TypeOverride, FunctionCall { + private static final int PROPERTY_NAME = 1 << 0; + private static final int INITIALIZED_HERE = 1 << 1; + private static final int FUNCTION = 1 << 2; + /** Identifier. */ private final String name; /** Type for a callsite, e.g. X in a get()X or a set(X)V */ private Type callSiteType; - /** flag for an ident that is the property name of an AccessNode. */ - private boolean isPropertyName; - - /** flag for an ident on the left hand side of var lhs = rhs;. */ - private boolean isInitializedHere; + private byte flags; /** * Constructor @@ -71,9 +71,8 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride, Functi */ public IdentNode(final IdentNode identNode) { super(identNode); - this.name = identNode.getName(); - this.isPropertyName = identNode.isPropertyName; - this.isInitializedHere = identNode.isInitializedHere; + this.name = identNode.getName(); + this.flags = identNode.flags; } @Override @@ -92,12 +91,17 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride, Functi } @Override - public void setType(final Type type) { + public IdentNode setType(final Type type) { if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType()); } - this.callSiteType = type; // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't + if(this.callSiteType == type) { + return this; + } + final IdentNode n = (IdentNode)clone(); + n.callSiteType = type; + return n; } @Override @@ -131,8 +135,8 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride, Functi */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - return visitor.leave(this); + if (visitor.enterIdentNode(this) != null) { + return visitor.leaveIdentNode(this); } return this; @@ -179,14 +183,18 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride, Functi * @return true if this is a property name */ public boolean isPropertyName() { - return isPropertyName; + return (flags & PROPERTY_NAME) != 0; } /** * Flag this IdentNode as a property name + * @return a node equivalent to this one except for the requested change. */ - public void setIsPropertyName() { - isPropertyName = true; + public IdentNode setIsPropertyName() { + if(isPropertyName()) return this; + final IdentNode n = (IdentNode)clone(); + n.flags |= PROPERTY_NAME; + return n; } /** @@ -194,14 +202,18 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride, Functi * @return true if IdentNode is initialized on creation */ public boolean isInitializedHere() { - return isInitializedHere; + return (flags & INITIALIZED_HERE) != 0; } /** * Flag IdentNode to be initialized on creation + * @return a node equivalent to this one except for the requested change. */ - public void setIsInitializedHere() { - isInitializedHere = true; + public IdentNode setIsInitializedHere() { + if(isInitializedHere()) return this; + final IdentNode n = (IdentNode)clone(); + n.flags |= INITIALIZED_HERE; + return n; } /** @@ -216,6 +228,17 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride, Functi @Override public boolean isFunction() { - return false; + return (flags & FUNCTION) != 0; + } + + /** + * Mark this node as being the callee operand of a {@link CallNode}. + * @return an ident node identical to this one in all aspects except with its function flag set. + */ + public IdentNode setIsFunction() { + if(isFunction()) return this; + final IdentNode n = (IdentNode)clone(); + n.flags |= FUNCTION; + return n; } } diff --git a/src/jdk/nashorn/internal/ir/IfNode.java b/src/jdk/nashorn/internal/ir/IfNode.java index 81148c38..3ddcf1dc 100644 --- a/src/jdk/nashorn/internal/ir/IfNode.java +++ b/src/jdk/nashorn/internal/ir/IfNode.java @@ -75,7 +75,7 @@ public class IfNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterIfNode(this) != null) { test = test.accept(visitor); pass = (Block)pass.accept(visitor); @@ -84,7 +84,7 @@ public class IfNode extends Node { fail = (Block)fail.accept(visitor); } - return visitor.leave(this); + return visitor.leaveIfNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/IndexNode.java b/src/jdk/nashorn/internal/ir/IndexNode.java index 9feb5ebf..4745bf64 100644 --- a/src/jdk/nashorn/internal/ir/IndexNode.java +++ b/src/jdk/nashorn/internal/ir/IndexNode.java @@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.Source; * IR representation of an indexed access (brackets operator.) * */ -public class IndexNode extends BaseNode implements TypeOverride { +public class IndexNode extends BaseNode implements TypeOverride { /** Property ident. */ private Node index; @@ -92,10 +92,10 @@ public class IndexNode extends BaseNode implements TypeOverride { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterIndexNode(this) != null) { base = base.accept(visitor); index = index.accept(visitor); - return visitor.leave(this); + return visitor.leaveIndexNode(this); } return this; @@ -144,12 +144,13 @@ public class IndexNode extends BaseNode implements TypeOverride { } @Override - public void setType(final Type type) { + public IndexNode setType(final Type type) { if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType()); } hasCallSiteType = true; getSymbol().setTypeOverride(type); + return this; } @Override diff --git a/src/jdk/nashorn/internal/ir/LabelNode.java b/src/jdk/nashorn/internal/ir/LabelNode.java index c61bfcbd..756ea2df 100644 --- a/src/jdk/nashorn/internal/ir/LabelNode.java +++ b/src/jdk/nashorn/internal/ir/LabelNode.java @@ -81,10 +81,10 @@ public class LabelNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterLabelNode(this) != null) { label = (IdentNode)label.accept(visitor); body = (Block)body.accept(visitor); - return visitor.leave(this); + return visitor.leaveLabelNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/LexicalContext.java b/src/jdk/nashorn/internal/ir/LexicalContext.java new file mode 100644 index 00000000..2db1f796 --- /dev/null +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java @@ -0,0 +1,198 @@ +package jdk.nashorn.internal.ir; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A class that tracks the current lexical context of node visitation as a stack of {@link Block} nodes. Has special + * methods to retrieve useful subsets of the context. + */ +public class LexicalContext implements Cloneable { + private final Deque lexicalContext; + + /** + * Creates a new empty lexical context. + */ + public LexicalContext() { + lexicalContext = new ArrayDeque<>(); + } + + /** + * Pushes a new block on top of the context, making it the innermost open block. + * @param block the new block + */ + public void push(Block block) { + //new Exception(block.toString()).printStackTrace(); + lexicalContext.push(block); + } + + /** + * Pops the innermost block off the context. + * @param the block expected to be popped, used to detect unbalanced pushes/pops + */ + public void pop(Block block) { + final Block popped = lexicalContext.pop(); + assert popped == block; + } + + /** + * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first. + * @return an iterator over all blocks in the context. + */ + public Iterator getBlocks() { + return lexicalContext.iterator(); + } + + /** + * Returns an iterator over all functions in the context, with the top (innermost open) function first. + * @return an iterator over all functions in the context. + */ + public Iterator getFunctions() { + return new FunctionIterator(getBlocks()); + } + + private static final class FunctionIterator implements Iterator { + private final Iterator it; + private FunctionNode next; + + FunctionIterator(Iterator it) { + this.it = it; + next = findNext(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public FunctionNode next() { + if(next == null) { + throw new NoSuchElementException(); + } + FunctionNode lnext = next; + next = findNext(); + return lnext; + } + + private FunctionNode findNext() { + while(it.hasNext()) { + final Block block = it.next(); + if(block instanceof FunctionNode) { + return ((FunctionNode)block); + } + } + return null; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns an iterator over all ancestors block of the given block, with its parent block first. + * @param block the block whose ancestors are returned + * @return an iterator over all ancestors block of the given block. + */ + public Iterator getAncestorBlocks(Block block) { + final Iterator it = getBlocks(); + while(it.hasNext()) { + final Block b = it.next(); + if(block == b) { + return it; + } + } + throw new AssertionError("Block is not on the current lexical context stack"); + } + + /** + * Returns an iterator over a block and all its ancestors blocks, with the block first. + * @param block the block that is the starting point of the iteration. + * @return an iterator over a block and all its ancestors. + */ + public Iterator getBlocks(final Block block) { + final Iterator it = getAncestorBlocks(block); + return new Iterator() { + boolean blockReturned = false; + @Override + public boolean hasNext() { + return it.hasNext() || !blockReturned; + } + @Override + public Block next() { + if(blockReturned) { + return it.next(); + } + blockReturned = true; + return block; + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Returns the closest function node to the block. If the block is itself a function, it is returned. + * @param block the block + * @return the function closest to the block. + * @see #getParentFunction(Block) + */ + public FunctionNode getFunction(Block block) { + if(block instanceof FunctionNode) { + return (FunctionNode)block; + } + return getParentFunction(block); + } + + /** + * Returns the closest function node to the block and all its ancestor functions. If the block is itself a function, + * it is returned too. + * @param block the block + * @return the closest function node to the block and all its ancestor functions. + */ + public Iterator getFunctions(final Block block) { + return new FunctionIterator(getBlocks(block)); + } + + /** + * Returns the containing function of the block. If the block is itself a function, its parent function is returned. + * @param block the block + * @return the containing function of the block. + * @see #getFunction(Block) + */ + public FunctionNode getParentFunction(Block block) { + return getFirstFunction(getAncestorBlocks(block)); + } + + private static FunctionNode getFirstFunction(Iterator it) { + while(it.hasNext()) { + final Block ancestor = it.next(); + if(ancestor instanceof FunctionNode) { + return (FunctionNode)ancestor; + } + } + return null; + } + + /** + * Returns the innermost block in the context. + * @return the innermost block in the context. + */ + public Block getCurrentBlock() { + return lexicalContext.element(); + } + + /** + * Returns the innermost function in the context. + * @return the innermost function in the context. + */ + public FunctionNode getCurrentFunction() { + return getFirstFunction(getBlocks()); + } +} diff --git a/src/jdk/nashorn/internal/ir/LineNumberNode.java b/src/jdk/nashorn/internal/ir/LineNumberNode.java index ef1c05e1..c7912ff0 100644 --- a/src/jdk/nashorn/internal/ir/LineNumberNode.java +++ b/src/jdk/nashorn/internal/ir/LineNumberNode.java @@ -63,8 +63,8 @@ public class LineNumberNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - return visitor.leave(this); + if (visitor.enterLineNumberNode(this) != null) { + return visitor.leaveLineNumberNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/LiteralNode.java b/src/jdk/nashorn/internal/ir/LiteralNode.java index f1bf1b80..cc424b7a 100644 --- a/src/jdk/nashorn/internal/ir/LiteralNode.java +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java @@ -46,7 +46,7 @@ import jdk.nashorn.internal.runtime.Undefined; */ public abstract class LiteralNode extends Node implements PropertyKey { /** Literal value */ - protected T value; + protected final T value; /** * Constructor @@ -67,8 +67,17 @@ public abstract class LiteralNode extends Node implements PropertyKey { * @param literalNode source node */ protected LiteralNode(final LiteralNode literalNode) { + this(literalNode, literalNode.value); + } + + /** + * A copy constructor with value change. + * @param literalNode the original literal node + * @param newValue new value for this node + */ + protected LiteralNode(final LiteralNode literalNode, final T newValue) { super(literalNode); - this.value = literalNode.value; + this.value = newValue; } @Override @@ -217,8 +226,8 @@ public abstract class LiteralNode extends Node implements PropertyKey { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - return visitor.leave(this); + if (visitor.enterLiteralNode(this) != null) { + return visitor.leaveLiteralNode(this); } return this; @@ -544,6 +553,10 @@ public abstract class LiteralNode extends Node implements PropertyKey { super(literalNode); } + private NodeLiteralNode(final LiteralNode literalNode, final Node value) { + super(literalNode, value); + } + @Override protected Node copy(final CopyState cs) { return new NodeLiteralNode(this); @@ -551,11 +564,14 @@ public abstract class LiteralNode extends Node implements PropertyKey { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterLiteralNode(this) != null) { if (value != null) { - value = value.accept(visitor); + final Node newValue = value.accept(visitor); + if(value != newValue) { + return visitor.leaveLiteralNode(new NodeLiteralNode(this, newValue)); + } } - return visitor.leave(this); + return visitor.leaveLiteralNode(this); } return this; @@ -878,14 +894,14 @@ public abstract class LiteralNode extends Node implements PropertyKey { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterLiteralNode(this) != null) { for (int i = 0; i < value.length; i++) { final Node element = value[i]; if (element != null) { value[i] = element.accept(visitor); } } - return visitor.leave(this); + return visitor.leaveLiteralNode(this); } return this; } diff --git a/src/jdk/nashorn/internal/ir/Location.java b/src/jdk/nashorn/internal/ir/Location.java index 16ee680d..c8e01dd3 100644 --- a/src/jdk/nashorn/internal/ir/Location.java +++ b/src/jdk/nashorn/internal/ir/Location.java @@ -65,7 +65,11 @@ public class Location implements Cloneable { @Override protected Object clone() { - return new Location(this); + try { + return super.clone(); + } catch(CloneNotSupportedException e) { + throw new AssertionError(e); + } } @Override diff --git a/src/jdk/nashorn/internal/ir/Node.java b/src/jdk/nashorn/internal/ir/Node.java index 24ad8793..c5f01337 100644 --- a/src/jdk/nashorn/internal/ir/Node.java +++ b/src/jdk/nashorn/internal/ir/Node.java @@ -165,11 +165,18 @@ public abstract class Node extends Location { return true; } - setIsResolved(); + setIsResolved(true); return false; } + /** + * Reset the resolved flag. + */ + public void resetResolved() { + setIsResolved(false); + } + /** * Is this a debug info node like LineNumberNode etc? * @@ -234,8 +241,7 @@ public abstract class Node extends Location { * * @return Deep copy of the Node. */ - @Override - public final Node clone() { + public final Node copy() { return copy(new CopyState()); } @@ -349,10 +355,10 @@ public abstract class Node extends Location { } /** - * Flag this node as resolved, i.e. code has been generated for it + * Flag this node as resolved or not, i.e. code has been generated for it */ - public void setIsResolved() { - this.isResolved = true; + private void setIsResolved(boolean isResolved) { + this.isResolved = isResolved; } /** diff --git a/src/jdk/nashorn/internal/ir/ObjectNode.java b/src/jdk/nashorn/internal/ir/ObjectNode.java index 8b720de4..f6724a62 100644 --- a/src/jdk/nashorn/internal/ir/ObjectNode.java +++ b/src/jdk/nashorn/internal/ir/ObjectNode.java @@ -72,12 +72,12 @@ public class ObjectNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterObjectNode(this) != null) { for (int i = 0, count = elements.size(); i < count; i++) { elements.set(i, elements.get(i).accept(visitor)); } - return visitor.leave(this); + return visitor.leaveObjectNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/PropertyNode.java b/src/jdk/nashorn/internal/ir/PropertyNode.java index 75103377..a6bc49de 100644 --- a/src/jdk/nashorn/internal/ir/PropertyNode.java +++ b/src/jdk/nashorn/internal/ir/PropertyNode.java @@ -88,7 +88,7 @@ public class PropertyNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterPropertyNode(this) != null) { key = (PropertyKey)((Node)key).accept(visitor); if (value != null) { @@ -103,7 +103,7 @@ public class PropertyNode extends Node { setter = setter.accept(visitor); } - return visitor.leave(this); + return visitor.leavePropertyNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/ReferenceNode.java b/src/jdk/nashorn/internal/ir/ReferenceNode.java deleted file mode 100644 index 3cae7b52..00000000 --- a/src/jdk/nashorn/internal/ir/ReferenceNode.java +++ /dev/null @@ -1,91 +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.ir; - -import jdk.nashorn.internal.ir.annotations.Reference; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; -import jdk.nashorn.internal.runtime.Source; - -/** - * IR representation of a reference to another entity (function.) - */ -public class ReferenceNode extends Node { - /** Node referenced. */ - @Reference - private final FunctionNode reference; - - /** - * Constructor - * - * @param source the source - * @param token token - * @param finish finish - * @param reference the function node to reference - */ - public ReferenceNode(final Source source, final long token, final int finish, final FunctionNode reference) { - super(source, token, finish); - - this.reference = reference; - } - - private ReferenceNode(final ReferenceNode referenceNode) { - super(referenceNode); - - this.reference = referenceNode.reference; - } - - @Override - protected Node copy(final CopyState cs) { - return new ReferenceNode(this); - } - - @Override - public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - return visitor.leave(this); - } - - return this; - } - - @Override - public void toString(final StringBuilder sb) { - if (reference == null) { - sb.append("null"); - } else { - reference.toString(sb); - } - } - - /** - * Get there function node reference that this node points tp - * @return a function node reference - */ - public FunctionNode getReference() { - return reference; - } - -} diff --git a/src/jdk/nashorn/internal/ir/ReturnNode.java b/src/jdk/nashorn/internal/ir/ReturnNode.java index 35395b30..1400f395 100644 --- a/src/jdk/nashorn/internal/ir/ReturnNode.java +++ b/src/jdk/nashorn/internal/ir/ReturnNode.java @@ -100,12 +100,12 @@ public class ReturnNode extends Node { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterReturnNode(this) != null) { if (expression != null) { expression = expression.accept(visitor); } - return visitor.leave(this); + return visitor.leaveReturnNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/RuntimeNode.java b/src/jdk/nashorn/internal/ir/RuntimeNode.java index bfc47d18..461007cd 100644 --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java @@ -38,7 +38,7 @@ import jdk.nashorn.internal.runtime.Source; * IR representation for a runtime call. * */ -public class RuntimeNode extends Node implements TypeOverride { +public class RuntimeNode extends Node implements TypeOverride { /** * Request enum used for meta-information about the runtime request @@ -393,8 +393,9 @@ public class RuntimeNode extends Node implements TypeOverride { } @Override - public void setType(final Type type) { + public RuntimeNode setType(final Type type) { this.callSiteType = type; + return this; } @Override @@ -408,12 +409,12 @@ public class RuntimeNode extends Node implements TypeOverride { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterRuntimeNode(this) != null) { for (int i = 0, count = args.size(); i < count; i++) { args.set(i, args.get(i).accept(visitor)); } - return visitor.leave(this); + return visitor.leaveRuntimeNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/SplitNode.java b/src/jdk/nashorn/internal/ir/SplitNode.java index c09a4f02..b751cdcb 100644 --- a/src/jdk/nashorn/internal/ir/SplitNode.java +++ b/src/jdk/nashorn/internal/ir/SplitNode.java @@ -108,10 +108,10 @@ public class SplitNode extends Node { visitor.setCurrentMethodEmitter(getMethodEmitter()); try { - if (visitor.enter(this) != null) { + if (visitor.enterSplitNode(this) != null) { body = body.accept(visitor); - return visitor.leave(this); + return visitor.leaveSplitNode(this); } } finally { visitor.setCurrentCompileUnit(saveCompileUnit); diff --git a/src/jdk/nashorn/internal/ir/SwitchNode.java b/src/jdk/nashorn/internal/ir/SwitchNode.java index 068398ee..23d9c7ea 100644 --- a/src/jdk/nashorn/internal/ir/SwitchNode.java +++ b/src/jdk/nashorn/internal/ir/SwitchNode.java @@ -85,7 +85,7 @@ public class SwitchNode extends BreakableNode { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterSwitchNode(this) != null) { expression = expression.accept(visitor); for (int i = 0, count = cases.size(); i < count; i++) { @@ -94,7 +94,7 @@ public class SwitchNode extends BreakableNode { //the default case is in the cases list and should not be explicitly traversed! - return visitor.leave(this); + return visitor.leaveSwitchNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/Symbol.java b/src/jdk/nashorn/internal/ir/Symbol.java index a58a7c14..603b8b08 100644 --- a/src/jdk/nashorn/internal/ir/Symbol.java +++ b/src/jdk/nashorn/internal/ir/Symbol.java @@ -38,31 +38,31 @@ import jdk.nashorn.internal.runtime.options.Options; */ public final class Symbol implements Comparable { - /** Symbol flags. Kind ordered by precedence. */ - public static final int IS_TEMP = 0b0000_0001; + /** Symbol kinds. Kind ordered by precedence. */ + public static final int IS_TEMP = 1; /** Is this Global */ - public static final int IS_GLOBAL = 0b0000_0010; + public static final int IS_GLOBAL = 2; /** Is this a variable */ - public static final int IS_VAR = 0b0000_0011; + public static final int IS_VAR = 3; /** Is this a parameter */ - public static final int IS_PARAM = 0b0000_0100; + public static final int IS_PARAM = 4; /** Is this a constant */ - public static final int IS_CONSTANT = 0b0000_0101; - - static final int KINDMASK = 0b0000_1111; + public static final int IS_CONSTANT = 5; + /** Mask for kind flags */ + public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits /** Is this scope */ - public static final int IS_SCOPE = 0b0000_0001_0000; + public static final int IS_SCOPE = 1 << 4; /** Is this a this symbol */ - public static final int IS_THIS = 0b0000_0010_0000; + public static final int IS_THIS = 1 << 5; /** Can this symbol ever be undefined */ - public static final int CAN_BE_UNDEFINED = 0b0000_0100_0000; + public static final int CAN_BE_UNDEFINED = 1 << 6; /** Can this symbol ever have primitive types */ - public static final int CAN_BE_PRIMITIVE = 0b0000_1000_0000; + public static final int CAN_BE_PRIMITIVE = 1 << 7; /** Is this a let */ - public static final int IS_LET = 0b0001_0000_0000; + public static final int IS_LET = 1 << 8; /** Is this an internal symbol, never represented explicitly in source code */ - public static final int IS_INTERNAL = 0b0010_0000_0000; + public static final int IS_INTERNAL = 1 << 9; /** Null or name identifying symbol. */ private final String name; @@ -269,15 +269,6 @@ public final class Symbol implements Comparable { return type.isCategory2() ? 2 : 1; } - /** - * Return the defining function (scope.) - * - * @return Defining function. - */ - public FunctionNode findFunction() { - return block != null ? block.getFunction() : null; - } - @Override public boolean equals(final Object other) { if (!(other instanceof Symbol)) { @@ -486,27 +477,6 @@ public final class Symbol implements Comparable { flags |= IS_LET; } - /** - * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load - * - * @param currentFunction function to check for fast scope - * @return true if fast scope - */ - public boolean isFastScope(final FunctionNode currentFunction) { - if (!isScope() || !block.needsScope()) { - return false; - } - // Allow fast scope access if no parent function contains with or eval - FunctionNode func = currentFunction; - while (func != null) { - if (func.hasWith() || func.hasEval()) { - return false; - } - func = func.findParentFunction(); - } - return true; - } - /** * Get the block in which the symbol is defined * @return a block @@ -651,7 +621,7 @@ public final class Symbol implements Comparable { * @return true if this this is a global scope symbol */ public boolean isTopLevel() { - return block instanceof FunctionNode && ((FunctionNode) block).isScript(); + return block instanceof FunctionNode && ((FunctionNode) block).isProgram(); } diff --git a/src/jdk/nashorn/internal/ir/TernaryNode.java b/src/jdk/nashorn/internal/ir/TernaryNode.java index a82991f9..de333851 100644 --- a/src/jdk/nashorn/internal/ir/TernaryNode.java +++ b/src/jdk/nashorn/internal/ir/TernaryNode.java @@ -77,11 +77,11 @@ public class TernaryNode extends BinaryNode { @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - lhs = lhs.accept(visitor); - rhs = rhs.accept(visitor); - third = third.accept(visitor); - return visitor.leave(this); + if (visitor.enterTernaryNode(this) != null) { + final Node newLhs = lhs().accept(visitor); + final Node newRhs = rhs().accept(visitor); + final Node newThird = third.accept(visitor); + return visitor.leaveTernaryNode((TernaryNode)setThird(newThird).setLHS(newLhs).setRHS(newRhs)); } return this; @@ -133,8 +133,12 @@ public class TernaryNode extends BinaryNode { /** * Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z * @param third a node + * @return a node equivalent to this one except for the requested change. */ - public void setThird(final Node third) { - this.third = third; + public TernaryNode setThird(final Node third) { + if(this.third == third) return this; + final TernaryNode n = (TernaryNode)clone(); + n.third = third; + return n; } } diff --git a/src/jdk/nashorn/internal/ir/ThrowNode.java b/src/jdk/nashorn/internal/ir/ThrowNode.java index efa63d96..ab6d59e2 100644 --- a/src/jdk/nashorn/internal/ir/ThrowNode.java +++ b/src/jdk/nashorn/internal/ir/ThrowNode.java @@ -75,9 +75,9 @@ public class ThrowNode extends Node { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterThrowNode(this) != null) { setExpression(expression.accept(visitor)); - return visitor.leave(this); + return visitor.leaveThrowNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/TryNode.java b/src/jdk/nashorn/internal/ir/TryNode.java index b6aa439d..7d3864bc 100644 --- a/src/jdk/nashorn/internal/ir/TryNode.java +++ b/src/jdk/nashorn/internal/ir/TryNode.java @@ -101,7 +101,7 @@ public class TryNode extends Node { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterTryNode(this) != null) { // Need to do first for termination analysis. if (finallyBody != null) { finallyBody = (Block)finallyBody.accept(visitor); @@ -115,7 +115,7 @@ public class TryNode extends Node { } this.catchBlocks = newCatchBlocks; - return visitor.leave(this); + return visitor.leaveTryNode(this); } return this; @@ -154,6 +154,15 @@ public class TryNode extends Node { return catches; } + /** + * Returns true if the specified block is the body of this try block, or any of its catch blocks. + * @param block the block + * @return true if the specified block is the body of this try block, or any of its catch blocks. + */ + public boolean isChildBlock(Block block) { + return body == block || catchBlocks.contains(block); + } + /** * Get the catch blocks for this try block * @return a list of blocks diff --git a/src/jdk/nashorn/internal/ir/TypeOverride.java b/src/jdk/nashorn/internal/ir/TypeOverride.java index 8321318a..61c1fe20 100644 --- a/src/jdk/nashorn/internal/ir/TypeOverride.java +++ b/src/jdk/nashorn/internal/ir/TypeOverride.java @@ -36,15 +36,17 @@ import jdk.nashorn.internal.codegen.types.Type; * by using JSType.toInt32. Especially in scenarios where the field is already stored * as a primitive, this will be much faster than the "object is all I see" scope * available in the method + * @param the type of the node implementing the interface */ -public interface TypeOverride { +public interface TypeOverride { /** * Set the override type * * @param type the type + * @return a node equivalent to this one except for the requested change. */ - public void setType(final Type type); + public T setType(final Type type); /** * Returns true if this node can have a callsite override, e.g. all scope ident nodes diff --git a/src/jdk/nashorn/internal/ir/UnaryNode.java b/src/jdk/nashorn/internal/ir/UnaryNode.java index 491151fa..d823c058 100644 --- a/src/jdk/nashorn/internal/ir/UnaryNode.java +++ b/src/jdk/nashorn/internal/ir/UnaryNode.java @@ -41,7 +41,7 @@ import jdk.nashorn.internal.runtime.Source; */ public class UnaryNode extends Node implements Assignment { /** Right hand side argument. */ - protected Node rhs; + private Node rhs; /** * Constructor @@ -103,6 +103,11 @@ public class UnaryNode extends Node implements Assignment { return isAssignment() ? rhs() : null; } + @Override + public Node setAssignmentDest(Node n) { + return setRHS(n); + } + @Override public Node getAssignmentSource() { return getAssignmentDest(); @@ -132,9 +137,8 @@ public class UnaryNode extends Node implements Assignment { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - rhs = rhs.accept(visitor); - return visitor.leave(this); + if (visitor.enterUnaryNode(this) != null) { + return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor))); } return this; @@ -212,10 +216,12 @@ public class UnaryNode extends Node implements Assignment { * @see BinaryNode * * @param rhs right hand side or expression node + * @return a node equivalent to this one except for the requested change. */ - public void setRHS(final Node rhs) { - this.rhs = rhs; + public UnaryNode setRHS(final Node rhs) { + if(this.rhs == rhs) return this; + final UnaryNode n = (UnaryNode)clone(); + n.rhs = rhs; + return n; } - - } diff --git a/src/jdk/nashorn/internal/ir/VarNode.java b/src/jdk/nashorn/internal/ir/VarNode.java index 07b6f8ec..b719c99d 100644 --- a/src/jdk/nashorn/internal/ir/VarNode.java +++ b/src/jdk/nashorn/internal/ir/VarNode.java @@ -38,8 +38,8 @@ public class VarNode extends Node implements Assignment { /** Initialization expression. */ private Node init; - /** Is this a function var node */ - private boolean isFunctionVarNode; + /** Is this a var statement (as opposed to a "var" in a for loop statement) */ + private final boolean isStatement; /** * Constructor @@ -51,20 +51,34 @@ public class VarNode extends Node implements Assignment { * @param init init node or null if just a declaration */ public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) { + this(source, token, finish, name, init, true); + } + + /** + * Constructor + * + * @param source the source + * @param token token + * @param finish finish + * @param name name of variable + * @param init init node or null if just a declaration + * @param isStatement if this is a var statement (true), or a for-loop initializer (false) + */ + public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, boolean isStatement) { super(source, token, finish); - this.name = name; + this.name = init == null ? name : name.setIsInitializedHere(); this.init = init; - if (init != null) { - this.name.setIsInitializedHere(); - } + this.isStatement = isStatement; } + private VarNode(final VarNode varNode, final CopyState cs) { super(varNode); this.name = (IdentNode)cs.existingOrCopy(varNode.name); this.init = cs.existingOrCopy(varNode.init); + this.isStatement = varNode.isStatement; } @Override @@ -82,6 +96,11 @@ public class VarNode extends Node implements Assignment { return isAssignment() ? name : null; } + @Override + public Node setAssignmentDest(IdentNode n) { + return setName(n); + } + @Override public Node getAssignmentSource() { return isAssignment() ? getInit() : null; @@ -127,16 +146,19 @@ public class VarNode extends Node implements Assignment { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { - name = (IdentNode)name.accept(visitor); - - if (init != null) { - init = init.accept(visitor); + if (visitor.enterVarNode(this) != null) { + final IdentNode newName = (IdentNode)name.accept(visitor); + final Node newInit = init == null ? null : init.accept(visitor); + final VarNode newThis; + if(name != newName || init != newInit) { + newThis = (VarNode)clone(); + newThis.init = newInit; + newThis.name = newInit == null ? newName : newName.setIsInitializedHere(); + } else { + newThis = this; } - - return visitor.leave(this); + return visitor.leaveVarNode(newThis); } - return this; } @@ -162,9 +184,13 @@ public class VarNode extends Node implements Assignment { /** * Reset the initialization expression * @param init new initialization expression + * @return a node equivalent to this one except for the requested change. */ - public void setInit(final Node init) { - this.init = init; + public VarNode setInit(final Node init) { + if(this.init == init) return this; + final VarNode n = (VarNode)clone(); + n.init = init; + return n; } /** @@ -179,30 +205,26 @@ public class VarNode extends Node implements Assignment { * Reset the identifier for this VarNode * @param name new IdentNode representing the variable being set or declared */ - public void setName(final IdentNode name) { - this.name = name; + private VarNode setName(final IdentNode name) { + if(this.name == name) return this; + final VarNode n = (VarNode)clone(); + n.name = name; + return n; } /** - * Check if this is a virtual assignment of a function node. Function nodes declared - * with a name are hoisted to the top of the scope and appear as symbols too. This is - * implemented by representing them as virtual VarNode assignments added to the code - * during lowering - * - * @see FunctionNode - * - * @return true if this is a virtual function declaration + * Returns true if this is a var statement (as opposed to a var initializer in a for loop). + * @return true if this is a var statement (as opposed to a var initializer in a for loop). */ - public boolean isFunctionVarNode() { - return isFunctionVarNode; + public boolean isStatement() { + return isStatement; } /** - * Flag this var node as a virtual function var node assignment as described in - * {@link VarNode#isFunctionVarNode()} + * Returns true if this is a function declaration. + * @return true if this is a function declaration. */ - public void setIsFunctionVarNode() { - this.isFunctionVarNode = true; + public boolean isFunctionDeclaration() { + return init instanceof FunctionNode && ((FunctionNode)init).isDeclared(); } - } diff --git a/src/jdk/nashorn/internal/ir/WhileNode.java b/src/jdk/nashorn/internal/ir/WhileNode.java index c51659b8..8db31c08 100644 --- a/src/jdk/nashorn/internal/ir/WhileNode.java +++ b/src/jdk/nashorn/internal/ir/WhileNode.java @@ -88,11 +88,11 @@ public class WhileNode extends BreakableNode { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterWhileNode(this) != null) { test = test.accept(visitor); body = (Block)body.accept(visitor); - return visitor.leave(this); + return visitor.leaveWhileNode(this); } return this; } diff --git a/src/jdk/nashorn/internal/ir/WithNode.java b/src/jdk/nashorn/internal/ir/WithNode.java index 52f4e9b5..f5ad3b13 100644 --- a/src/jdk/nashorn/internal/ir/WithNode.java +++ b/src/jdk/nashorn/internal/ir/WithNode.java @@ -73,10 +73,10 @@ public class WithNode extends Node { */ @Override public Node accept(final NodeVisitor visitor) { - if (visitor.enter(this) != null) { + if (visitor.enterWithNode(this) != null) { expression = expression.accept(visitor); body = (Block)body.accept(visitor); - return visitor.leave(this); + return visitor.leaveWithNode(this); } return this; diff --git a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java index 68fb68b0..a8c3c4a4 100644 --- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java @@ -112,7 +112,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final AccessNode accessNode) { + public Node enterAccessNode(final AccessNode accessNode) { enterDefault(accessNode); type("MemberExpression"); @@ -132,7 +132,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { enterDefault(block); type("BlockStatement"); @@ -154,7 +154,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final BinaryNode binaryNode) { + public Node enterBinaryNode(final BinaryNode binaryNode) { enterDefault(binaryNode); final String name; @@ -183,7 +183,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final BreakNode breakNode) { + public Node enterBreakNode(final BreakNode breakNode) { enterDefault(breakNode); type("BreakStatement"); @@ -201,7 +201,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final CallNode callNode) { + public Node enterCallNode(final CallNode callNode) { enterDefault(callNode); type("CallExpression"); @@ -217,7 +217,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final CaseNode caseNode) { + public Node enterCaseNode(final CaseNode caseNode) { enterDefault(caseNode); type("SwitchCase"); @@ -238,7 +238,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final CatchNode catchNode) { + public Node enterCatchNode(final CatchNode catchNode) { enterDefault(catchNode); type("CatchClause"); @@ -264,7 +264,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final ContinueNode continueNode) { + public Node enterContinueNode(final ContinueNode continueNode) { enterDefault(continueNode); type("ContinueStatement"); @@ -282,7 +282,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final DoWhileNode doWhileNode) { + public Node enterDoWhileNode(final DoWhileNode doWhileNode) { enterDefault(doWhileNode); type("DoWhileStatement"); @@ -299,7 +299,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final EmptyNode emptyNode) { + public Node enterEmptyNode(final EmptyNode emptyNode) { enterDefault(emptyNode); type("EmptyStatement"); @@ -308,7 +308,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final ExecuteNode executeNode) { + public Node enterExecuteNode(final ExecuteNode executeNode) { enterDefault(executeNode); type("ExpressionStatement"); @@ -321,7 +321,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final ForNode forNode) { + public Node enterForNode(final ForNode forNode) { enterDefault(forNode); if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) { @@ -384,14 +384,14 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { enterDefault(functionNode); - final boolean program = functionNode.isScript(); + final boolean program = functionNode.isProgram(); final String name; if (program) { name = "Program"; - } else if (functionNode.isStatement()) { + } else if (functionNode.isDeclared()) { name = "FunctionDeclaration"; } else { name = "FunctionExpression"; @@ -419,20 +419,11 @@ public final class JSONWriter extends NodeVisitor { } // body consists of nested functions and statements - final List funcs = functionNode.getFunctions(); final List stats = functionNode.getStatements(); - final int size = stats.size() + funcs.size(); + final int size = stats.size(); int idx = 0; arrayStart("body"); - for (final Node func : funcs) { - func.accept(this); - if (idx != (size - 1)) { - comma(); - } - idx++; - } - for (final Node stat : stats) { if (! stat.isDebug()) { stat.accept(this); @@ -448,7 +439,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final IdentNode identNode) { + public Node enterIdentNode(final IdentNode identNode) { enterDefault(identNode); final String name = identNode.getName(); @@ -464,7 +455,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final IfNode ifNode) { + public Node enterIfNode(final IfNode ifNode) { enterDefault(ifNode); type("IfStatement"); @@ -490,7 +481,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final IndexNode indexNode) { + public Node enterIndexNode(final IndexNode indexNode) { enterDefault(indexNode); type("MemberExpression"); @@ -510,7 +501,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final LabelNode labelNode) { + public Node enterLabelNode(final LabelNode labelNode) { enterDefault(labelNode); type("LabeledStatement"); @@ -527,13 +518,13 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final LineNumberNode lineNumberNode) { + public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { return null; } @SuppressWarnings("rawtypes") @Override - public Node enter(final LiteralNode literalNode) { + public Node enterLiteralNode(final LiteralNode literalNode) { enterDefault(literalNode); if (literalNode instanceof LiteralNode.ArrayLiteralNode) { @@ -569,7 +560,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final ObjectNode objectNode) { + public Node enterObjectNode(final ObjectNode objectNode) { enterDefault(objectNode); type("ObjectExpression"); @@ -581,7 +572,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final PropertyNode propertyNode) { + public Node enterPropertyNode(final PropertyNode propertyNode) { final Node key = propertyNode.getKey(); final Node value = propertyNode.getValue(); @@ -647,7 +638,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final ReturnNode returnNode) { + public Node enterReturnNode(final ReturnNode returnNode) { enterDefault(returnNode); type("ReturnStatement"); @@ -665,7 +656,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final RuntimeNode runtimeNode) { + public Node enterRuntimeNode(final RuntimeNode runtimeNode) { final RuntimeNode.Request req = runtimeNode.getRequest(); if (req == RuntimeNode.Request.DEBUGGER) { @@ -680,12 +671,12 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final SplitNode splitNode) { + public Node enterSplitNode(final SplitNode splitNode) { return null; } @Override - public Node enter(final SwitchNode switchNode) { + public Node enterSwitchNode(final SwitchNode switchNode) { enterDefault(switchNode); type("SwitchStatement"); @@ -701,7 +692,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final TernaryNode ternaryNode) { + public Node enterTernaryNode(final TernaryNode ternaryNode) { enterDefault(ternaryNode); type("ConditionalExpression"); @@ -722,7 +713,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final ThrowNode throwNode) { + public Node enterThrowNode(final ThrowNode throwNode) { enterDefault(throwNode); type("ThrowStatement"); @@ -735,7 +726,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final TryNode tryNode) { + public Node enterTryNode(final TryNode tryNode) { enterDefault(tryNode); type("TryStatement"); @@ -760,7 +751,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final UnaryNode unaryNode) { + public Node enterUnaryNode(final UnaryNode unaryNode) { enterDefault(unaryNode); final TokenType tokenType = unaryNode.tokenType(); @@ -816,7 +807,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final VarNode varNode) { + public Node enterVarNode(final VarNode varNode) { enterDefault(varNode); type("VariableDeclaration"); @@ -852,7 +843,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final WhileNode whileNode) { + public Node enterWhileNode(final WhileNode whileNode) { enterDefault(whileNode); type("WhileStatement"); @@ -869,7 +860,7 @@ public final class JSONWriter extends NodeVisitor { } @Override - public Node enter(final WithNode withNode) { + public Node enterWithNode(final WithNode withNode) { enterDefault(withNode); type("WithStatement"); diff --git a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java index 11b8dbd5..d2f40d1a 100644 --- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java +++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java @@ -42,7 +42,6 @@ import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.SplitNode; @@ -138,13 +137,13 @@ public final class PrintVisitor extends NodeVisitor { * Visits. */ @Override - public Node enter(final AccessNode accessNode) { + public Node enterAccessNode(final AccessNode accessNode) { accessNode.toString(sb); return null; } @Override - public Node enter(final Block block) { + public Node enterBlock(final Block block) { sb.append(' '); sb.append('{'); @@ -152,21 +151,6 @@ public final class PrintVisitor extends NodeVisitor { final boolean isFunction = block instanceof FunctionNode; - if (isFunction) { - final FunctionNode function = (FunctionNode)block; - final List functions = function.getFunctions(); - - for (final FunctionNode f : functions) { - sb.append(EOLN); - indent(); - f.accept(this); - } - - if (!functions.isEmpty()) { - sb.append(EOLN); - } - } - final List statements = block.getStatements(); boolean lastLineNumber = false; @@ -224,25 +208,25 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final BreakNode breakNode) { + public Node enterBreakNode(final BreakNode breakNode) { breakNode.toString(sb); return null; } @Override - public Node enter(final CallNode callNode) { + public Node enterCallNode(final CallNode callNode) { callNode.toString(sb); return null; } @Override - public Node enter(final ContinueNode continueNode) { + public Node enterContinueNode(final ContinueNode continueNode) { continueNode.toString(sb); return null; } @Override - public Node enter(final DoWhileNode doWhileNode) { + public Node enterDoWhileNode(final DoWhileNode doWhileNode) { sb.append("do"); doWhileNode.getBody().accept(this); sb.append(' '); @@ -252,7 +236,7 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final ExecuteNode executeNode) { + public Node enterExecuteNode(final ExecuteNode executeNode) { final Node expression = executeNode.getExpression(); if (expression instanceof UnaryNode) { @@ -265,7 +249,7 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final ForNode forNode) { + public Node enterForNode(final ForNode forNode) { forNode.toString(sb); forNode.getBody().accept(this); @@ -273,15 +257,15 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { functionNode.toString(sb); - enter((Block)functionNode); + enterBlock(functionNode); return null; } @Override - public Node enter(final IfNode ifNode) { + public Node enterIfNode(final IfNode ifNode) { ifNode.toString(sb); ifNode.getPass().accept(this); @@ -296,13 +280,13 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final IndexNode indexNode) { + public Node enterIndexNode(final IndexNode indexNode) { indexNode.toString(sb); return null; } @Override - public Node enter(final LabelNode labeledNode) { + public Node enterLabelNode(final LabelNode labeledNode) { indent -= TABWIDTH; indent(); indent += TABWIDTH; @@ -313,7 +297,7 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final LineNumberNode lineNumberNode) { + public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { if (printLineNumbers) { lineNumberNode.toString(sb); } @@ -323,25 +307,19 @@ public final class PrintVisitor extends NodeVisitor { @Override - public Node enter(final ReferenceNode referenceNode) { - referenceNode.toString(sb); - return null; - } - - @Override - public Node enter(final ReturnNode returnNode) { + public Node enterReturnNode(final ReturnNode returnNode) { returnNode.toString(sb); return null; } @Override - public Node enter(final RuntimeNode runtimeNode) { + public Node enterRuntimeNode(final RuntimeNode runtimeNode) { runtimeNode.toString(sb); return null; } @Override - public Node enter(final SplitNode splitNode) { + public Node enterSplitNode(final SplitNode splitNode) { splitNode.toString(sb); sb.append(EOLN); indent += TABWIDTH; @@ -350,7 +328,7 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node leave(final SplitNode splitNode) { + public Node leaveSplitNode(final SplitNode splitNode) { sb.append(""); sb.append(EOLN); indent -= TABWIDTH; @@ -359,7 +337,7 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final SwitchNode switchNode) { + public Node enterSwitchNode(final SwitchNode switchNode) { switchNode.toString(sb); sb.append(" {"); @@ -383,13 +361,13 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final ThrowNode throwNode) { + public Node enterThrowNode(final ThrowNode throwNode) { throwNode.toString(sb); return null; } @Override - public Node enter(final TryNode tryNode) { + public Node enterTryNode(final TryNode tryNode) { tryNode.toString(sb); tryNode.getBody().accept(this); @@ -412,13 +390,19 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final VarNode varNode) { - varNode.toString(sb); + public Node enterVarNode(final VarNode varNode) { + sb.append("var "); + varNode.getName().toString(sb); + final Node init = varNode.getInit(); + if(init != null) { + sb.append(" = "); + init.accept(this); + } return null; } @Override - public Node enter(final WhileNode whileNode) { + public Node enterWhileNode(final WhileNode whileNode) { whileNode.toString(sb); whileNode.getBody().accept(this); @@ -426,7 +410,7 @@ public final class PrintVisitor extends NodeVisitor { } @Override - public Node enter(final WithNode withNode) { + public Node enterWithNode(final WithNode withNode) { withNode.toString(sb); withNode.getBody().accept(this); diff --git a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java index 6856282e..0021b7d2 100644 --- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java +++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java @@ -53,7 +53,7 @@ public class NodeOperatorVisitor extends NodeVisitor { } @Override - public final Node enter(final UnaryNode unaryNode) { + public final Node enterUnaryNode(final UnaryNode unaryNode) { switch (unaryNode.tokenType()) { case ADD: return enterADD(unaryNode); @@ -81,12 +81,12 @@ public class NodeOperatorVisitor extends NodeVisitor { case INCPOSTFIX: return enterDECINC(unaryNode); default: - return super.enter(unaryNode); + return super.enterUnaryNode(unaryNode); } } @Override - public final Node leave(final UnaryNode unaryNode) { + public final Node leaveUnaryNode(final UnaryNode unaryNode) { switch (unaryNode.tokenType()) { case ADD: return leaveADD(unaryNode); @@ -114,12 +114,12 @@ public class NodeOperatorVisitor extends NodeVisitor { case INCPOSTFIX: return leaveDECINC(unaryNode); default: - return super.leave(unaryNode); + return super.leaveUnaryNode(unaryNode); } } @Override - public final Node enter(final BinaryNode binaryNode) { + public final Node enterBinaryNode(final BinaryNode binaryNode) { switch (binaryNode.tokenType()) { case ADD: return enterADD(binaryNode); @@ -198,12 +198,12 @@ public class NodeOperatorVisitor extends NodeVisitor { case SUB: return enterSUB(binaryNode); default: - return super.enter(binaryNode); + return super.enterBinaryNode(binaryNode); } } @Override - public final Node leave(final BinaryNode binaryNode) { + public final Node leaveBinaryNode(final BinaryNode binaryNode) { switch (binaryNode.tokenType()) { case ADD: return leaveADD(binaryNode); @@ -282,7 +282,7 @@ public class NodeOperatorVisitor extends NodeVisitor { case SUB: return leaveSUB(binaryNode); default: - return super.leave(binaryNode); + return super.leaveBinaryNode(binaryNode); } } diff --git a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java index 9ce6fd02..f10d8c03 100644 --- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java +++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java @@ -49,7 +49,6 @@ import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.SplitNode; @@ -153,7 +152,7 @@ public abstract class NodeVisitor { * @param accessNode the node * @return processed node, null if traversal should end, null if traversal should end */ - public Node enter(final AccessNode accessNode) { + public Node enterAccessNode(final AccessNode accessNode) { return enterDefault(accessNode); } @@ -163,7 +162,7 @@ public abstract class NodeVisitor { * @param accessNode the node * @return processed node, null if traversal should end */ - public Node leave(final AccessNode accessNode) { + public Node leaveAccessNode(final AccessNode accessNode) { return leaveDefault(accessNode); } @@ -173,7 +172,7 @@ public abstract class NodeVisitor { * @param block the node * @return processed node, null if traversal should end */ - public Node enter(final Block block) { + public Node enterBlock(final Block block) { return enterDefault(block); } @@ -183,7 +182,7 @@ public abstract class NodeVisitor { * @param block the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final Block block) { + public Node leaveBlock(final Block block) { return leaveDefault(block); } @@ -193,7 +192,7 @@ public abstract class NodeVisitor { * @param binaryNode the node * @return processed node */ - public Node enter(final BinaryNode binaryNode) { + public Node enterBinaryNode(final BinaryNode binaryNode) { return enterDefault(binaryNode); } @@ -203,7 +202,7 @@ public abstract class NodeVisitor { * @param binaryNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final BinaryNode binaryNode) { + public Node leaveBinaryNode(final BinaryNode binaryNode) { return leaveDefault(binaryNode); } @@ -213,7 +212,7 @@ public abstract class NodeVisitor { * @param breakNode the node * @return processed node, null if traversal should end */ - public Node enter(final BreakNode breakNode) { + public Node enterBreakNode(final BreakNode breakNode) { return enterDefault(breakNode); } @@ -223,7 +222,7 @@ public abstract class NodeVisitor { * @param breakNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final BreakNode breakNode) { + public Node leaveBreakNode(final BreakNode breakNode) { return leaveDefault(breakNode); } @@ -233,7 +232,7 @@ public abstract class NodeVisitor { * @param callNode the node * @return processed node, null if traversal should end */ - public Node enter(final CallNode callNode) { + public Node enterCallNode(final CallNode callNode) { return enterDefault(callNode); } @@ -243,7 +242,7 @@ public abstract class NodeVisitor { * @param callNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final CallNode callNode) { + public Node leaveCallNode(final CallNode callNode) { return leaveDefault(callNode); } @@ -253,7 +252,7 @@ public abstract class NodeVisitor { * @param caseNode the node * @return processed node, null if traversal should end */ - public Node enter(final CaseNode caseNode) { + public Node enterCaseNode(final CaseNode caseNode) { return enterDefault(caseNode); } @@ -263,7 +262,7 @@ public abstract class NodeVisitor { * @param caseNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final CaseNode caseNode) { + public Node leaveCaseNode(final CaseNode caseNode) { return leaveDefault(caseNode); } @@ -273,7 +272,7 @@ public abstract class NodeVisitor { * @param catchNode the node * @return processed node, null if traversal should end */ - public Node enter(final CatchNode catchNode) { + public Node enterCatchNode(final CatchNode catchNode) { return enterDefault(catchNode); } @@ -283,7 +282,7 @@ public abstract class NodeVisitor { * @param catchNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final CatchNode catchNode) { + public Node leaveCatchNode(final CatchNode catchNode) { return leaveDefault(catchNode); } @@ -293,7 +292,7 @@ public abstract class NodeVisitor { * @param continueNode the node * @return processed node, null if traversal should end */ - public Node enter(final ContinueNode continueNode) { + public Node enterContinueNode(final ContinueNode continueNode) { return enterDefault(continueNode); } @@ -303,7 +302,7 @@ public abstract class NodeVisitor { * @param continueNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final ContinueNode continueNode) { + public Node leaveContinueNode(final ContinueNode continueNode) { return leaveDefault(continueNode); } @@ -313,7 +312,7 @@ public abstract class NodeVisitor { * @param doWhileNode the node * @return processed node */ - public Node enter(final DoWhileNode doWhileNode) { + public Node enterDoWhileNode(final DoWhileNode doWhileNode) { return enterDefault(doWhileNode); } @@ -323,7 +322,7 @@ public abstract class NodeVisitor { * @param doWhileNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final DoWhileNode doWhileNode) { + public Node leaveDoWhileNode(final DoWhileNode doWhileNode) { return leaveDefault(doWhileNode); } @@ -333,7 +332,7 @@ public abstract class NodeVisitor { * @param emptyNode the node * @return processed node */ - public Node enter(final EmptyNode emptyNode) { + public Node enterEmptyNode(final EmptyNode emptyNode) { return enterDefault(emptyNode); } @@ -343,7 +342,7 @@ public abstract class NodeVisitor { * @param emptyNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final EmptyNode emptyNode) { + public Node leaveEmptyNode(final EmptyNode emptyNode) { return leaveDefault(emptyNode); } @@ -353,7 +352,7 @@ public abstract class NodeVisitor { * @param executeNode the node * @return processed node, null if traversal should end */ - public Node enter(final ExecuteNode executeNode) { + public Node enterExecuteNode(final ExecuteNode executeNode) { return enterDefault(executeNode); } @@ -363,7 +362,7 @@ public abstract class NodeVisitor { * @param executeNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final ExecuteNode executeNode) { + public Node leaveExecuteNode(final ExecuteNode executeNode) { return leaveDefault(executeNode); } @@ -373,7 +372,7 @@ public abstract class NodeVisitor { * @param forNode the node * @return processed node, null if traversal should end */ - public Node enter(final ForNode forNode) { + public Node enterForNode(final ForNode forNode) { return enterDefault(forNode); } @@ -383,7 +382,7 @@ public abstract class NodeVisitor { * @param forNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final ForNode forNode) { + public Node leaveForNode(final ForNode forNode) { return leaveDefault(forNode); } @@ -393,7 +392,7 @@ public abstract class NodeVisitor { * @param functionNode the node * @return processed node */ - public Node enter(final FunctionNode functionNode) { + public Node enterFunctionNode(final FunctionNode functionNode) { return enterDefault(functionNode); } @@ -403,7 +402,7 @@ public abstract class NodeVisitor { * @param functionNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final FunctionNode functionNode) { + public Node leaveFunctionNode(final FunctionNode functionNode) { return leaveDefault(functionNode); } @@ -413,7 +412,7 @@ public abstract class NodeVisitor { * @param identNode the node * @return processed node, null if traversal should end */ - public Node enter(final IdentNode identNode) { + public Node enterIdentNode(final IdentNode identNode) { return enterDefault(identNode); } @@ -423,7 +422,7 @@ public abstract class NodeVisitor { * @param identNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final IdentNode identNode) { + public Node leaveIdentNode(final IdentNode identNode) { return leaveDefault(identNode); } @@ -433,7 +432,7 @@ public abstract class NodeVisitor { * @param ifNode the node * @return processed node, null if traversal should end */ - public Node enter(final IfNode ifNode) { + public Node enterIfNode(final IfNode ifNode) { return enterDefault(ifNode); } @@ -443,7 +442,7 @@ public abstract class NodeVisitor { * @param ifNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final IfNode ifNode) { + public Node leaveIfNode(final IfNode ifNode) { return leaveDefault(ifNode); } @@ -453,7 +452,7 @@ public abstract class NodeVisitor { * @param indexNode the node * @return processed node, null if traversal should end */ - public Node enter(final IndexNode indexNode) { + public Node enterIndexNode(final IndexNode indexNode) { return enterDefault(indexNode); } @@ -463,7 +462,7 @@ public abstract class NodeVisitor { * @param indexNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final IndexNode indexNode) { + public Node leaveIndexNode(final IndexNode indexNode) { return leaveDefault(indexNode); } @@ -473,7 +472,7 @@ public abstract class NodeVisitor { * @param labelNode the node * @return processed node, null if traversal should end */ - public Node enter(final LabelNode labelNode) { + public Node enterLabelNode(final LabelNode labelNode) { return enterDefault(labelNode); } @@ -483,7 +482,7 @@ public abstract class NodeVisitor { * @param labelNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final LabelNode labelNode) { + public Node leaveLabelNode(final LabelNode labelNode) { return leaveDefault(labelNode); } @@ -493,7 +492,7 @@ public abstract class NodeVisitor { * @param lineNumberNode the node * @return processed node, null if traversal should end */ - public Node enter(final LineNumberNode lineNumberNode) { + public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { return enterDefault(lineNumberNode); } @@ -503,7 +502,7 @@ public abstract class NodeVisitor { * @param lineNumberNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final LineNumberNode lineNumberNode) { + public Node leaveLineNumberNode(final LineNumberNode lineNumberNode) { return leaveDefault(lineNumberNode); } @@ -513,8 +512,7 @@ public abstract class NodeVisitor { * @param literalNode the node * @return processed node */ - @SuppressWarnings("rawtypes") - public Node enter(final LiteralNode literalNode) { + public Node enterLiteralNode(final LiteralNode literalNode) { return enterDefault(literalNode); } @@ -524,8 +522,7 @@ public abstract class NodeVisitor { * @param literalNode the node * @return processed node, which will replace the original one, or the original node */ - @SuppressWarnings("rawtypes") - public Node leave(final LiteralNode literalNode) { + public Node leaveLiteralNode(final LiteralNode literalNode) { return leaveDefault(literalNode); } @@ -535,7 +532,7 @@ public abstract class NodeVisitor { * @param objectNode the node * @return processed node */ - public Node enter(final ObjectNode objectNode) { + public Node enterObjectNode(final ObjectNode objectNode) { return enterDefault(objectNode); } @@ -545,7 +542,7 @@ public abstract class NodeVisitor { * @param objectNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final ObjectNode objectNode) { + public Node leaveObjectNode(final ObjectNode objectNode) { return leaveDefault(objectNode); } @@ -555,7 +552,7 @@ public abstract class NodeVisitor { * @param propertyNode the node * @return processed node, null if traversal should end */ - public Node enter(final PropertyNode propertyNode) { + public Node enterPropertyNode(final PropertyNode propertyNode) { return enterDefault(propertyNode); } @@ -565,37 +562,17 @@ public abstract class NodeVisitor { * @param propertyNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final PropertyNode propertyNode) { + public Node leavePropertyNode(final PropertyNode propertyNode) { return leaveDefault(propertyNode); } - /** - * Callback for entering a ReferenceNode - * - * @param referenceNode the node - * @return processed node, null if traversal should end - */ - public Node enter(final ReferenceNode referenceNode) { - return enterDefault(referenceNode); - } - - /** - * Callback for leaving a ReferenceNode - * - * @param referenceNode the node - * @return processed node, which will replace the original one, or the original node - */ - public Node leave(final ReferenceNode referenceNode) { - return leaveDefault(referenceNode); - } - /** * Callback for entering a ReturnNode * * @param returnNode the node * @return processed node, null if traversal should end */ - public Node enter(final ReturnNode returnNode) { + public Node enterReturnNode(final ReturnNode returnNode) { return enterDefault(returnNode); } @@ -605,7 +582,7 @@ public abstract class NodeVisitor { * @param returnNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final ReturnNode returnNode) { + public Node leaveReturnNode(final ReturnNode returnNode) { return leaveDefault(returnNode); } @@ -615,7 +592,7 @@ public abstract class NodeVisitor { * @param runtimeNode the node * @return processed node, null if traversal should end */ - public Node enter(final RuntimeNode runtimeNode) { + public Node enterRuntimeNode(final RuntimeNode runtimeNode) { return enterDefault(runtimeNode); } @@ -625,7 +602,7 @@ public abstract class NodeVisitor { * @param runtimeNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final RuntimeNode runtimeNode) { + public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { return leaveDefault(runtimeNode); } @@ -635,7 +612,7 @@ public abstract class NodeVisitor { * @param splitNode the node * @return processed node, null if traversal should end */ - public Node enter(final SplitNode splitNode) { + public Node enterSplitNode(final SplitNode splitNode) { return enterDefault(splitNode); } @@ -645,7 +622,7 @@ public abstract class NodeVisitor { * @param splitNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final SplitNode splitNode) { + public Node leaveSplitNode(final SplitNode splitNode) { return leaveDefault(splitNode); } @@ -655,7 +632,7 @@ public abstract class NodeVisitor { * @param switchNode the node * @return processed node, null if traversal should end */ - public Node enter(final SwitchNode switchNode) { + public Node enterSwitchNode(final SwitchNode switchNode) { return enterDefault(switchNode); } @@ -665,7 +642,7 @@ public abstract class NodeVisitor { * @param switchNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final SwitchNode switchNode) { + public Node leaveSwitchNode(final SwitchNode switchNode) { return leaveDefault(switchNode); } @@ -675,7 +652,7 @@ public abstract class NodeVisitor { * @param ternaryNode the node * @return processed node, null if traversal should end */ - public Node enter(final TernaryNode ternaryNode) { + public Node enterTernaryNode(final TernaryNode ternaryNode) { return enterDefault(ternaryNode); } @@ -685,7 +662,7 @@ public abstract class NodeVisitor { * @param ternaryNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final TernaryNode ternaryNode) { + public Node leaveTernaryNode(final TernaryNode ternaryNode) { return leaveDefault(ternaryNode); } @@ -695,7 +672,7 @@ public abstract class NodeVisitor { * @param throwNode the node * @return processed node, null if traversal should end */ - public Node enter(final ThrowNode throwNode) { + public Node enterThrowNode(final ThrowNode throwNode) { return enterDefault(throwNode); } @@ -705,7 +682,7 @@ public abstract class NodeVisitor { * @param throwNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final ThrowNode throwNode) { + public Node leaveThrowNode(final ThrowNode throwNode) { return leaveDefault(throwNode); } @@ -715,7 +692,7 @@ public abstract class NodeVisitor { * @param tryNode the node * @return processed node, null if traversal should end */ - public Node enter(final TryNode tryNode) { + public Node enterTryNode(final TryNode tryNode) { return enterDefault(tryNode); } @@ -725,7 +702,7 @@ public abstract class NodeVisitor { * @param tryNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final TryNode tryNode) { + public Node leaveTryNode(final TryNode tryNode) { return leaveDefault(tryNode); } @@ -735,7 +712,7 @@ public abstract class NodeVisitor { * @param unaryNode the node * @return processed node, null if traversal should end */ - public Node enter(final UnaryNode unaryNode) { + public Node enterUnaryNode(final UnaryNode unaryNode) { return enterDefault(unaryNode); } @@ -745,7 +722,7 @@ public abstract class NodeVisitor { * @param unaryNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final UnaryNode unaryNode) { + public Node leaveUnaryNode(final UnaryNode unaryNode) { return leaveDefault(unaryNode); } @@ -755,7 +732,7 @@ public abstract class NodeVisitor { * @param varNode the node * @return processed node, null if traversal should end */ - public Node enter(final VarNode varNode) { + public Node enterVarNode(final VarNode varNode) { return enterDefault(varNode); } @@ -765,7 +742,7 @@ public abstract class NodeVisitor { * @param varNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final VarNode varNode) { + public Node leaveVarNode(final VarNode varNode) { return leaveDefault(varNode); } @@ -775,7 +752,7 @@ public abstract class NodeVisitor { * @param whileNode the node * @return processed node, null if traversal should end */ - public Node enter(final WhileNode whileNode) { + public Node enterWhileNode(final WhileNode whileNode) { return enterDefault(whileNode); } @@ -785,7 +762,7 @@ public abstract class NodeVisitor { * @param whileNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final WhileNode whileNode) { + public Node leaveWhileNode(final WhileNode whileNode) { return leaveDefault(whileNode); } @@ -795,7 +772,7 @@ public abstract class NodeVisitor { * @param withNode the node * @return processed node, null if traversal should end */ - public Node enter(final WithNode withNode) { + public Node enterWithNode(final WithNode withNode) { return enterDefault(withNode); } @@ -805,7 +782,7 @@ public abstract class NodeVisitor { * @param withNode the node * @return processed node, which will replace the original one, or the original node */ - public Node leave(final WithNode withNode) { + public Node leaveWithNode(final WithNode withNode) { return leaveDefault(withNode); } diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java index 166fed91..b810043e 100644 --- a/src/jdk/nashorn/internal/parser/Parser.java +++ b/src/jdk/nashorn/internal/parser/Parser.java @@ -56,10 +56,10 @@ import static jdk.nashorn.internal.parser.TokenType.WHILE; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; - import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.Namespace; import jdk.nashorn.internal.ir.AccessNode; @@ -76,17 +76,18 @@ import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ExecuteNode; 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; import jdk.nashorn.internal.ir.LabelNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyKey; import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.ReferenceNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.SwitchNode; @@ -97,7 +98,6 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSErrorType; @@ -117,11 +117,8 @@ public class Parser extends AbstractParser { /** Is scripting mode. */ private final boolean scripting; - /** Current function being parsed. */ - private FunctionNode function; - - /** Current parsing block. */ - private Block block; + private final LexicalContext lexicalContext = new LexicalContext(); + private List functionDeclarations; /** Namespace for function names where not explicitly given */ private final Namespace namespace; @@ -277,7 +274,9 @@ loop: * @return New block. */ private Block newBlock() { - return block = new Block(source, token, Token.descPosition(token), block, function); + final Block block = new Block(source, token, Token.descPosition(token)); + lexicalContext.push(block); + return block; } /** @@ -290,19 +289,23 @@ loop: // Build function name. final StringBuilder sb = new StringBuilder(); - if (block != null) { - block.addParentName(sb); + final FunctionNode parentFunction = getFunction(); + if(parentFunction != null && !parentFunction.isProgram()) { + sb.append(parentFunction.getName()).append('$'); } sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.tag()); final String name = namespace.uniqueName(sb.toString()); - assert function != null || name.equals(RUN_SCRIPT.tag()) : "name = " + name;// must not rename runScript(). + assert parentFunction != null || name.equals(RUN_SCRIPT.tag()) : "name = " + name;// must not rename runScript(). // Start new block. - final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, block, ident, name); - block = function = functionBlock; - function.setStrictMode(isStrictMode); - function.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED); + final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, ident, name); + if(parentFunction == null) { + functionBlock.setProgram(); + } + functionBlock.setStrictMode(isStrictMode); + functionBlock.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED); + lexicalContext.push(functionBlock); return functionBlock; } @@ -310,9 +313,8 @@ loop: /** * Restore the current block. */ - private void restoreBlock() { - block = block.getParent(); - function = block.getFunction(); + private void restoreBlock(Block block) { + lexicalContext.pop(block); } /** @@ -322,19 +324,22 @@ loop: private Block getBlock(final boolean needsBraces) { // Set up new block. Captures LBRACE. final Block newBlock = newBlock(); - pushControlNode(newBlock); + try { + pushControlNode(newBlock); - // Block opening brace. - if (needsBraces) { - expect(LBRACE); - } + // Block opening brace. + if (needsBraces) { + expect(LBRACE); + } - try { - // Accumulate block statements. - statementList(); + try { + // Accumulate block statements. + statementList(); + } finally { + popControlNode(); + } } finally { - restoreBlock(); - popControlNode(); + restoreBlock(newBlock); } final int possibleEnd = Token.descPosition(token) + Token.descLength(token); @@ -364,7 +369,7 @@ loop: // Accumulate statements. statement(); } finally { - restoreBlock(); + restoreBlock(newBlock); } return newBlock; @@ -378,7 +383,10 @@ loop: final String name = ident.getName(); if (EVAL.tag().equals(name)) { - function.setHasEval(); + final Iterator it = lexicalContext.getFunctions(); + if(it.hasNext()) { + it.next().setHasEval(it); + } } } @@ -390,7 +398,7 @@ loop: final String name = ident.getName(); if (ARGUMENTS.tag().equals(name)) { - function.setUsesArguments(); + getFunction().setUsesArguments(); } } @@ -481,7 +489,7 @@ loop: * @return null or the found label node. */ private LabelNode findLabel(final IdentNode ident) { - for (final LabelNode labelNode : function.getLabelStack()) { + for (final LabelNode labelNode : getFunction().getLabelStack()) { if (labelNode.getLabel().equals(ident)) { return labelNode; } @@ -495,14 +503,14 @@ loop: * @param labelNode Label to add. */ private void pushLabel(final LabelNode labelNode) { - function.getLabelStack().push(labelNode); + getFunction().getLabelStack().push(labelNode); } /** * Remove a label from the label stack. */ private void popLabel() { - function.getLabelStack().pop(); + getFunction().getLabelStack().pop(); } /** @@ -512,6 +520,7 @@ loop: private void pushControlNode(final Node node) { final boolean isLoop = node instanceof WhileNode; final boolean isBreakable = node instanceof BreakableNode || node instanceof Block; + final FunctionNode function = getFunction(); function.getControlStack().push(node); for (final LabelNode labelNode : function.getLabelStack()) { @@ -530,7 +539,7 @@ loop: */ private void popControlNode() { // Get control stack. - final Stack controlStack = function.getControlStack(); + final Stack controlStack = getFunction().getControlStack(); // Can be empty if missing brace. if (!controlStack.isEmpty()) { @@ -540,7 +549,7 @@ loop: private void popControlNode(final Node node) { // Get control stack. - final Stack controlStack = function.getControlStack(); + final Stack controlStack = getFunction().getControlStack(); // Can be empty if missing brace. if (!controlStack.isEmpty() && controlStack.peek() == node) { @@ -549,7 +558,7 @@ loop: } private boolean isInWithBlock() { - final Stack controlStack = function.getControlStack(); + final Stack controlStack = getFunction().getControlStack(); for (int i = controlStack.size() - 1; i >= 0; i--) { final Node node = controlStack.get(i); @@ -562,7 +571,7 @@ loop: } private T findControl(final Class ctype) { - final Stack controlStack = function.getControlStack(); + final Stack controlStack = getFunction().getControlStack(); for (int i = controlStack.size() - 1; i >= 0; i--) { final Node node = controlStack.get(i); @@ -576,7 +585,7 @@ loop: private List findControls(final Class ctype, final Node to) { final List nodes = new ArrayList<>(); - final Stack controlStack = function.getControlStack(); + final Stack controlStack = getFunction().getControlStack(); for (int i = controlStack.size() - 1; i >= 0; i--) { final Node node = controlStack.get(i); @@ -625,7 +634,10 @@ loop: script.setKind(FunctionNode.Kind.SCRIPT); script.setFirstToken(functionToken); + functionDeclarations = new ArrayList<>(); sourceElements(); + script.prependStatements(functionDeclarations); + functionDeclarations = null; expect(EOF); script.setLastToken(token); script.setFinish(source.getLength() - 1); @@ -704,7 +716,7 @@ loop: // check for directive prologues if (checkDirective) { // skip any debug statement like line number to get actual first line - final Node lastStatement = lastStatement(block.getStatements()); + final Node lastStatement = lastStatement(getBlock().getStatements()); // get directive prologue, if any final String directive = getDirective(lastStatement); @@ -724,6 +736,7 @@ loop: // handle use strict directive if ("use strict".equals(directive)) { isStrictMode = true; + final FunctionNode function = getFunction(); function.setStrictMode(true); // We don't need to check these, if lexical environment is already strict @@ -796,11 +809,11 @@ loop: if (isStrictMode && !topLevel) { error(AbstractParser.message("strict.no.func.here"), token); } - functionExpression(true); + functionExpression(true, topLevel); return; } - block.addStatement(lineNumberNode); + getBlock().addStatement(lineNumberNode); switch (type) { case LBRACE: @@ -886,7 +899,7 @@ loop: // Force block execution. final ExecuteNode executeNode = new ExecuteNode(source, newBlock.getToken(), finish, newBlock); - block.addStatement(executeNode); + getBlock().addStatement(executeNode); } /** @@ -981,13 +994,9 @@ loop: // Allocate var node. final VarNode var = new VarNode(source, varToken, finish, name, init); - if (isStatement) { - function.addDeclaration(var); - } - vars.add(var); // Add to current block. - block.addStatement(var); + getBlock().addStatement(var); if (type != COMMARIGHT) { break; @@ -1000,7 +1009,7 @@ loop: boolean semicolon = type == SEMICOLON; endOfLine(); if (semicolon) { - block.setFinish(finish); + getBlock().setFinish(finish); } } @@ -1017,7 +1026,7 @@ loop: */ private void emptyStatement() { if (env._empty_statements) { - block.addStatement(new EmptyNode(source, token, + getBlock().addStatement(new EmptyNode(source, token, Token.descPosition(token) + Token.descLength(token))); } @@ -1043,7 +1052,7 @@ loop: ExecuteNode executeNode = null; if (expression != null) { executeNode = new ExecuteNode(source, expressionToken, finish, expression); - block.addStatement(executeNode); + getBlock().addStatement(executeNode); } else { expect(null); } @@ -1052,7 +1061,7 @@ loop: if (executeNode != null) { executeNode.setFinish(finish); - block.setFinish(finish); + getBlock().setFinish(finish); } } @@ -1094,7 +1103,7 @@ loop: // Construct and add new if node. final IfNode ifNode = new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail); - block.addStatement(ifNode); + getBlock().addStatement(ifNode); } /** @@ -1143,13 +1152,13 @@ loop: outer.setFinish(body.getFinish()); // Add for to current block. - block.addStatement(forNode); + getBlock().addStatement(forNode); } finally { - restoreBlock(); + restoreBlock(outer); popControlNode(); } - block.addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer)); + getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer)); } /** @@ -1283,7 +1292,7 @@ loop: whileNode.setFinish(statements.getFinish()); // Add WHILE node. - block.addStatement(whileNode); + getBlock().addStatement(whileNode); } finally { popControlNode(); } @@ -1330,7 +1339,7 @@ loop: doWhileNode.setFinish(finish); // Add DO node. - block.addStatement(doWhileNode); + getBlock().addStatement(doWhileNode); } finally { popControlNode(); } @@ -1382,7 +1391,7 @@ loop: final ContinueNode continueNode = new ContinueNode(source, continueToken, finish, labelNode, targetNode, findControl(TryNode.class)); continueNode.setScopeNestingLevel(countControls(WithNode.class, targetNode)); - block.addStatement(continueNode); + getBlock().addStatement(continueNode); } /** @@ -1430,7 +1439,7 @@ loop: final BreakNode breakNode = new BreakNode(source, breakToken, finish, labelNode, targetNode, findControl(TryNode.class)); breakNode.setScopeNestingLevel(countControls(WithNode.class, targetNode)); - block.addStatement(breakNode); + getBlock().addStatement(breakNode); } /** @@ -1443,7 +1452,7 @@ loop: */ private void returnStatement() { // check for return outside function - if (function.getKind() == FunctionNode.Kind.SCRIPT) { + if (getFunction().getKind() == FunctionNode.Kind.SCRIPT) { error(AbstractParser.message("invalid.return")); } @@ -1470,7 +1479,7 @@ loop: // Construct and add RETURN node. final ReturnNode returnNode = new ReturnNode(source, returnToken, finish, expression, findControl(TryNode.class)); - block.addStatement(returnNode); + getBlock().addStatement(returnNode); } /** @@ -1505,7 +1514,7 @@ loop: // Construct and add YIELD node. final ReturnNode yieldNode = new ReturnNode(source, yieldToken, finish, expression, findControl(TryNode.class)); - block.addStatement(yieldNode); + getBlock().addStatement(yieldNode); } /** @@ -1529,7 +1538,10 @@ loop: // Get WITH expression. final WithNode withNode = new WithNode(source, withToken, finish, null, null); - function.setHasWith(); + final Iterator it = lexicalContext.getFunctions(); + if(it.hasNext()) { + it.next().setHasWith(it); + } try { pushControlNode(withNode); @@ -1549,7 +1561,7 @@ loop: popControlNode(withNode); } - block.addStatement(withNode); + getBlock().addStatement(withNode); } /** @@ -1649,7 +1661,7 @@ loop: switchNode.setFinish(finish); - block.addStatement(switchNode); + getBlock().addStatement(switchNode); } finally { popControlNode(); } @@ -1684,7 +1696,7 @@ loop: labelNode.setBody(statements); labelNode.setFinish(finish); - block.addStatement(labelNode); + getBlock().addStatement(labelNode); } finally { // Remove label. popLabel(); @@ -1727,7 +1739,7 @@ loop: // Construct and add THROW node. final ThrowNode throwNode = new ThrowNode(source, throwToken, finish, expression, findControl(TryNode.class)); - block.addStatement(throwNode); + getBlock().addStatement(throwNode); } /** @@ -1793,18 +1805,18 @@ loop: expect(RPAREN); + final Block catchBlock = newBlock(); try { - final Block catchBlock = newBlock(); // Get CATCH body. final Block catchBody = getBlock(true); // Create and add catch. final CatchNode catchNode = new CatchNode(source, catchToken, finish, exception, ifExpression, catchBody); - block.addStatement(catchNode); + getBlock().addStatement(catchNode); catchBlocks.add(catchBlock); } finally { - restoreBlock(); + restoreBlock(catchBlock); } // If unconditional catch then should to be the end. @@ -1840,11 +1852,11 @@ loop: outer.addStatement(tryNode); } finally { popControlNode(tryNode); - restoreBlock(); + restoreBlock(outer); popControlNode(outer); } - block.addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer)); + getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer)); } /** @@ -1864,7 +1876,7 @@ loop: endOfLine(); final RuntimeNode runtimeNode = new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList()); - block.addStatement(runtimeNode); + getBlock().addStatement(runtimeNode); } /** @@ -2244,7 +2256,7 @@ loop: parameters = new ArrayList<>(); functionNode = functionBody(getSetToken, getNameNode, parameters, FunctionNode.Kind.GETTER); propertyNode = new PropertyNode(source, propertyToken, finish, getIdent, null); - propertyNode.setGetter(new ReferenceNode(source, propertyToken, finish, functionNode)); + propertyNode.setGetter(functionNode); return propertyNode; case "set": @@ -2259,7 +2271,7 @@ loop: parameters.add(argIdent); functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER); propertyNode = new PropertyNode(source, propertyToken, finish, setIdent, null); - propertyNode.setSetter(new ReferenceNode(source, propertyToken, finish, functionNode)); + propertyNode.setSetter(functionNode); return propertyNode; default: @@ -2440,7 +2452,7 @@ loop: case FUNCTION: // Get function expression. - lhs = functionExpression(false); + lhs = functionExpression(false, false); break; default: @@ -2545,7 +2557,7 @@ loop: * * @return Expression node. */ - private Node functionExpression(final boolean isStatement) { + private Node functionExpression(final boolean isStatement, final boolean topLevel) { final LineNumberNode lineNumber = lineNumber(); final long functionToken = token; @@ -2580,10 +2592,12 @@ loop: final FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL); - if (isStatement && !isInWithBlock()) { - functionNode.setIsStatement(); + if (isStatement) { + if(topLevel) { + functionNode.setIsDeclared(); + } if(ARGUMENTS.tag().equals(name.getName())) { - functionNode.findParentFunction().setDefinesArguments(); + getFunction().setDefinesArguments(); } } @@ -2591,8 +2605,6 @@ loop: functionNode.setIsAnonymous(); } - final ReferenceNode referenceNode = new ReferenceNode(source, functionToken, finish, functionNode); - final int arity = parameters.size(); final boolean strict = functionNode.isStrictMode(); @@ -2628,17 +2640,18 @@ loop: } if (isStatement) { - final VarNode var = new VarNode(source, functionToken, finish, name, referenceNode); - if (isInWithBlock()) { - function.addDeclaration(var); - // Add to current block. - block.addStatement(var); + final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, true); + if(topLevel) { + functionDeclarations.add(lineNumber); + functionDeclarations.add(varNode); } else { - functionNode.setFunctionVarNode(var, lineNumber); + final Block block = getBlock(); + block.addStatement(lineNumber); + block.addStatement(varNode); } } - return referenceNode; + return functionNode; } /** @@ -2721,7 +2734,14 @@ loop: expect(LBRACE); // Gather the function elements. - sourceElements(); + final List prevFunctionDecls = functionDeclarations; + functionDeclarations = new ArrayList<>(); + try { + sourceElements(); + functionNode.prependStatements(functionDeclarations); + } finally { + functionDeclarations = prevFunctionDecls; + } functionNode.setLastToken(token); expect(RBRACE); @@ -2729,12 +2749,9 @@ loop: } } finally { - restoreBlock(); + restoreBlock(functionNode); } - // Add the body of the function to the current block. - block.addFunction(functionNode); - return functionNode; } @@ -3069,4 +3086,12 @@ loop: public String toString() { return "[JavaScript Parsing]"; } + + private Block getBlock() { + return lexicalContext.getCurrentBlock(); + } + + private FunctionNode getFunction() { + return lexicalContext.getCurrentFunction(); + } } diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java index 0ca2c47a..448df143 100644 --- a/src/jdk/nashorn/internal/runtime/Context.java +++ b/src/jdk/nashorn/internal/runtime/Context.java @@ -27,9 +27,9 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT; import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; +import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; -import static jdk.nashorn.internal.lookup.Lookup.MH; import java.io.File; import java.io.IOException; diff --git a/src/jdk/nashorn/internal/runtime/resources/Messages.properties b/src/jdk/nashorn/internal/runtime/resources/Messages.properties index 4547e444..43a7ff62 100644 --- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties @@ -42,6 +42,8 @@ parser.error.expected.stmt=Expected statement but found {0} parser.error.expected.comma=Expected comma but found {0} parser.error.expected=Expected {0} but found {1} parser.error.invalid.return=Invalid return statement +parser.error.no.func.decl.here=Function declarations can only occur at program or function body level. You should use a function expression here instead. +parser.error.no.func.decl.here.warn=Function declarations should only occur at program or function body level. Function declaration in nested block was converted to a function expression. parser.error.property.redefinition=Property "{0}" already defined parser.error.unexpected.token=Unexpected token: {0} parser.error.many.vars.in.for.in.loop=Only one variable allowed in for..in loop @@ -57,7 +59,7 @@ parser.error.strict.name="{0}" cannot be used as {1} in strict mode parser.error.strict.cant.delete.ident=cannot delete identifier "{0}" in strict mode parser.error.strict.param.redefinition=strict mode function cannot have duplicate parameter name "{0}" parser.error.strict.no.octal=cannot use octal value in strict mode -parser.error.strict.no.func.here=In strict mode, functions can only be declared at top-level or immediately within a function +parser.error.strict.no.func.decl.here=In strict mode, function declarations can only occur at program or function body level. You should use a function expression here instead. type.error.strict.getter.setter.poison=In strict mode, "caller", "callee", and "arguments" properties can not be accessed on functions or the arguments object # not the expected type in a given context diff --git a/test/script/basic/JDK-8006755.js b/test/script/basic/JDK-8006755.js index 3012a4be..09243473 100644 --- a/test/script/basic/JDK-8006755.js +++ b/test/script/basic/JDK-8006755.js @@ -31,7 +31,7 @@ var scope = { x: "hello" }; with (scope) { - function main() { + var main = function() { if (x != "hello") { fail("x != 'hello'"); } diff --git a/test/script/basic/NASHORN-837.js b/test/script/basic/NASHORN-837.js index 0632fb39..ef9ec64d 100644 --- a/test/script/basic/NASHORN-837.js +++ b/test/script/basic/NASHORN-837.js @@ -28,23 +28,13 @@ * @run */ -var failed = false; - try { - try { - throw new TypeError('error'); - } catch (iox) { - function f() { - print(iox.message); - } + throw new TypeError('error'); +} catch (iox) { + var f = function() { + if(iox.message != 'error') { + print("Failure! iox did not throw correct exception"); + } } - f(); -} catch (e) { - failed = (e instanceof ReferenceError); - //iox not defined should be thrown -} - -if (!failed) { - print("Failure! iox did not throw correct exception"); } - +f(); diff --git a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java index adf361f4..3cbe1ed4 100644 --- a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java +++ b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java @@ -44,6 +44,7 @@ public class CompilerTest { private static final boolean VERBOSE = Boolean.valueOf(System.getProperty("compilertest.verbose")); private static final boolean TEST262 = Boolean.valueOf(System.getProperty("compilertest.test262")); private static final String TEST_BASIC_DIR = System.getProperty("test.basic.dir"); + private static final String TEST_NODE_DIR = System.getProperty("test.node.dir"); private static final String TEST262_SUITE_DIR = System.getProperty("test262.suite.dir"); interface TestFilter { @@ -81,21 +82,22 @@ public class CompilerTest { @Test public void compileAllTests() { if (TEST262) { - compileTestSet(TEST262_SUITE_DIR, new TestFilter() { + compileTestSet(new File(TEST262_SUITE_DIR), new TestFilter() { @Override public boolean exclude(final File file, final String content) { return content.indexOf("@negative") != -1; } }); } - compileTestSet(TEST_BASIC_DIR, null); + compileTestSet(new File(TEST_BASIC_DIR), null); + compileTestSet(new File(TEST_NODE_DIR, "node"), null); + compileTestSet(new File(TEST_NODE_DIR, "src"), null); } - private void compileTestSet(final String testSet, final TestFilter filter) { + private void compileTestSet(final File testSetDir, final TestFilter filter) { passed = 0; failed = 0; skipped = 0; - final File testSetDir = new File(testSet); if (! testSetDir.isDirectory()) { log("WARNING: " + testSetDir + " not found or not a directory"); return; @@ -103,7 +105,7 @@ public class CompilerTest { log(testSetDir.getAbsolutePath()); compileJSDirectory(testSetDir, filter); - log(testSet + " compile done!"); + log(testSetDir + " compile done!"); log("compile ok: " + passed); log("compile failed: " + failed); log("compile skipped: " + skipped); -- cgit v1.2.3 From f1d834aff48966fde88026b4852a8049a362ace5 Mon Sep 17 00:00:00 2001 From: lagergren Date: Mon, 25 Mar 2013 12:01:06 +0100 Subject: 8017010: index evaluation to a temporary location for index operator much change temporaries to slots, but never scoped vars Reviewed-by: hannesw, sundar --- src/jdk/nashorn/internal/codegen/Attr.java | 9 ++- .../runtime/regexp/joni/ByteCodeMachine.java | 2 +- .../runtime/regexp/joni/encoding/AsciiTables.java | 2 +- test/script/basic/JDK-8017010.js | 68 ++++++++++++++++++++++ test/script/basic/JDK-8017010.js.EXPECTED | 3 + test/script/basic/NASHORN-258.js | 12 ++++ test/script/basic/NASHORN-258.js.EXPECTED | 1 + 7 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 test/script/basic/JDK-8017010.js create mode 100644 test/script/basic/JDK-8017010.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java index 93805598..9ecf7c89 100644 --- a/src/jdk/nashorn/internal/codegen/Attr.java +++ b/src/jdk/nashorn/internal/codegen/Attr.java @@ -577,7 +577,7 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveIndexNode(final IndexNode indexNode) { - newTemporary(Type.OBJECT, indexNode); //TORO + newTemporary(Type.OBJECT, indexNode); //TODO return indexNode; } @@ -1470,8 +1470,13 @@ final class Attr extends NodeOperatorVisitor { assignmentDest.accept(new NodeVisitor() { @Override public Node leaveIndexNode(final IndexNode indexNode) { + assert indexNode.getSymbol().isTemp(); final Node index = indexNode.getIndex(); - index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant()); + //only temps can be set as needing slots. the others will self resolve + //it is illegal to take a scope var and force it to be a slot, that breaks + if (index.getSymbol().isTemp() && !index.getSymbol().isConstant()) { + index.getSymbol().setNeedsSlot(true); + } return indexNode; } }); diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java b/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java index 8aaabe2d..43cc5188 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java +++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java @@ -1459,4 +1459,4 @@ class ByteCodeMachine extends StackMachine { private int finish() { return bestLen; } -} \ No newline at end of file +} diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/encoding/AsciiTables.java b/src/jdk/nashorn/internal/runtime/regexp/joni/encoding/AsciiTables.java index 77eba2de..3dec4ba2 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/joni/encoding/AsciiTables.java +++ b/src/jdk/nashorn/internal/runtime/regexp/joni/encoding/AsciiTables.java @@ -154,4 +154,4 @@ public class AsciiTables { {0x59, 0x79}, {0x5a, 0x7a} }; -} \ No newline at end of file +} diff --git a/test/script/basic/JDK-8017010.js b/test/script/basic/JDK-8017010.js new file mode 100644 index 00000000..aa6e61a9 --- /dev/null +++ b/test/script/basic/JDK-8017010.js @@ -0,0 +1,68 @@ +/* + * 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. + * + * 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-8010710 - slot/scope problem with temporary expressions + * as array index in self modifying assigns + * + * @test + * @run + */ +function zero() { + return 0; +} + +//try complex self modifying assignment and force slots to temporary value index operators +var a = [1, 2, 3, 4, 5]; +var b = [a, a]; +print(b[zero() + 1][2 + a[0]] += 10); + +//repro for NASHORN-258 that never made it +function AddRoundKey() { + var r=0; + state[r][1] &= 17; +} + +var srcFiles = []; +for(i=0;i<100;i++) { + srcFiles.push('dummy'); +} +var added = ''; + +//this broke the javafx build system. verify it works +function bouncingBall() { + for (j=0; j<100; j++) { + added += srcFiles[j]; + } +} +bouncingBall(); +print(added); + +//this is how they should have done it for speed, that works always, verify this too +function bouncingBall2() { + for (var k=0; k<100; k++) { + added += srcFiles[k]; + } +} +bouncingBall2(); +print(added); diff --git a/test/script/basic/JDK-8017010.js.EXPECTED b/test/script/basic/JDK-8017010.js.EXPECTED new file mode 100644 index 00000000..296c81e5 --- /dev/null +++ b/test/script/basic/JDK-8017010.js.EXPECTED @@ -0,0 +1,3 @@ +14 +dummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummy +dummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummydummy diff --git a/test/script/basic/NASHORN-258.js b/test/script/basic/NASHORN-258.js index 0f4f674b..6d71266a 100644 --- a/test/script/basic/NASHORN-258.js +++ b/test/script/basic/NASHORN-258.js @@ -29,6 +29,16 @@ */ function test3(a) { + for (i = 0; i < a.length ; i++) { + for (j = 0; j < a[i].length ; j++) { + for (k = 0; k < a[i][j].length ; k++) { + a[i][j][k] *= 8; + } + } + } +} + +function test3local(a) { for (var i = 0; i < a.length ; i++) { for (var j = 0; j < a[i].length ; j++) { for (var k = 0; k < a[i][j].length ; k++) { @@ -45,6 +55,8 @@ var array = [ [[1,1,1],[1,1,1],[1,1,1]], test3(array); print(array); +test3local(array); +print(array); function outer() { diff --git a/test/script/basic/NASHORN-258.js.EXPECTED b/test/script/basic/NASHORN-258.js.EXPECTED index 8c95c914..5986c471 100644 --- a/test/script/basic/NASHORN-258.js.EXPECTED +++ b/test/script/basic/NASHORN-258.js.EXPECTED @@ -1,2 +1,3 @@ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 1,1,8,1,1,8,1,1,8,1,1,8,1,1,8,1,1,8,1,1,8,1,1,8,1,1,8 -- cgit v1.2.3 From 33fdf006b2c7384b157894108cfa908fa41cc1c2 Mon Sep 17 00:00:00 2001 From: sundar Date: Mon, 25 Mar 2013 18:20:16 +0530 Subject: 8010709: org on the top level doesn't resolve Reviewed-by: lagergren, hannesw --- src/jdk/nashorn/internal/objects/Global.java | 30 +++++++++++++++++- test/script/basic/JDK-8010709.js | 46 ++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/script/basic/JDK-8010709.js diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java index e0bb9ece..e079248b 100644 --- a/src/jdk/nashorn/internal/objects/Global.java +++ b/src/jdk/nashorn/internal/objects/Global.java @@ -259,13 +259,29 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { @Property(name = "Packages", attributes = Attribute.NOT_ENUMERABLE) public volatile Object packages; + /** Nashorn extension: Java access - global.com */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public volatile Object com; + + /** Nashorn extension: Java access - global.edu */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public volatile Object edu; + /** Nashorn extension: Java access - global.java */ @Property(attributes = Attribute.NOT_ENUMERABLE) public volatile Object java; + /** Nashorn extension: Java access - global.javafx */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public volatile Object javafx; + /** Nashorn extension: Java access - global.javax */ @Property(attributes = Attribute.NOT_ENUMERABLE) - public Object javax; + public volatile Object javax; + + /** Nashorn extension: Java access - global.org */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public volatile Object org; /** Nashorn extension: Java access - global.javaImporter */ @Property(name = "JavaImporter", attributes = Attribute.NOT_ENUMERABLE) @@ -320,8 +336,12 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { private ScriptFunction builtinTypeError; private ScriptFunction builtinURIError; private ScriptObject builtinPackages; + private ScriptObject builtinCom; + private ScriptObject builtinEdu; private ScriptObject builtinJava; + private ScriptObject builtinJavafx; private ScriptObject builtinJavax; + private ScriptObject builtinOrg; private ScriptObject builtinJavaImporter; private ScriptObject builtinJavaApi; private ScriptObject builtinArrayBuffer; @@ -1482,8 +1502,12 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { private void initJavaAccess() { final ScriptObject objectProto = getObjectPrototype(); this.builtinPackages = new NativeJavaPackage("", objectProto); + this.builtinCom = new NativeJavaPackage("com", objectProto); + this.builtinEdu = new NativeJavaPackage("edu", objectProto); this.builtinJava = new NativeJavaPackage("java", objectProto); + this.builtinJavafx = new NativeJavaPackage("javafx", objectProto); this.builtinJavax = new NativeJavaPackage("javax", objectProto); + this.builtinOrg = new NativeJavaPackage("org", objectProto); this.builtinJavaImporter = initConstructor("JavaImporter"); this.builtinJavaApi = initConstructor("Java"); } @@ -1566,8 +1590,12 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { this.function = this.builtinFunction; this.jsadapter = this.builtinJSAdapter; this.json = this.builtinJSON; + this.com = this.builtinCom; + this.edu = this.builtinEdu; this.java = this.builtinJava; + this.javafx = this.builtinJavafx; this.javax = this.builtinJavax; + this.org = this.builtinOrg; this.javaImporter = this.builtinJavaImporter; this.javaApi = this.builtinJavaApi; this.math = this.builtinMath; diff --git a/test/script/basic/JDK-8010709.js b/test/script/basic/JDK-8010709.js new file mode 100644 index 00000000..d2ec87ae --- /dev/null +++ b/test/script/basic/JDK-8010709.js @@ -0,0 +1,46 @@ +/* + * 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. + * + * 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-8010709 org on the top level doesn't resolve + * + * @test + * @run + */ + +function check(pkgName) { + if (typeof this[pkgName] != 'object') { + fail(pkgName + " not defined"); + } + + if (String(this[pkgName]) != '[JavaPackage ' + pkgName + ']') { + fail(pkgName + " is not a JavaPackage"); + } +} + +check("com"); +check("edu"); +check("java"); +check("javafx"); +check("javax"); +check("org"); -- cgit v1.2.3 From ae25391dd2ce3ed676cf015f4e9b0c9c47eb19b6 Mon Sep 17 00:00:00 2001 From: lagergren Date: Tue, 26 Mar 2013 08:42:35 +0100 Subject: 8010706: -Dnashorn.args system property to create command lines to wrapped nashorn.jar:s Reviewed-by: hannesw, sundar --- docs/DEVELOPER_README | 11 +++++++++++ src/jdk/nashorn/internal/runtime/options/Options.java | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/DEVELOPER_README b/docs/DEVELOPER_README index 3bd220b2..d0587ce6 100644 --- a/docs/DEVELOPER_README +++ b/docs/DEVELOPER_README @@ -13,6 +13,17 @@ properties described herein are subject to change without notice. This documentation of the system property flags assume that the default value of the flag is false, unless otherwise specified. +SYSTEM PROPERTY: -Dnashorn.args= + +This property takes as its value a space separated list of Nashorn +command line options that should be passed to Nashorn. This might be useful +in environments where it is hard to tell how a nashorn.jar is launched. + +Example: + +> java -Dnashorn.args="--lazy-complation --log=compiler" large-java-app-with-nashorn.jar +> ant -Dnashorn.args="--log=codegen" antjob + SYSTEM PROPERTY: -Dnashorn.unstable.relink.threshold=x This property controls how many call site misses are allowed before a diff --git a/src/jdk/nashorn/internal/runtime/options/Options.java b/src/jdk/nashorn/internal/runtime/options/Options.java index 0f30b1a1..3e09fa57 100644 --- a/src/jdk/nashorn/internal/runtime/options/Options.java +++ b/src/jdk/nashorn/internal/runtime/options/Options.java @@ -66,6 +66,9 @@ public final class Options { /** The options map of enabled options */ private final TreeMap> options; + /** System property that can be used for command line option propagation */ + private static final String NASHORN_ARGS_PROPERTY = "nashorn.args"; + /** * Constructor * @@ -386,6 +389,14 @@ public final class Options { final LinkedList argList = new LinkedList<>(); Collections.addAll(argList, args); + final String extra = getStringProperty(NASHORN_ARGS_PROPERTY, null); + if (extra != null) { + final StringTokenizer st = new StringTokenizer(extra); + while (st.hasMoreTokens()) { + argList.add(st.nextToken()); + } + } + while (!argList.isEmpty()) { final String arg = argList.remove(0); -- cgit v1.2.3 From d1011f14290fbcee99b6bd75f7226351de30858b Mon Sep 17 00:00:00 2001 From: sundar Date: Tue, 26 Mar 2013 18:26:19 +0530 Subject: 8010720: Linkage problem with java.lang.String.length() Reviewed-by: hannesw, lagergren --- src/jdk/nashorn/internal/objects/NativeString.java | 13 ++++++ .../internal/runtime/linker/PrimitiveLookup.java | 1 - test/script/basic/JDK-8010720.js | 49 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/script/basic/JDK-8010720.js diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java index b2b1cf2b..5f48ad98 100644 --- a/src/jdk/nashorn/internal/objects/NativeString.java +++ b/src/jdk/nashorn/internal/objects/NativeString.java @@ -122,6 +122,19 @@ public final class NativeString extends ScriptObject { return value.length(); } + // This is to support length as method call as well. + @Override + protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { + final String name = desc.getNameToken(2); + + // if str.length(), then let the bean linker handle it + if ("length".equals(name) && "getMethod".equals(operator)) { + return null; + } + + return super.findGetMethod(desc, request, operator); + } + // This is to provide array-like access to string characters without creating a NativeString wrapper. @Override protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { diff --git a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java index 968dba91..eaba64f4 100644 --- a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java +++ b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java @@ -109,7 +109,6 @@ public class PrimitiveLookup { } return new GuardedInvocation(method, guard, link.getSwitchPoint()); } - assert desc.getNameTokenCount() <= 2; // Named operations would hit the return null after findProperty return null; } } diff --git a/test/script/basic/JDK-8010720.js b/test/script/basic/JDK-8010720.js new file mode 100644 index 00000000..1dddf22b --- /dev/null +++ b/test/script/basic/JDK-8010720.js @@ -0,0 +1,49 @@ +/* + * 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. + * + * 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-8010720: Linkage problem with java.lang.String.length() + * + * @test + * @run + */ + +var s = new java.lang.String("nashorn"); + +if (s.length() != 7) { + fail("s.length() does not return expected value"); +} + +if (s.length != 7) { + fail("s.length does not return expected value"); +} + + +if ('hello'.length() != 5) { + fail("'hello'.length() does not return expected value"); +} + +if ('hello'.length != 5) { + fail("'hello'.length does not return expected value"); +} + -- cgit v1.2.3