This commit is contained in:
Lana Steuck 2013-04-01 21:42:31 -07:00
commit f614f5d693
174 changed files with 4334 additions and 4172 deletions

View File

@ -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 $*

View File

@ -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 $*

View File

@ -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 $*

View File

@ -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 $*

View File

@ -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=<string>
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

View File

@ -533,9 +533,8 @@ with (SwingGui) {
<hr>
<a name="jsarrays" id="jsarrays"></a>
<h3>Creating, Converting and Using Java Arrays</h3>
<p>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
<p>
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.</p>
@ -543,7 +542,8 @@ don't have to create Java arrays explicitly.</p>
// <a href="source/javaarray.js">javaarray.js</a>
// 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!";

View File

@ -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!";

View File

@ -124,7 +124,7 @@
<echo message="release=${nashorn.version}" file="${build.classes.dir}/jdk/nashorn/internal/runtime/resources/version.properties" append="true"/>
</target>
<target name="jar" depends="compile, run-nasgen" description="Creates nashorn.jar">
<target name="jar" depends="compile, run-nasgen, generate-cc-template" description="Creates nashorn.jar">
<jar jarfile="${dist.jar}" manifest="${meta.inf.dir}/MANIFEST.MF" index="true" filesetmanifest="merge">
<fileset dir="${build.classes.dir}"/>
<manifest>
@ -191,12 +191,12 @@
<!-- tests that check nashorn internals and internal API -->
<jar jarfile="${nashorn.internal.tests.jar}">
<fileset dir="${build.test.classes.dir}" excludes="**/api/*"/>
<fileset dir="${build.test.classes.dir}" excludes="**/api/**"/>
</jar>
<!-- tests that check nashorn script engine (jsr-223) API -->
<jar jarfile="${nashorn.api.tests.jar}">
<fileset dir="${build.test.classes.dir}" includes="**/api/*"/>
<fileset dir="${build.test.classes.dir}" includes="**/api/**"/>
</jar>
</target>

View File

@ -36,7 +36,12 @@
<equals arg1="${jcov}" arg2="dynamic" trim="true"/>
</condition>
<condition property="cc.generate.template" value="true">
<equals arg1="${cc.dynamic.genereate.template}" arg2="true" trim="true"/>
</condition>
<mkdir dir="${cc.dir}"/>
<mkdir dir="${build.dir}/to_be_instrumented"/>
<!-- info -->
<echo message="jcov=${jcov}"/>
@ -51,25 +56,66 @@
<property name="run.test.cc.jvmargs" value=""/>
</target>
<target name="prepare-to-be-instrumented" depends="compile" description="Prepares to_be_instrumented dir">
<copy todir="${build.dir}/to_be_instrumented">
<fileset dir="${build.classes.dir}">
<include name="**/*.class"/>
<include name="**/*.clazz"/>
</fileset>
</copy>
<move todir="${build.dir}/to_be_instrumented/jdk/nashorn/internal/objects">
<fileset dir="${build.dir}/to_be_instrumented/jdk/nashorn/internal/objects">
<include name="**/*.clazz"/>
</fileset>
<mapper type="glob" from="*.clazz" to="*.class"/>
</move>
</target>
<target name="generate-cc-template" depends="prepare-to-be-instrumented" description="Generates code coverage template for dynamic CC" if="cc.generate.template">
<property name="cc.instrumented.path" location="${build.dir}/to_be_instrumented"/>
<java classname="com.sun.tdk.jcov.TmplGen">
<arg value="-verbose"/>
<arg line="-include ${cc.include}"/>
<arg line="-type all"/>
<arg line="-template ${cc.template}"/>
<arg value="${cc.instrumented.path}"/>
<classpath>
<pathelement location="${jcov.jar}"/>
</classpath>
</java>
<java classname="com.sun.tdk.jcov.RepGen">
<arg value="-verbose"/>
<arg line="-output ${cc.dir}/CC_template_report"/>
<arg value="${cc.template}"/>
<classpath>
<pathelement location="${jcov.jar}"/>
</classpath>
</java>
</target>
<target name="init-cc" depends="init-cc-disabled, init-cc-enabled">
<property name="run.test.cc.jvmargs" value=""/>
</target>
<target name="init-cc-cleanup" if="${cc.enabled}">
<delete dir="${cc.dir}" failonerror="false" />
<delete dir="${build.dir}/to_be_instrumented" failonerror="false" />
</target>
<target name="check-merging-files" depends="init">
<resourcecount property="cc.xmls">
<filelist dir="${cc.dir}" files="*.xml" />
</resourcecount>
<echo message="checking avalibility of ${cc.template}"/>
<condition property="nothing-to-merge" value="true">
<equals arg1="${cc.xmls}" arg2="1" trim="true"/>
<not>
<available file="${cc.template}"/>
</not>
</condition>
<echo message="nothing-to-merge = ${nothing-to-merge}"/>
</target>
<target name="fix-merging-files" depends="check-merging-files" if="${nothing-to-merge}">
<echo message="making pre-merge workaround"/>
<echo message="making pre-merge workaround due to missed template"/>
<move todir="${cc.dir}" includeemptydirs="false">
<fileset dir="${cc.dir}">
<include name="*.xml"/>
@ -81,12 +127,12 @@
<target name="merge-code-coverage" depends="fix-merging-files" unless="${nothing-to-merge}">
<echo message="merging files"/>
<fileset dir="${cc.dir}" id="cc.xmls">
<include name="**/*${jcov}*.xml"/>
<include name="**/*_${jcov}_*.xml"/>
<include name="**/CC_template.xml"/>
</fileset>
<pathconvert pathsep=" " property="cc.all.xmls" refid="cc.xmls"/>
<echo message="merging files - ${cc.all.xmls}" />
<java classname="com.sun.tdk.jcov.Merger">
<arg value="-verbose"/>
<arg value="-output"/>

View File

@ -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.

View File

@ -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
@ -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}

