Merge
This commit is contained in:
commit
f614f5d693
@ -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 $*
|
||||
|
@ -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 $*
|
||||
|
@ -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 $*
|
||||
|
@ -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 $*
|
||||
|
@ -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
|
||||
|
@ -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!";
|
||||
|
@ -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!";
|
||||
|
@ -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>
|
||||
|
@ -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"/>
|
||||
|
@ -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.
|
||||
|
@ -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}
|
||||
|
@ -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 '<'. This is used to find out if
|
||||
* previous parameter is used.
|
||||
*
|
||||
* @param s
|
||||
* @param s string to check
|
||||
* @return true if '<' is in the string, else false
|
||||
*/
|
||||
private static boolean isPreviousArgument(final String s) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
60
nashorn/src/jdk/nashorn/api/scripting/ScriptUtils.java
Normal file
60
nashorn/src/jdk/nashorn/api/scripting/ScriptUtils.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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 & 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
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
@ -1378,8 +1410,8 @@ 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 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;
|
||||
}
|
||||
@ -3235,41 +3255,21 @@ 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 };
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,52 +246,82 @@ 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 ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Install compiled classes into a given loader
|
||||
* @return root script class - if there are several compile units they will also be installed
|
||||
*/
|
||||
public Class<?> install() {
|
||||
final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
|
||||
LOG.fine(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
|
||||
if (info) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Compile job for '").
|
||||
append(functionNode.getName()).
|
||||
append("' finished");
|
||||
|
||||
Class<?> rootClass = null;
|
||||
if (time > 0L) {
|
||||
sb.append(" in ").
|
||||
append(time).
|
||||
append(" ms");
|
||||
}
|
||||
|
||||
for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
|
||||
final String className = entry.getKey();
|
||||
LOG.info(sb.toString());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Class<?> install(final String className, final byte[] code) {
|
||||
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;
|
||||
}
|
||||
|
||||
try {
|
||||
final Source source = getSource();
|
||||
final Object[] constants = getConstantData().toArray();
|
||||
@ -313,13 +342,81 @@ public final class Compiler {
|
||||
} catch (final PrivilegedActionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install compiled classes into a given loader
|
||||
* @return root script class - if there are several compile units they will also be installed
|
||||
*/
|
||||
public Class<?> install() {
|
||||
final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
|
||||
|
||||
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
|
||||
|
||||
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();
|
||||
if (className.equals(rootClassName)) {
|
||||
continue;
|
||||
}
|
||||
final byte[] code = entry.getValue();
|
||||
length += code.length;
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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++) {
|
||||
|
@ -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 enter(final IfNode ifNode) {
|
||||
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 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;
|
||||
return ((IdentNode)function).setIsFunction();
|
||||
} else if (function instanceof BaseNode) {
|
||||
return ((BaseNode)function).setIsFunction();
|
||||
}
|
||||
};
|
||||
} 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 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) {
|
||||
return true;
|
||||
for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
|
||||
final Block block = blocks.next();
|
||||
if(block == target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final Block catchBlock : tryNode.getCatchBlocks()) {
|
||||
if (catchBlock == current) {
|
||||
if(tryNode.isChildBlock(block)) {
|
||||
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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
@ -271,7 +260,6 @@ public class FunctionNode extends Block {
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -72,8 +72,7 @@ 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.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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
198
nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java
Normal file
198
nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java
Normal 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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -313,7 +313,7 @@ loop:
|
||||
}
|
||||
|
||||
// Construct new object literal.
|
||||
return new ObjectNode(source, objectToken, finish, null, elements);
|
||||
return new ObjectNode(source, objectToken, finish, elements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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,6 +324,7 @@ loop:
|
||||
private Block getBlock(final boolean needsBraces) {
|
||||
// Set up new block. Captures LBRACE.
|
||||
final Block newBlock = newBlock();
|
||||
try {
|
||||
pushControlNode(newBlock);
|
||||
|
||||
// Block opening brace.
|
||||
@ -334,9 +336,11 @@ loop:
|
||||
// Accumulate block statements.
|
||||
statementList();
|
||||
} finally {
|
||||
restoreBlock();
|
||||
popControlNode();
|
||||
}
|
||||
} finally {
|
||||
restoreBlock(newBlock);
|
||||
}
|
||||
|
||||
final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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);
|
||||
|
||||
try {
|
||||
final Block catchBlock = newBlock();
|
||||
try {
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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();
|
||||
|
||||
boolean commaSeen = true;
|
||||
loop:
|
||||
while (true) {
|
||||
@ -2172,15 +2177,8 @@ loop:
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3083,4 +3086,12 @@ loop:
|
||||
public String toString() {
|
||||
return "[JavaScript Parsing]";
|
||||
}
|
||||
|
||||
private Block getBlock() {
|
||||
return lexicalContext.getCurrentBlock();
|
||||
}
|
||||
|
||||
private FunctionNode getFunction() {
|
||||
return lexicalContext.getCurrentFunction();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
162
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
Normal file
162
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
Normal 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.
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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,10 +94,29 @@ 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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")
|
||||
@ -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);
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user