View File

@ -46,7 +46,7 @@ import java.util.regex.Pattern;
* <p>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 '&lt;'. This is used to find out if
* previous parameter is used.
*
* @param s
* @param s string to check
* @return true if '&lt;' is in the string, else false
*/
private static boolean isPreviousArgument(final String s) {

View File

@ -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> T getInterfaceInner(final Object self, final Class<T> 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) {
@ -394,14 +399,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() : "<eval>";
// 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 ("<system-init>".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) {
@ -471,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();

View File

@ -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 static void checkConfigPermission() {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("nashorn.setConfig"));
}
}
private static final List<String> names;
private static final List<String> mimeTypes;
private static final List<String> extensions;

View File

@ -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.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);
}
/**
* 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);
}
}

View File

@ -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);
}
});

View File

@ -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.<br>
*
* Example:
* <pre>
* <code>
* 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
* </code>
* </pre>
* @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.<br>
* Example:
* <pre>
* <code>
* 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
* </code>
* </pre>
*
* @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. <br>
*
* Example:
* <pre>
* <code>
* 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 "<system properties>";
}
});
// 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<br>
*
* Examples:
* <pre>
* <code>
* load('test.js'); // load script file 'test.js'
* load('http://java.sun.com/foo.js'); // load from a URL
* </code>
* </pre>
*
* @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<br>
* Examples:
* <pre>
* <code>
* cat('test.txt'); // show test.txt file contents
* cat('http://java.net'); // show the contents from the URL http://java.net
* </code>
* </pre>
* @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<fieldlen; j++)
len.append(" ");
len.insert(0, java.lang.Long.toString(f.length()));
len.setLength(fieldlen);
// move the spaces to the front
var si = len.toString().indexOf(" ");
if ( si != -1 ) {
var pad = len.toString().substring(si);
len.setLength(si);
len.insert(0, pad);
}
sb.append(len.toString());
sb.append(" ");
sb.append(f.getName());
if (f.isDirectory()) {
sb.append('/');
}
println(sb.toString());
}
/**
* Lists the files in a directory
*
* @param dir directory from which to list the files. optional, default to pwd
* @param filter pattern to filter the files listed. optional, default is '.'.
*/
function ls(dir, filter) {
if (dir) {
dir = pathToFile(dir);
} else {
dir = curDir;
}
if (dir.isDirectory()) {
var files = dir.listFiles();
for (var i in files) {
var f = files[i];
if (filter) {
if(!f.getName().match(filter)) {
continue;
}
}
printFile(f);
}
} else {
printFile(dir);
}
}
/**
* Synonym for 'ls'.
*/
function dir(d, filter) {
ls(d, filter);
}
/**
* Unix-like grep, but accepts JavaScript regex patterns
*
* @param pattern to search in files
* @param files one or more files
*/
function grep(pattern, files /*, one or more files */) {
if (arguments.length < 2) return;
for (var i = 1; i < arguments.length; i++) {
println(arguments[i] + ":");
cat(arguments[i], pattern);
}
}
/**
* Find in files. Calls arbitrary callback function
* for each matching file.<br>
*
* Examples:
* <pre>
* <code>
* find('.')
* find('.', '.*\.class', rm); // remove all .class files
* find('.', '.*\.java'); // print fullpath of each .java file
* find('.', '.*\.java', cat); // print all .java files
* </code>
* </pre>
*
* @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 &amp; 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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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();

View File

@ -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;
@ -79,9 +76,11 @@ 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.LiteralNode.ArrayLiteralNode;
@ -89,7 +88,6 @@ 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;
@ -108,14 +106,14 @@ 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.DebugLogger;
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 +147,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;
@ -161,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<SharedScopeCall, SharedScopeCall> 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.
*
@ -215,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);
@ -226,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<FunctionNode> 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;
@ -245,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<Block> blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
final Block currentBlock = blocks.next();
if (currentBlock == definingBlock) {
return depth;
}
if (currentBlock.needsScope()) {
++depth;
}
}
if (currentBlock instanceof FunctionNode) {
for (final Block lookupBlock : ((FunctionNode)currentBlock).getReferencingParentBlocks()) {
final int result = getScopeProtoDepth(lookupBlock, symbol);
if (result != -1) {
return delta + result;
}
}
}
return -1;
}
@ -318,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);
}
@ -334,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());
@ -343,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.
@ -355,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;
}
@ -427,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);
@ -439,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;
}
@ -472,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;
}
@ -520,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<Node> args = callNode.getArgs();
final Node function = callNode.getFunction();
final FunctionNode currentFunction = getCurrentFunctionNode();
final Block currentBlock = getCurrentBlock();
function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
@ -536,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 {
@ -598,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()) {
@ -611,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 {
@ -626,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();
@ -639,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();
@ -658,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();
@ -699,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;
}
@ -714,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;
}
@ -736,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;
}
@ -818,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;
/*
@ -920,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);
}
@ -963,31 +983,42 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
public Node enter(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
return null;
}
public Node enterFunctionNode(final FunctionNode functionNode) {
final boolean isCallee = functionNodeIsCallee;
functionNodeIsCallee = false;
if (functionNode.testResolved()) {
return null;
}
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();
method.label(functionNode.getEntryLabel());
initLocals(functionNode);
functionNode.setState(CompilationState.EMITTED);
return functionNode;
}
@Override
public Node leave(final FunctionNode functionNode) {
public Node leaveFunctionNode(final FunctionNode functionNode) {
// Mark end for variable tables.
method.label(functionNode.getBreakLabel());
@ -1005,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;
}
@ -1053,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;
}
@ -1064,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;
}
@ -1072,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;
}
@ -1110,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();
@ -1216,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);
}
@ -1296,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;
}
@ -1376,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;
@ -1408,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;
}
@ -1560,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;
}
@ -1641,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;
}
@ -1710,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();
@ -1767,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;
}
@ -1899,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;
}
@ -1926,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;
}
@ -1959,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();
@ -1970,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;
}
@ -2016,7 +2038,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}
leave(catchBlock);
leaveBlock(catchBlock);
} finally {
setCurrentBlock(saveBlock);
}
@ -2031,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) {
@ -2053,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);
@ -2069,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;
}
@ -2102,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;
}
@ -2868,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;
}
@ -3064,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++;
@ -3087,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();
@ -3159,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
@ -3178,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());
}
@ -3186,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());
@ -3203,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;
}
@ -3234,42 +3254,22 @@ 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();
new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), 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();
m.loadScope();
} else {
method.loadNull();
m.loadNull();
}
method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
final List<Class<?>> cparamList = new ArrayList<>();
if (isLazy) {
cparamList.add(CodeInstaller.class);
cparamList.add(FunctionNode.class);
} else {
cparamList.add(MethodHandle.class);
}
cparamList.addAll(Arrays.asList(cparams));
method.invoke(constructorNoLookup(className, cparamList.toArray(new Class<?>[cparamList.size()])));
m.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class));
}
}.makeObject(method);
}

View File

@ -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;
@ -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;
@ -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) {
@ -65,23 +65,25 @@ 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;
}
assert Compiler.LAZY_JIT;
assert compiler.isLazy();
lazy.add(node);
//also needs scope, potentially needs arguments etc etc
return node;
}
});
@ -92,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
@ -113,7 +124,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 +145,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 +161,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 +180,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 +214,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 +241,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();
@ -238,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) {
@ -306,18 +328,12 @@ enum CompilationPhase {
};
private final EnumSet<CompilationState> pre;
private final CompilationState post;
private long startTime;
private long endTime;
private boolean isFinished;
private CompilationPhase(final EnumSet<CompilationState> pre) {
this(pre, null);
}
private CompilationPhase(final EnumSet<CompilationState> pre, final CompilationState post) {
this.pre = pre;
this.post = post;
this.pre = pre;
}
boolean isApplicable(final FunctionNode functionNode) {
@ -343,10 +359,6 @@ enum CompilationPhase {
endTime = System.currentTimeMillis();
Timing.accumulateTime(toString(), endTime - startTime);
if (post != null) {
functionNode.setState(post);
}
isFinished = true;
}

View File

@ -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

View File

@ -45,11 +45,14 @@ 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 +74,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<String, byte[]> bytecode;
private final Set<CompileUnit> compileUnits;
@ -164,7 +165,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 +174,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);
final static CompilationSequence SEQUENCE_DEFAULT =
LAZY_JIT ?
SEQUENCE_LAZY :
SEQUENCE_NORMAL;
private static CompilationSequence sequence(final boolean lazy) {
return lazy ? SEQUENCE_LAZY : SEQUENCE_EAGER;
}
boolean isLazy() {
return sequence == SEQUENCE_LAZY;
}
private static String lazyTag(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
@ -212,11 +216,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() + "'");
if (functionNode.isLazy()) {
LOG.info(">>> This is a lazy recompilation triggered by a trampoline");
}
}
/**
@ -227,7 +226,7 @@ public final class Compiler {
* @param strict should this compilation use strict mode semantics
*/
public Compiler(final CodeInstaller<ScriptEnvironment> 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 +236,7 @@ public final class Compiler {
* @param functionNode function node (in any available {@link CompilationState}) to compile
*/
public Compiler(final CodeInstaller<ScriptEnvironment> 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 +246,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 void compile() throws CompilationException {
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 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<Void>() {
@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,46 +355,68 @@ public final class Compiler {
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
Class<?> rootClass = null;
final Map<String, Class<?>> installedClasses = new HashMap<>();
final String rootClassName = firstCompileUnitName();
final byte[] rootByteCode = bytecode.get(rootClassName);
final Class<?> rootClass = install(rootClassName, rootByteCode);
int length = rootByteCode.length;
installedClasses.put(rootClassName, rootClass);
for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
final String className = entry.getKey();
LOG.fine("Installing class " + className);
final byte[] code = entry.getValue();
final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
if (rootClass == null && firstCompileUnitName().equals(className)) {
rootClass = clazz;
final String className = entry.getKey();
if (className.equals(rootClassName)) {
continue;
}
final byte[] code = entry.getValue();
length += code.length;
try {
final Source source = getSource();
final Object[] constants = getConstantData().toArray();
// Need doPrivileged because these fields are private
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
//use reflection to write source and constants table to installed classes
final Field sourceField = clazz.getDeclaredField(SOURCE.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);
}
installedClasses.put(className, install(className, code));
}
for (final CompileUnit unit : compileUnits) {
unit.setCode(installedClasses.get(unit.getUnitClassName()));
}
functionNode.accept(new NodeVisitor() {
@Override
public Node enterFunctionNode(final FunctionNode node) {
if (node.isLazy()) {
return null;
}
node.setState(CompilationState.INSTALLED);
return node;
}
});
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;
}
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);
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;
@ -444,8 +541,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 +555,5 @@ public final class Compiler {
assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
}
}

View File

@ -40,13 +40,14 @@ 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;
@ -84,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));
@ -96,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
@ -125,8 +127,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
specialize(unaryNode);
return unaryNode;
return specialize(unaryNode).node;
}
@Override
@ -158,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
@ -170,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
@ -235,40 +235,40 @@ 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);
}
@Override
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
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
@ -344,7 +344,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);
}
@ -354,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));
@ -369,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();
@ -413,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.
@ -432,18 +440,26 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
updateSymbols(functionNode);
functionNode.setState(CompilationState.FINALIZED);
return functionNode;
}
@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();
@ -461,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()));
@ -470,7 +486,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
@Override
public Node leave(final RuntimeNode runtimeNode) {
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
final List<Node> args = runtimeNode.getArgs();
for (final Node arg : args) {
assert !arg.getType().isUnknown();
@ -479,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<CaseNode> cases = switchNode.getCases();
final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
@ -498,34 +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));
@ -534,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;
}
@ -553,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<Symbol> symbols = block.getFrame().getSymbols();
final boolean allVarsInScope = functionNode.allVarsInScope();
final boolean isVarArg = functionNode.isVarArg();
@ -629,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));
}
/**
@ -654,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));
}
/**
@ -677,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());
}
@ -685,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 <T extends Node> SpecializedNode specialize(final Assignment<T> 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;
@ -716,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);
}
@ -734,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();
}
/**
@ -760,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 extends Node> 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);
@ -769,7 +791,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
}
LOG.info("Type override for lhs in '" + node + "' => " + to);
((TypeOverride)node).setType(to);
return ((TypeOverride<T>)node).setType(to);
}
/**
@ -814,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);
}

View File

@ -30,6 +30,8 @@ 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.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
@ -52,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);
@ -62,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);
@ -72,7 +74,21 @@ final class FoldConstants extends NodeVisitor {
}
@Override
public Node leave(final IfNode ifNode) {
public Node enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
return null;
}
return functionNode;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
functionNode.setState(CompilationState.CONSTANT_FOLDED);
return functionNode;
}
@Override
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();
@ -85,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();

View File

@ -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
*/
@ -155,7 +155,7 @@ public final class FunctionSignature {
true,
functionNode.needsCallee(),
functionNode.getReturnType(),
(functionNode.isVarArg() && !functionNode.isScript()) ?
(functionNode.isVarArg() && !functionNode.isProgram()) ?
null :
functionNode.getParameters());
}
@ -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++) {

View File

@ -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;
@ -102,6 +103,8 @@ final class Lower extends NodeOperatorVisitor {
private List<Node> statements;
private LexicalContext lexicalContext = new LexicalContext();
/**
* Constructor.
*
@ -113,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<Node> 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
@ -132,7 +136,7 @@ final class Lower extends NodeOperatorVisitor {
*/
if (lastStatement != null && lastStatement.isTerminal()) {
copyTerminal(block, lastStatement);
break;
visitor = new DeadCodeVarDeclarationVisitor();
}
}
block.setStatements(statements);
@ -140,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
@ -159,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(),
@ -212,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();
@ -236,6 +241,7 @@ final class Lower extends NodeOperatorVisitor {
if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
forNode.setTest(null);
setHasGoto(forNode);
setTerminal(forNode, !escapes);
}
@ -245,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);
/*
@ -270,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;
@ -331,17 +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();
@ -356,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);
@ -365,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();
@ -409,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<CaseNode> cases = switchNode.getCases();
@ -442,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();
@ -534,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<Node>(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);
@ -580,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());
@ -604,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) {
@ -636,7 +663,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);
}
}
@ -649,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);
}
@ -678,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;
}
@ -742,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());
@ -769,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);
@ -790,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) {
@ -855,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) {
for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
final Block block = blocks.next();
if(block == target) {
return false;
}
if(tryNode.isChildBlock(block)) {
return true;
}
for (final Block catchBlock : tryNode.getCatchBlocks()) {
if (catchBlock == current) {
return true;
}
}
}
return false;
}
@ -895,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);
@ -970,6 +976,3 @@ final class Lower extends NodeOperatorVisitor {
}
}

View File

@ -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;
}

View File

@ -39,7 +39,9 @@ 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.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
@ -48,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;
@ -69,6 +72,8 @@ final class Splitter extends NodeVisitor {
/** Cache for calculated block weights. */
private final Map<Node, Long> 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);
@ -92,15 +97,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);
@ -110,7 +116,7 @@ final class Splitter extends NodeVisitor {
}
if (weight >= SPLIT_THRESHOLD) {
weight = splitBlock(functionNode);
weight = splitBlock(functionNode, functionNode);
}
if (functionNode.isSplit()) {
@ -130,9 +136,22 @@ 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);
}
/**
@ -151,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<Node> splits = new ArrayList<>();
@ -163,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;
}
@ -179,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);
@ -195,13 +214,13 @@ final class Splitter extends NodeVisitor {
*
* @return New split node.
*/
private SplitNode createBlockSplitNode(final Block parent, final List<Node> statements, final long weight) {
private SplitNode createBlockSplitNode(final Block parent, final FunctionNode function, final List<Node> 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);
@ -213,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;
}
@ -229,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) {
@ -290,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<Node> statements = node.getStatements();
for (final Node statement : statements) {
statement.accept(this);
}
return null;
}
@ -317,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);
}
@ -356,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;

View File

@ -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<Node, Long> weightCache;
/*
private final FunctionNode topFunction;
/**
* Constructor
*
* @param weightCache cache of already calculated block weights
*/
private WeighNodes(final Map<Node, Long> weightCache) {
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> 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<Node, Long> 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<Node> 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;
}

View File

@ -647,21 +647,20 @@ public abstract class Type implements Comparable<Type>, 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);
}
}

View File

@ -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<AccessNode> {
/** 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

View File

@ -46,4 +46,11 @@ public interface Assignment<D extends Node> {
* @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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -25,41 +25,24 @@
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;
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.Ignore;
import jdk.nashorn.internal.ir.annotations.ParentNode;
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
private Block parent;
/** Owning function. */
@Ignore //don't print it, it is apparent in the tree
protected FunctionNode function;
/** List of statements */
protected List<Node> statements;
@ -84,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");
@ -107,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));
@ -123,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);
}
/**
@ -189,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<Node> newStatements = new ArrayList<>();
newStatements.add(statement);
newStatements.addAll(statements);
setStatements(newStatements);
}
public void prependStatements(final List<Node> prepended) {
statements.addAll(0, prepended);
}
/**
@ -211,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<FunctionNode> 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<FunctionNode> functionNodes) {
assert parent != null : "Parent context missing.";
parent.setFunctions(functionNodes);
}
/**
* Assist in IR navigation.
*
@ -258,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);
@ -274,51 +161,21 @@ public class Block extends Node {
}
/**
* Search for symbol.
*
* @param name Symbol name.
*
* @return Found symbol or null if not found.
* Get an iterator for all the symbols defined in this block
* @return symbol iterator
*/
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;
public Iterator<Symbol> symbolIterator() {
return symbols.values().iterator();
}
/**
* Search for symbol in current function.
*
* @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 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);
}
/**
@ -331,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) {
@ -504,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
*
@ -523,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
*
@ -550,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<Node> stmts = statements.listIterator(); stmts.hasNext();) {
stmts.set(stmts.next().accept(visitor));
}
}
/**
* Reset the statement list for this block
*
@ -585,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<Block> 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<Block> ancestors) {
if(ancestors.hasNext()) {
ancestors.next().setUsesScopeSymbol(symbol, ancestors);
}
}
}

View File

@ -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;

View File

@ -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<CallNode> {
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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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.LinkedList;
import java.util.HashMap;
import java.util.Iterator;
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,16 +47,18 @@ 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.ScriptFunction;
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 {
private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
/** Function kinds */
public enum Kind {
/** a normal function - nothing special */
@ -86,7 +90,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. */
@ -108,9 +114,6 @@ public class FunctionNode extends Block {
/** List of parameters. */
private List<IdentNode> parameters;
/** List of nested functions. */
private List<FunctionNode> functions;
/** First token of function. **/
private long firstToken;
@ -153,10 +156,6 @@ public class FunctionNode extends Block {
/** Pending control list. */
private final Stack<Node> controlStack;
/** Variable declarations in the function's scope */
@Ignore
private final List<VarNode> declarations;
/** VarNode for this function statement */
@Ignore //this is explicit code anyway and should not be traversed after lower
private VarNode funcVarNode;
@ -173,37 +172,42 @@ public class FunctionNode extends Block {
@Ignore
private final EnumSet<CompilationState> compilationState;
/** Type hints, e.g based on parameters at call site */
private final Map<IdentNode, Type> specializedTypes;
/** Function flags. */
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 = 0b0010_0000_0000_0000;
private static final int HAS_LAZY_CHILDREN = 1 << 13;
/** Does this function have lazy, yet uncompiled children */
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;
@ -211,20 +215,13 @@ 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;
/**
* 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<Block> referencingParentBlocks;
/**
* Constructor
*
@ -232,33 +229,25 @@ 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);
@ -268,10 +257,9 @@ public class FunctionNode extends Block {
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();
@ -283,43 +271,34 @@ 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<>();
}
@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);
}
@ -328,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();
}
/**
@ -530,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;
}
/**
@ -575,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<FunctionNode> 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<FunctionNode> 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<FunctionNode> ancestors) {
if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
flags |= HAS_DESCENDANT_WITH_OR_EVAL;
markParentForWithOrEval();
markParentForWithOrEval(ancestors);
}
}
@ -614,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<FunctionNode> ancestors) {
if(!hasEval()) {
this.flags |= HAS_EVAL;
markParentForWithOrEval();
markParentForWithOrEval(ancestors);
}
}
@ -651,11 +611,34 @@ public class FunctionNode extends Block {
}
/**
* Get all nested functions
* @return list of nested functions in this function
* 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<FunctionNode> getFunctions() {
return Collections.unmodifiableList(functions);
public List<FunctionNode> 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<FunctionNode> 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;
}
/**
@ -710,6 +693,7 @@ 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.
*
* @return true if the function's generated Java method needs a {@code callee} parameter.
*/
public boolean needsCallee() {
@ -786,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();
}
/**
@ -805,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();
}
/**
@ -865,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;
}
/**
@ -918,6 +902,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
@ -953,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;
}
/**
@ -1013,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<Block> ancestors) {
setUsesAncestorScope();
super.setUsesParentScopeSymbol(symbol, ancestors);
}
/**
@ -1116,7 +1102,7 @@ public class FunctionNode extends Block {
@Override
public Type getType() {
return getReturnType();
return FUNCTION_TYPE;
}
/**
@ -1175,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<FunctionNode> functionNodes) {
functions.addAll(functionNodes);
}
/**
* Set a function list
*
* @param functionNodes to set
*/
@Override
public void setFunctions(final List<FunctionNode> 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<VarNode> getDeclarations() {
return Collections.unmodifiableList(declarations);
}
/**
* Get the compile unit used to compile this function
* @see Compiler
@ -1258,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(); // 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<Block> getReferencingParentBlocks() {
if (referencingParentBlocks == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(referencingParentBlocks);
}
}

View File

@ -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<IdentNode>, 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 <code>var lhs = rhs;</code>. */
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;
}
}

View File

@ -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;

View File

@ -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<IndexNode> {
/** 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

View File

@ -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;

View File

@ -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<Block> 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<Block> 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<FunctionNode> getFunctions() {
return new FunctionIterator(getBlocks());
}
private static final class FunctionIterator implements Iterator<FunctionNode> {
private final Iterator<Block> it;
private FunctionNode next;
FunctionIterator(Iterator<Block> 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<Block> getAncestorBlocks(Block block) {
final Iterator<Block> 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<Block> getBlocks(final Block block) {
final Iterator<Block> it = getAncestorBlocks(block);
return new Iterator<Block>() {
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<FunctionNode> 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<Block> 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());
}
}

View File

@ -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;

View File

@ -46,7 +46,7 @@ import jdk.nashorn.internal.runtime.Undefined;
*/
public abstract class LiteralNode<T> extends Node implements PropertyKey {
/** Literal value */
protected T value;
protected final T value;
/**
* Constructor
@ -67,8 +67,17 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @param literalNode source node
*/
protected LiteralNode(final LiteralNode<T> 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<T> literalNode, final T newValue) {
super(literalNode);
this.value = literalNode.value;
this.value = newValue;
}
@Override
@ -217,8 +226,8 @@ public abstract class LiteralNode<T> 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<T> extends Node implements PropertyKey {
super(literalNode);
}
private NodeLiteralNode(final LiteralNode<Node> 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<T> 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<T> 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;
}

View File

@ -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

View File

@ -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;
}
/**

View File

@ -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<Node> 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<Node> elements) {
public ObjectNode(final Source source, final long token, final int finish, final List<Node> 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;
}
@ -79,16 +72,12 @@ 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);
}
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;
@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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<RuntimeNode> {
/**
* 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;

View File

@ -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);

View File

@ -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;

View File

@ -38,31 +38,31 @@ import jdk.nashorn.internal.runtime.options.Options;
*/
public final class Symbol implements Comparable<Symbol> {
/** 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<Symbol> {
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<Symbol> {
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<Symbol> {
* @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();
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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

View File

@ -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 <T> the type of the node implementing the interface
*/
public interface TypeOverride {
public interface TypeOverride<T extends Node> {
/**
* 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

View File

@ -41,7 +41,7 @@ import jdk.nashorn.internal.runtime.Source;
*/
public class UnaryNode extends Node implements Assignment<Node> {
/** Right hand side argument. */
protected Node rhs;
private Node rhs;
/**
* Constructor
@ -103,6 +103,11 @@ public class UnaryNode extends Node implements Assignment<Node> {
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<Node> {
*/
@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;
@ -152,9 +156,9 @@ public class UnaryNode extends Node implements Assignment<Node> {
if (isConvert) {
convertPos = sb.length();
sb.append("((");
sb.append("(");
sb.append(getType());
sb.append(")");
sb.append(")(");
}
if (!isPostfix && !isConvert) {
@ -191,8 +195,6 @@ public class UnaryNode extends Node implements Assignment<Node> {
sb.setCharAt(convertPos, ' ');
}
}
//TODO - conversions still have too many parenthesis - makes --print-lower-parse hard to read
}
/**
@ -214,10 +216,12 @@ public class UnaryNode extends Node implements Assignment<Node> {
* @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;
}
}

View File

@ -38,8 +38,8 @@ public class VarNode extends Node implements Assignment<IdentNode> {
/** 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<IdentNode> {
* @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<IdentNode> {
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<IdentNode> {
*/
@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<IdentNode> {
/**
* 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<IdentNode> {
* 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();
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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
* <p>
* TODO this could probably be automated using the @Reference annotation.
*/
@Retention(value=RetentionPolicy.RUNTIME)

View File

@ -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<Node> 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<Node> 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<Field> 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<Node>)value) {
printAST(sb, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
printAST(sb, preorder, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
}
}
}

View File

@ -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<FunctionNode> funcs = functionNode.getFunctions();
final List<Node> 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");

View File

@ -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<FunctionNode> functions = function.getFunctions();
for (final FunctionNode f : functions) {
sb.append(EOLN);
indent();
f.accept(this);
}
if (!functions.isEmpty()) {
sb.append(EOLN);
}
}
final List<Node> 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("</split>");
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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
@ -256,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)
@ -317,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;
@ -1479,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");
}
@ -1503,8 +1530,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 +1552,22 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
addOwnProperty(ScriptingFunctions.EXIT_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED);
}
private static void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
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");
@ -1545,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;

View File

@ -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);

View File

@ -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();
}
@ -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
*

View File

@ -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) {

View File

@ -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:

View File

@ -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) {

View File

@ -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();
}

View File

@ -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<ScriptEnvironment> 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<ScriptEnvironment> 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);
}
}
}

View File

@ -313,7 +313,7 @@ loop:
}
// Construct new object literal.
return new ObjectNode(source, objectToken, finish, null, elements);
return new ObjectNode(source, objectToken, finish, elements);
}
/**

View File

@ -56,6 +56,7 @@ 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;
@ -75,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;
@ -96,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.runtime.Context;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
@ -116,19 +117,13 @@ 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;
/** Current parsing block. */
private Block block;
private final LexicalContext lexicalContext = new LexicalContext();
private List<Node> functionDeclarations;
/** 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
@ -279,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;
}
/**
@ -292,18 +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);
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;
}
@ -311,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);
}
/**
@ -323,22 +324,25 @@ loop:
private Block getBlock(final boolean needsBraces) {
// Set up new block. Captures LBRACE.
final Block newBlock = newBlock();
pushControlNode(newBlock);
// Block opening brace.
if (needsBraces) {
expect(LBRACE);
}
try {
// Accumulate block statements.
statementList();
pushControlNode(newBlock);
// Block opening brace.
if (needsBraces) {
expect(LBRACE);
}
try {
// Accumulate block statements.
statementList();
} finally {
popControlNode();
}
} finally {
restoreBlock();
popControlNode();
restoreBlock(newBlock);
}
final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
// Block closing brace.
if (needsBraces) {
@ -365,7 +369,7 @@ loop:
// Accumulate statements.
statement();
} finally {
restoreBlock();
restoreBlock(newBlock);
}
return newBlock;
@ -379,7 +383,10 @@ loop:
final String name = ident.getName();
if (EVAL.tag().equals(name)) {
function.setHasEval();
final Iterator<FunctionNode> it = lexicalContext.getFunctions();
if(it.hasNext()) {
it.next().setHasEval(it);
}
}
}
@ -391,7 +398,7 @@ loop:
final String name = ident.getName();
if (ARGUMENTS.tag().equals(name)) {
function.setUsesArguments();
getFunction().setUsesArguments();
}
}
@ -438,7 +445,7 @@ loop:
}
if (lhs instanceof IdentNode) {
if (! checkIdentLValue((IdentNode)lhs)) {
if (!checkIdentLValue((IdentNode)lhs)) {
return referenceError(lhs, rhs);
}
verifyStrictIdent((IdentNode)lhs, "assignment");
@ -482,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;
}
@ -496,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();
}
/**
@ -513,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()) {
@ -531,7 +539,7 @@ loop:
*/
private void popControlNode() {
// Get control stack.
final Stack<Node> controlStack = function.getControlStack();
final Stack<Node> controlStack = getFunction().getControlStack();
// Can be empty if missing brace.
if (!controlStack.isEmpty()) {
@ -541,7 +549,7 @@ loop:
private void popControlNode(final Node node) {
// Get control stack.
final Stack<Node> controlStack = function.getControlStack();
final Stack<Node> controlStack = getFunction().getControlStack();
// Can be empty if missing brace.
if (!controlStack.isEmpty() && controlStack.peek() == node) {
@ -550,7 +558,7 @@ loop:
}
private boolean isInWithBlock() {
final Stack<Node> controlStack = function.getControlStack();
final Stack<Node> controlStack = getFunction().getControlStack();
for (int i = controlStack.size() - 1; i >= 0; i--) {
final Node node = controlStack.get(i);
@ -563,7 +571,7 @@ loop:
}
private <T extends Node> T findControl(final Class<T> ctype) {
final Stack<Node> controlStack = function.getControlStack();
final Stack<Node> controlStack = getFunction().getControlStack();
for (int i = controlStack.size() - 1; i >= 0; i--) {
final Node node = controlStack.get(i);
@ -577,7 +585,7 @@ loop:
private <T extends Node> List<T> findControls(final Class<T> ctype, final Node to) {
final List<T> nodes = new ArrayList<>();
final Stack<Node> controlStack = function.getControlStack();
final Stack<Node> controlStack = getFunction().getControlStack();
for (int i = controlStack.size() - 1; i >= 0; i--) {
final Node node = controlStack.get(i);
@ -621,15 +629,16 @@ 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.
functionDeclarations = new ArrayList<>();
sourceElements();
script.prependStatements(functionDeclarations);
functionDeclarations = null;
expect(EOF);
// Set the last token of the script.
script.setLastToken(token);
script.setFinish(source.getLength() - 1);
@ -707,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);
@ -727,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
@ -799,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:
@ -889,7 +899,7 @@ loop:
// Force block execution.
final ExecuteNode executeNode = new ExecuteNode(source, newBlock.getToken(), finish, newBlock);
block.addStatement(executeNode);
getBlock().addStatement(executeNode);
}
/**
@ -984,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;
@ -1003,7 +1009,7 @@ loop:
boolean semicolon = type == SEMICOLON;
endOfLine();
if (semicolon) {
block.setFinish(finish);
getBlock().setFinish(finish);
}
}
@ -1020,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)));
}
@ -1046,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);
}
@ -1055,7 +1061,7 @@ loop:
if (executeNode != null) {
executeNode.setFinish(finish);
block.setFinish(finish);
getBlock().setFinish(finish);
}
}
@ -1097,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);
}
/**
@ -1146,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));
}
/**
@ -1231,7 +1237,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");
@ -1286,7 +1292,7 @@ loop:
whileNode.setFinish(statements.getFinish());
// Add WHILE node.
block.addStatement(whileNode);
getBlock().addStatement(whileNode);
} finally {
popControlNode();
}
@ -1333,7 +1339,7 @@ loop:
doWhileNode.setFinish(finish);
// Add DO node.
block.addStatement(doWhileNode);
getBlock().addStatement(doWhileNode);
} finally {
popControlNode();
}
@ -1385,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);
}
/**
@ -1433,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);
}
/**
@ -1446,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"));
}
@ -1473,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);
}
/**
@ -1508,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);
}
/**
@ -1532,7 +1538,10 @@ loop:
// Get WITH expression.
final WithNode withNode = new WithNode(source, withToken, finish, null, null);
function.setHasWith();
final Iterator<FunctionNode> it = lexicalContext.getFunctions();
if(it.hasNext()) {
it.next().setHasWith(it);
}
try {
pushControlNode(withNode);
@ -1552,7 +1561,7 @@ loop:
popControlNode(withNode);
}
block.addStatement(withNode);
getBlock().addStatement(withNode);
}
/**
@ -1652,7 +1661,7 @@ loop:
switchNode.setFinish(finish);
block.addStatement(switchNode);
getBlock().addStatement(switchNode);
} finally {
popControlNode();
}
@ -1687,7 +1696,7 @@ loop:
labelNode.setBody(statements);
labelNode.setFinish(finish);
block.addStatement(labelNode);
getBlock().addStatement(labelNode);
} finally {
// Remove label.
popLabel();
@ -1730,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);
}
/**
@ -1796,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.
@ -1843,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));
}
/**
@ -1867,7 +1876,7 @@ loop:
endOfLine();
final RuntimeNode runtimeNode = new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>());
block.addStatement(runtimeNode);
getBlock().addStatement(runtimeNode);
}
/**
@ -2026,7 +2035,7 @@ loop:
break;
default:
if (! elision) {
if (!elision) {
error(AbstractParser.message("expected.comma", type.getNameOrType()));
}
// Add expression element.
@ -2067,15 +2076,11 @@ loop:
next();
// Object context.
Block objectContext = null;
// Prepare to accumulate elements.
final List<Node> elements = new ArrayList<>();
final Map<Object, PropertyNode> 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 +2095,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) {
redefinitionOk = false;
}
// 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 (! redefinitionOk) {
error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
}
if (value != null) {
final Node existingValue = existingProperty.getValue();
if (existingValue == null) {
existingProperty.setValue(value);
} else {
final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
}
existingProperty.setGetter(null);
existingProperty.setSetter(null);
}
if (getter != null) {
existingProperty.setGetter(getter);
}
if (setter != null) {
existingProperty.setSetter(setter);
}
} else {
map.put(key, property);
elements.add(property);
}
break;
}
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) {
redefinitionOk = false;
}
// 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 (!redefinitionOk) {
error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
}
if (value != null) {
final Node existingValue = existingProperty.getValue();
if (existingValue == null) {
existingProperty.setValue(value);
} else {
final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
}
existingProperty.setGetter(null);
existingProperty.setSetter(null);
}
if (getter != null) {
existingProperty.setGetter(getter);
}
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);
}
/**
@ -2258,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":
@ -2273,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:
@ -2454,7 +2452,7 @@ loop:
case FUNCTION:
// Get function expression.
lhs = functionExpression(false);
lhs = functionExpression(false, false);
break;
default:
@ -2559,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;
@ -2594,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();
}
}
@ -2605,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();
@ -2642,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;
}
/**
@ -2735,7 +2734,14 @@ loop:
expect(LBRACE);
// Gather the function elements.
sourceElements();
final List<Node> prevFunctionDecls = functionDeclarations;
functionDeclarations = new ArrayList<>();
try {
sourceElements();
functionNode.prependStatements(functionDeclarations);
} finally {
functionDeclarations = prevFunctionDecls;
}
functionNode.setLastToken(token);
expect(RBRACE);
@ -2743,12 +2749,9 @@ loop:
}
} finally {
restoreBlock();
restoreBlock(functionNode);
}
// Add the body of the function to the current block.
block.addFunction(functionNode);
return functionNode;
}
@ -2845,7 +2848,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 +2875,7 @@ loop:
return referenceError(lhs, null);
}
if (lhs instanceof IdentNode) {
if (! checkIdentLValue((IdentNode)lhs)) {
if (!checkIdentLValue((IdentNode)lhs)) {
next();
return referenceError(lhs, null);
}
@ -3083,4 +3086,12 @@ loop:
public String toString() {
return "[JavaScript Parsing]";
}
private Block getBlock() {
return lexicalContext.getCurrentBlock();
}
private FunctionNode getFunction() {
return lexicalContext.getCurrentFunction();
}
}

View File

@ -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;
}

View File

@ -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<T> {
*/
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);
}

View File

@ -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<CompiledFunction> {
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 "<invoker=" + invoker + " ctor=" + constructor + ">";
}
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.
}
}

View File

@ -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> {
CompiledFunction best(final MethodType type) {
final Iterator<CompiledFunction> 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);
}
}

View File

@ -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;
@ -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;
@ -77,7 +80,7 @@ public final class Context {
/**
* Return the context for this installer
* @return context
* @return ScriptEnvironment
*/
@Override
public ScriptEnvironment getOwner() {
@ -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<Void>() {
@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;
@ -731,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;
}
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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

View File

@ -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<ScriptEnvironment> 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<ScriptEnvironment> 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;
}
}

View File

@ -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");

View File

@ -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);

Some files were not shown because too many files have changed in this diff Show More