8035820: Optimistic recompilation
Co-authored-by: Marcus Lagergren <marcus.lagergren@oracle.com> Reviewed-by: hannesw, jlaskey, sundar
This commit is contained in:
parent
18489cc7a4
commit
e9e7dd2ec1
@ -13,6 +13,8 @@ webrev.zip
|
|||||||
*.clazz
|
*.clazz
|
||||||
*.log
|
*.log
|
||||||
*.orig
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*~
|
||||||
genfiles.properties
|
genfiles.properties
|
||||||
hotspot.log
|
hotspot.log
|
||||||
.DS_Store*
|
.DS_Store*
|
||||||
|
25
nashorn/bin/rundiff.sh
Normal file
25
nashorn/bin/rundiff.sh
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# do two runs of a script, one optimistic and one pessimistic, expect identical outputs
|
||||||
|
# if not, display and error message and a diff
|
||||||
|
|
||||||
|
which opendiff >/dev/null
|
||||||
|
RES=$?
|
||||||
|
if [ $RES = 0 ]; then
|
||||||
|
DIFFTOOL=opendiff
|
||||||
|
else
|
||||||
|
DIFFTOOL=diff
|
||||||
|
fi
|
||||||
|
|
||||||
|
OPTIMISTIC=out_optimistic
|
||||||
|
PESSIMISTIC=out_pessimistic
|
||||||
|
$JAVA_HOME/bin/java -ea -jar ../dist/nashorn.jar ${@} >$PESSIMISTIC
|
||||||
|
$JAVA_HOME/bin/java -ea -Dnashorn.optimistic -jar ../dist/nashorn.jar ${@} >$OPTIMISTIC
|
||||||
|
|
||||||
|
if ! diff -q $PESSIMISTIC $OPTIMISTIC >/dev/null ; then
|
||||||
|
echo "Failure! Results are different"
|
||||||
|
echo ""
|
||||||
|
$DIFFTOOL $PESSIMISTIC $OPTIMISTIC
|
||||||
|
else
|
||||||
|
echo "OK - Results are identical"
|
||||||
|
fi
|
3
nashorn/bin/runnormal.sh
Normal file
3
nashorn/bin/runnormal.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
FILENAME="./pessimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||||
|
$JAVA_HOME/bin/java -ea -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -Djava.ext.dirs=dist jdk.nashorn.tools.Shell ${@}
|
3
nashorn/bin/runnormaldual.sh
Normal file
3
nashorn/bin/runnormaldual.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
FILENAME="./optimistic_dual_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||||
|
$JAVA_HOME/bin/java -ea -Dnashorn.fields.dual -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:+UseMathExactIntrinsics ${@}
|
3
nashorn/bin/runopt.sh
Normal file
3
nashorn/bin/runopt.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
FILENAME="./optimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||||
|
$JAVA_HOME/bin/java -ea -Dnashorn.optimistic -Dnashorn.fastrewrite -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:-UseMathExactIntrinsics -Xbootclasspath/p:dist/nashorn.jar jdk.nashorn.tools.Shell ${@}
|
3
nashorn/bin/runoptdual.sh
Normal file
3
nashorn/bin/runoptdual.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
FILENAME="./optimistic_dual_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||||
|
$JAVA_HOME/bin/java -ea -Dnashorn.fields.dual -Dnashorn.optimistic -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:-UseMathExactIntrinsics ${@}
|
25
nashorn/bin/runoptdualcatch.sh
Normal file
25
nashorn/bin/runoptdualcatch.sh
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
|
||||||
|
|
||||||
|
FILENAME="./optimistic_dual_catch$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||||
|
|
||||||
|
$JAVA_HOME/bin/java \
|
||||||
|
-ea \
|
||||||
|
-esa \
|
||||||
|
$FLAGS \
|
||||||
|
-Dnashorn.fastrewrite \
|
||||||
|
-Dnashorn.optimistic \
|
||||||
|
-Xbootclasspath/p:/Users/marcus/src/tip/dist/nashorn.jar \
|
||||||
|
-Xms2G -Xmx2G \
|
||||||
|
-XX:+UnlockCommercialFeatures \
|
||||||
|
-XX:+FlightRecorder \
|
||||||
|
-XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=1024 \
|
||||||
|
-XX:TypeProfileLevel=222 \
|
||||||
|
-XX:+UnlockExperimentalVMOptions \
|
||||||
|
-XX:+UseTypeSpeculation \
|
||||||
|
-XX:+UseMathExactIntrinsics \
|
||||||
|
-XX:+UnlockDiagnosticVMOptions \
|
||||||
|
-cp $CLASSPATH:../build/test/classes/ \
|
||||||
|
jdk.nashorn.tools.Shell ${@}
|
||||||
|
|
@ -413,7 +413,8 @@ public class MethodGenerator extends MethodVisitor {
|
|||||||
super.visitMethodInsn(INVOKEVIRTUAL,
|
super.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
"java/io/PrintStream",
|
"java/io/PrintStream",
|
||||||
"println",
|
"println",
|
||||||
"(Ljava/lang/String;)V", false);
|
"(Ljava/lang/String;)V",
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// print the object on the top of the stack
|
// print the object on the top of the stack
|
||||||
@ -426,6 +427,7 @@ public class MethodGenerator extends MethodVisitor {
|
|||||||
super.visitMethodInsn(INVOKEVIRTUAL,
|
super.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
"java/io/PrintStream",
|
"java/io/PrintStream",
|
||||||
"println",
|
"println",
|
||||||
"(Ljava/lang/Object;)V", false);
|
"(Ljava/lang/Object;)V",
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,18 +365,6 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
|||||||
</testng>
|
</testng>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="test-basicparallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file">
|
|
||||||
<!-- use just build.test.classes.dir to avoid referring to TestNG -->
|
|
||||||
<java classname="${parallel.test.runner}" dir="${basedir}" classpath="${build.test.classes.dir}" failonerror="true" fork="true">
|
|
||||||
<jvmarg line="${ext.class.path}"/>
|
|
||||||
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
|
|
||||||
<syspropertyset>
|
|
||||||
<propertyref prefix="test-sys-prop."/>
|
|
||||||
<mapper type="glob" from="test-sys-prop.*" to="*"/>
|
|
||||||
</syspropertyset>
|
|
||||||
</java>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available">
|
<target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available">
|
||||||
<echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/>
|
<echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/>
|
||||||
</target>
|
</target>
|
||||||
@ -467,6 +455,28 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
|||||||
</java>
|
</java>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
<!-- classpath="${build.test.classes.dir}"-->
|
||||||
|
|
||||||
|
<target name="testparallel" depends="test-parallel"/>
|
||||||
|
|
||||||
|
<target name="test-parallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
|
||||||
|
<!-- use just build.test.classes.dir to avoid referring to TestNG -->
|
||||||
|
<java classname="${parallel.test.runner}" dir="${basedir}"
|
||||||
|
failonerror="true"
|
||||||
|
fork="true">
|
||||||
|
<jvmarg line="${ext.class.path}"/>
|
||||||
|
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
|
||||||
|
<classpath>
|
||||||
|
<pathelement path="${run.test.classpath}"/>
|
||||||
|
<pathelement path="${build.test.classes.dir}"/>
|
||||||
|
</classpath>
|
||||||
|
<syspropertyset>
|
||||||
|
<propertyref prefix="test-sys-prop."/>
|
||||||
|
<mapper type="glob" from="test-sys-prop.*" to="*"/>
|
||||||
|
</syspropertyset>
|
||||||
|
</java>
|
||||||
|
</target>
|
||||||
|
|
||||||
<target name="all" depends="test, docs"
|
<target name="all" depends="test, docs"
|
||||||
description="Build, test and generate docs for nashorn"/>
|
description="Build, test and generate docs for nashorn"/>
|
||||||
|
|
||||||
|
@ -31,9 +31,10 @@
|
|||||||
<classpath path="${run.test.classpath}"/>
|
<classpath path="${run.test.classpath}"/>
|
||||||
</nbjpdastart>
|
</nbjpdastart>
|
||||||
<java classname="jdk.nashorn.tools.Shell" classpath="${run.test.classpath}" dir="samples" fork="true">
|
<java classname="jdk.nashorn.tools.Shell" classpath="${run.test.classpath}" dir="samples" fork="true">
|
||||||
|
<jvmarg line="-Dnashorn.optimistic"/>
|
||||||
<jvmarg line="${ext.class.path}"/>
|
<jvmarg line="${ext.class.path}"/>
|
||||||
<jvmarg line="${run.test.jvmargs}"/>
|
<jvmarg line="${run.test.jvmargs}"/>
|
||||||
<arg value="test.js"/>
|
<arg value="../make/str.js"/>
|
||||||
<jvmarg value="-Xdebug"/>
|
<jvmarg value="-Xdebug"/>
|
||||||
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
|
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
|
||||||
</java>
|
</java>
|
||||||
|
@ -175,7 +175,7 @@ octane-test-sys-prop.test.js.exclude.list=\
|
|||||||
mandreel.js
|
mandreel.js
|
||||||
|
|
||||||
# test root for sunspider
|
# test root for sunspider
|
||||||
sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0/
|
sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0.2/
|
||||||
|
|
||||||
# framework root for sunspider
|
# framework root for sunspider
|
||||||
sunspider-test-sys-prop.test.js.framework=${test.basic.dir}/runsunspider.js
|
sunspider-test-sys-prop.test.js.framework=${test.basic.dir}/runsunspider.js
|
||||||
@ -258,20 +258,29 @@ run.test.classpath=\
|
|||||||
src.dir=src
|
src.dir=src
|
||||||
test.src.dir=test/src
|
test.src.dir=test/src
|
||||||
|
|
||||||
# -Xmx is used for all tests, -Xms only for octane benchmark
|
|
||||||
run.test.xmx=3G
|
run.test.xmx=3G
|
||||||
run.test.xms=2G
|
run.test.xms=2G
|
||||||
|
|
||||||
|
#uncomment to enable flight recording - crank up stack trace for lambda forms
|
||||||
|
#jfr.args=-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath="test_suite.jfr",stackdepth=1024 \
|
||||||
|
jfr.args=
|
||||||
|
|
||||||
run.test.user.language=tr
|
run.test.user.language=tr
|
||||||
run.test.user.country=TR
|
run.test.user.country=TR
|
||||||
|
|
||||||
run.test.jvmargs.common=-server -XX:+TieredCompilation -Dfile.encoding=UTF-8 -Duser.language=${run.test.user.language} -Duser.country=${run.test.user.country} -XX:+HeapDumpOnOutOfMemoryError
|
run.test.jvmargs.common=\
|
||||||
|
-server \
|
||||||
|
-Dfile.encoding=UTF-8 \
|
||||||
|
-Duser.language=${run.test.user.language} \
|
||||||
|
-Duser.country=${run.test.user.country} \
|
||||||
|
${jfr.args} \
|
||||||
|
-XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
|
||||||
#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
||||||
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
|
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
|
||||||
|
|
||||||
# turn on assertions for tests
|
# turn on assertions for tests
|
||||||
run.test.jvmargs.main=${run.test.jvmargs.common} -ea
|
run.test.jvmargs.main=${run.test.jvmargs.common} -ea -Dnashorn.optimistic -Dnashorn.lazy
|
||||||
|
|
||||||
#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
||||||
run.test.jvmargs.octane.main=${run.test.jvmargs.common}
|
run.test.jvmargs.octane.main=${run.test.jvmargs.common}
|
||||||
|
@ -140,7 +140,6 @@ import jdk.internal.dynalink.support.RuntimeContextLinkRequestImpl;
|
|||||||
* @author Attila Szegedi
|
* @author Attila Szegedi
|
||||||
*/
|
*/
|
||||||
public class DynamicLinker {
|
public class DynamicLinker {
|
||||||
|
|
||||||
private static final String CLASS_NAME = DynamicLinker.class.getName();
|
private static final String CLASS_NAME = DynamicLinker.class.getName();
|
||||||
private static final String RELINK_METHOD_NAME = "relink";
|
private static final String RELINK_METHOD_NAME = "relink";
|
||||||
|
|
||||||
@ -148,6 +147,7 @@ public class DynamicLinker {
|
|||||||
private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite";
|
private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite";
|
||||||
|
|
||||||
private final LinkerServices linkerServices;
|
private final LinkerServices linkerServices;
|
||||||
|
private final GuardedInvocationFilter prelinkFilter;
|
||||||
private final int runtimeContextArgCount;
|
private final int runtimeContextArgCount;
|
||||||
private final boolean syncOnRelink;
|
private final boolean syncOnRelink;
|
||||||
private final int unstableRelinkThreshold;
|
private final int unstableRelinkThreshold;
|
||||||
@ -156,18 +156,20 @@ public class DynamicLinker {
|
|||||||
* Creates a new dynamic linker.
|
* Creates a new dynamic linker.
|
||||||
*
|
*
|
||||||
* @param linkerServices the linkerServices used by the linker, created by the factory.
|
* @param linkerServices the linkerServices used by the linker, created by the factory.
|
||||||
|
* @param prelinkFilter see {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter)}
|
||||||
* @param runtimeContextArgCount see {@link DynamicLinkerFactory#setRuntimeContextArgCount(int)}
|
* @param runtimeContextArgCount see {@link DynamicLinkerFactory#setRuntimeContextArgCount(int)}
|
||||||
*/
|
*/
|
||||||
DynamicLinker(LinkerServices linkerServices, int runtimeContextArgCount, boolean syncOnRelink,
|
DynamicLinker(LinkerServices linkerServices, GuardedInvocationFilter prelinkFilter, int runtimeContextArgCount,
|
||||||
int unstableRelinkThreshold) {
|
boolean syncOnRelink, int unstableRelinkThreshold) {
|
||||||
if(runtimeContextArgCount < 0) {
|
if(runtimeContextArgCount < 0) {
|
||||||
throw new IllegalArgumentException("runtimeContextArgCount < 0");
|
throw new IllegalArgumentException("runtimeContextArgCount < 0");
|
||||||
}
|
}
|
||||||
if(unstableRelinkThreshold < 0) {
|
if(unstableRelinkThreshold < 0) {
|
||||||
throw new IllegalArgumentException("unstableRelinkThreshold < 0");
|
throw new IllegalArgumentException("unstableRelinkThreshold < 0");
|
||||||
}
|
}
|
||||||
this.runtimeContextArgCount = runtimeContextArgCount;
|
|
||||||
this.linkerServices = linkerServices;
|
this.linkerServices = linkerServices;
|
||||||
|
this.prelinkFilter = prelinkFilter;
|
||||||
|
this.runtimeContextArgCount = runtimeContextArgCount;
|
||||||
this.syncOnRelink = syncOnRelink;
|
this.syncOnRelink = syncOnRelink;
|
||||||
this.unstableRelinkThreshold = unstableRelinkThreshold;
|
this.unstableRelinkThreshold = unstableRelinkThreshold;
|
||||||
}
|
}
|
||||||
@ -224,11 +226,10 @@ public class DynamicLinker {
|
|||||||
final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0;
|
final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0;
|
||||||
final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
|
final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
|
||||||
final LinkRequest linkRequest =
|
final LinkRequest linkRequest =
|
||||||
runtimeContextArgCount == 0 ? new LinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments)
|
runtimeContextArgCount == 0 ?
|
||||||
: new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments,
|
new LinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments) :
|
||||||
runtimeContextArgCount);
|
new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments, runtimeContextArgCount);
|
||||||
|
|
||||||
// Find a suitable method handle with a guard
|
|
||||||
GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);
|
GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);
|
||||||
|
|
||||||
// None found - throw an exception
|
// None found - throw an exception
|
||||||
@ -248,6 +249,11 @@ public class DynamicLinker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure we filter the invocation before linking it into the call site. This is typically used to match the
|
||||||
|
// return type of the invocation to the call site.
|
||||||
|
guardedInvocation = prelinkFilter.filter(guardedInvocation, linkRequest, linkerServices);
|
||||||
|
guardedInvocation.getClass(); // null pointer check
|
||||||
|
|
||||||
int newRelinkCount = relinkCount;
|
int newRelinkCount = relinkCount;
|
||||||
// Note that the short-circuited "&&" evaluation below ensures we'll increment the relinkCount until
|
// Note that the short-circuited "&&" evaluation below ensures we'll increment the relinkCount until
|
||||||
// threshold + 1 but not beyond that. Threshold + 1 is treated as a special value to signal that resetAndRelink
|
// threshold + 1 but not beyond that. Threshold + 1 is treated as a special value to signal that resetAndRelink
|
||||||
|
@ -102,14 +102,15 @@ import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
|
|||||||
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
|
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
|
||||||
import jdk.internal.dynalink.support.CompositeGuardingDynamicLinker;
|
import jdk.internal.dynalink.support.CompositeGuardingDynamicLinker;
|
||||||
import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
|
import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
|
||||||
|
import jdk.internal.dynalink.support.DefaultPrelinkFilter;
|
||||||
import jdk.internal.dynalink.support.LinkerServicesImpl;
|
import jdk.internal.dynalink.support.LinkerServicesImpl;
|
||||||
import jdk.internal.dynalink.support.TypeConverterFactory;
|
import jdk.internal.dynalink.support.TypeConverterFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
|
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
|
||||||
* of all {@link GuardingDynamicLinker}s known and pre-created by the caller as well as any
|
* of all {@link GuardingDynamicLinker}s known and pre-created by the caller as well as any
|
||||||
* {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker}. See
|
* {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker} and a
|
||||||
* {@link DynamicLinker} documentation for tips on how to use this class.
|
* {@link DefaultPrelinkFilter}. See {@link DynamicLinker} documentation for tips on how to use this class.
|
||||||
*
|
*
|
||||||
* @author Attila Szegedi
|
* @author Attila Szegedi
|
||||||
*/
|
*/
|
||||||
@ -128,6 +129,7 @@ public class DynamicLinkerFactory {
|
|||||||
private int runtimeContextArgCount = 0;
|
private int runtimeContextArgCount = 0;
|
||||||
private boolean syncOnRelink = false;
|
private boolean syncOnRelink = false;
|
||||||
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
|
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
|
||||||
|
private GuardedInvocationFilter prelinkFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
|
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
|
||||||
@ -246,7 +248,19 @@ public class DynamicLinkerFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers.
|
* Set the pre-link filter. This is a {@link GuardedInvocationFilter} that will get the final chance to modify the
|
||||||
|
* guarded invocation after it has been created by a component linker and before the dynamic linker links it into
|
||||||
|
* the call site. It is normally used to adapt the return value type of the invocation to the type of the call site.
|
||||||
|
* When not set explicitly, {@link DefaultPrelinkFilter} will be used.
|
||||||
|
* @param prelinkFilter the pre-link filter for the dynamic linker.
|
||||||
|
*/
|
||||||
|
public void setPrelinkFilter(GuardedInvocationFilter prelinkFilter) {
|
||||||
|
this.prelinkFilter = prelinkFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
|
||||||
|
* the pre-link filter.
|
||||||
*
|
*
|
||||||
* @return the new dynamic Linker
|
* @return the new dynamic Linker
|
||||||
*/
|
*/
|
||||||
@ -306,8 +320,12 @@ public class DynamicLinkerFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(prelinkFilter == null) {
|
||||||
|
prelinkFilter = new DefaultPrelinkFilter();
|
||||||
|
}
|
||||||
|
|
||||||
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
|
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
|
||||||
runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
|
prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClassLoader getThreadContextClassLoader() {
|
private static ClassLoader getThreadContextClassLoader() {
|
||||||
|
105
nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java
Normal file
105
nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is available under and governed by the GNU General Public
|
||||||
|
* License version 2 only, as published by the Free Software Foundation.
|
||||||
|
* However, the following notice accompanied the original version of this
|
||||||
|
* file, and Oracle licenses the original version of this file under the BSD
|
||||||
|
* license:
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Copyright 2009-2013 Attila Szegedi
|
||||||
|
|
||||||
|
Licensed under both the Apache License, Version 2.0 (the "Apache License")
|
||||||
|
and the BSD License (the "BSD License"), with licensee being free to
|
||||||
|
choose either of the two at their discretion.
|
||||||
|
|
||||||
|
You may not use this file except in compliance with either the Apache
|
||||||
|
License or the BSD License.
|
||||||
|
|
||||||
|
If you choose to use this file in compliance with the Apache License, the
|
||||||
|
following notice applies to you:
|
||||||
|
|
||||||
|
You may obtain a copy of the Apache License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied. See the License for the specific language governing
|
||||||
|
permissions and limitations under the License.
|
||||||
|
|
||||||
|
If you choose to use this file in compliance with the BSD License, the
|
||||||
|
following notice applies to you:
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the copyright holder nor the names of
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
|
||||||
|
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.internal.dynalink;
|
||||||
|
|
||||||
|
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||||
|
import jdk.internal.dynalink.linker.LinkRequest;
|
||||||
|
import jdk.internal.dynalink.linker.LinkerServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for objects that are used to transform one guarded invocation into another one. Typical usage is for
|
||||||
|
* implementing {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter) pre-link filters}.
|
||||||
|
*/
|
||||||
|
public interface GuardedInvocationFilter {
|
||||||
|
/**
|
||||||
|
* Given a guarded invocation, return a potentially different guarded invocation.
|
||||||
|
* @param inv the original guarded invocation. Null is never passed.
|
||||||
|
* @param linkRequest the link request for which the invocation was generated (usually by some linker).
|
||||||
|
* @param linkerServices the linker services that can be used during creation of a new invocation.
|
||||||
|
* @return either the passed guarded invocation or a different one, with the difference usually determined based on
|
||||||
|
* information in the link request and the differing invocation created with the assistance of the linker services.
|
||||||
|
* Whether or not {@code null} is an accepted return value is dependent on the user of the filter.
|
||||||
|
*/
|
||||||
|
public GuardedInvocation filter(GuardedInvocation inv, LinkRequest linkRequest, LinkerServices linkerServices);
|
||||||
|
}
|
@ -97,7 +97,6 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||||
import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
|
import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
|
||||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||||
@ -107,6 +106,7 @@ import jdk.internal.dynalink.linker.LinkerServices;
|
|||||||
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
|
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
|
||||||
import jdk.internal.dynalink.support.Guards;
|
import jdk.internal.dynalink.support.Guards;
|
||||||
import jdk.internal.dynalink.support.Lookup;
|
import jdk.internal.dynalink.support.Lookup;
|
||||||
|
import jdk.internal.dynalink.support.TypeUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
|
* A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
|
||||||
@ -459,12 +459,16 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
|
|
||||||
private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
|
private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
|
||||||
LinkerServices linkerServices, List<String> operations) throws Exception {
|
LinkerServices linkerServices, List<String> operations) throws Exception {
|
||||||
final MethodType type = callSiteDescriptor.getMethodType();
|
|
||||||
switch(callSiteDescriptor.getNameTokenCount()) {
|
switch(callSiteDescriptor.getNameTokenCount()) {
|
||||||
case 2: {
|
case 2: {
|
||||||
// Must have three arguments: target object, property name, and property value.
|
// Must have three arguments: target object, property name, and property value.
|
||||||
assertParameterCount(callSiteDescriptor, 3);
|
assertParameterCount(callSiteDescriptor, 3);
|
||||||
|
|
||||||
|
// We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
|
||||||
|
// valid for us to convert return values proactively. Also, since we don't know what setters will be
|
||||||
|
// invoked, we'll conservatively presume Object return type.
|
||||||
|
final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
|
||||||
|
|
||||||
// What's below is basically:
|
// What's below is basically:
|
||||||
// foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
|
// foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
|
||||||
// get_setter_handle(type, linkerServices))
|
// get_setter_handle(type, linkerServices))
|
||||||
@ -473,8 +477,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
// component's invocation.
|
// component's invocation.
|
||||||
|
|
||||||
// Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
|
// Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
|
||||||
// abbreviate to R(O, N, V) going forward.
|
// abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
|
||||||
// We want setters that conform to "R(O, V)"
|
// Object return type).
|
||||||
final MethodType setterType = type.dropParameterTypes(1, 2);
|
final MethodType setterType = type.dropParameterTypes(1, 2);
|
||||||
// Bind property setter handle to the expected setter type and linker services. Type is
|
// Bind property setter handle to the expected setter type and linker services. Type is
|
||||||
// MethodHandle(Object, String, Object)
|
// MethodHandle(Object, String, Object)
|
||||||
@ -495,11 +499,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
|
|
||||||
final MethodHandle fallbackFolded;
|
final MethodHandle fallbackFolded;
|
||||||
if(nextComponent == null) {
|
if(nextComponent == null) {
|
||||||
// Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
|
// Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
|
||||||
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
|
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
|
||||||
type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
|
type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
|
||||||
} else {
|
} else {
|
||||||
// R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
|
// Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
|
||||||
// extra argument resulting from fold
|
// extra argument resulting from fold
|
||||||
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
|
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
|
||||||
0, MethodHandle.class);
|
0, MethodHandle.class);
|
||||||
@ -545,9 +549,12 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
|
|
||||||
private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
|
private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
|
||||||
LinkerServices linkerServices, List<String> ops) throws Exception {
|
LinkerServices linkerServices, List<String> ops) throws Exception {
|
||||||
final MethodType type = callSiteDescriptor.getMethodType();
|
|
||||||
switch(callSiteDescriptor.getNameTokenCount()) {
|
switch(callSiteDescriptor.getNameTokenCount()) {
|
||||||
case 2: {
|
case 2: {
|
||||||
|
// Since we can't know what kind of a getter we'll get back on different invocations, we'll just
|
||||||
|
// conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
|
||||||
|
// runtime might not allow coercing at that call site.
|
||||||
|
final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
|
||||||
// Must have exactly two arguments: receiver and name
|
// Must have exactly two arguments: receiver and name
|
||||||
assertParameterCount(callSiteDescriptor, 2);
|
assertParameterCount(callSiteDescriptor, 2);
|
||||||
|
|
||||||
@ -563,11 +570,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
|
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
|
||||||
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
|
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
|
||||||
callSiteBoundMethodGetter);
|
callSiteBoundMethodGetter);
|
||||||
// Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
|
// Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
|
||||||
final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
|
final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
|
||||||
MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
|
MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
|
||||||
// Since it's in the target of a fold, drop the unnecessary second argument
|
// Since it's in the target of a fold, drop the unnecessary second argument
|
||||||
// R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
|
// Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
|
||||||
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
|
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
|
||||||
type.parameterType(1));
|
type.parameterType(1));
|
||||||
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
|
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
|
||||||
@ -575,17 +582,19 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
|
|
||||||
final MethodHandle fallbackFolded;
|
final MethodHandle fallbackFolded;
|
||||||
if(nextComponent == null) {
|
if(nextComponent == null) {
|
||||||
// Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
|
// Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
|
||||||
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
|
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
|
||||||
type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
|
type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
|
||||||
} else {
|
} else {
|
||||||
// R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
|
// Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
|
||||||
// extra argument resulting from fold
|
// drop the extra argument resulting from fold and to change its return type to Object.
|
||||||
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
|
final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
|
||||||
0, AnnotatedDynamicMethod.class);
|
final MethodType nextType = nextInvocation.type();
|
||||||
|
fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
|
||||||
|
nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
|
// fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
|
||||||
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
|
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
|
||||||
IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
|
IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
|
||||||
if(nextComponent == null) {
|
if(nextComponent == null) {
|
||||||
@ -612,8 +621,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
// value is null.
|
// value is null.
|
||||||
final ValidationType validationType = annGetter.validationType;
|
final ValidationType validationType = annGetter.validationType;
|
||||||
// TODO: we aren't using the type that declares the most generic getter here!
|
// TODO: we aren't using the type that declares the most generic getter here!
|
||||||
return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
|
return new GuardedInvocationComponent(getter, getGuard(validationType,
|
||||||
type), clazz, validationType);
|
callSiteDescriptor.getMethodType()), clazz, validationType);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// Can't do anything with more than 3 name components
|
// Can't do anything with more than 3 name components
|
||||||
@ -642,21 +651,25 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
|
private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
|
||||||
MethodType.methodType(boolean.class, DynamicMethod.class));
|
MethodType.methodType(boolean.class, Object.class));
|
||||||
private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
|
private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
|
||||||
|
|
||||||
private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
|
private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
|
||||||
LinkerServices linkerServices, List<String> ops) throws Exception {
|
LinkerServices linkerServices, List<String> ops) throws Exception {
|
||||||
final MethodType type = callSiteDescriptor.getMethodType();
|
// The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
|
||||||
|
// be visible outside of this linker, declare it to return Object.
|
||||||
|
final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
|
||||||
switch(callSiteDescriptor.getNameTokenCount()) {
|
switch(callSiteDescriptor.getNameTokenCount()) {
|
||||||
case 2: {
|
case 2: {
|
||||||
// Must have exactly two arguments: receiver and name
|
// Must have exactly two arguments: receiver and name
|
||||||
assertParameterCount(callSiteDescriptor, 2);
|
assertParameterCount(callSiteDescriptor, 2);
|
||||||
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
|
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
|
||||||
linkerServices, ops);
|
linkerServices, ops);
|
||||||
if(nextComponent == null) {
|
if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
|
||||||
// No next component operation; just return a component for this operation.
|
nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
|
||||||
|
// No next component operation, or it can never produce a dynamic method; just return a component
|
||||||
|
// for this operation.
|
||||||
return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
|
return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,21 +678,20 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
// bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
|
// bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
|
||||||
// DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
|
// DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
|
||||||
|
|
||||||
final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
|
final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
|
||||||
DynamicMethod.class));
|
|
||||||
// Since it is part of the foldArgument() target, it will have extra args that we need to drop.
|
// Since it is part of the foldArgument() target, it will have extra args that we need to drop.
|
||||||
final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
|
final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
|
||||||
DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
|
OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
|
||||||
DynamicMethod.class));
|
|
||||||
final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
|
final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
|
||||||
// The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
|
// The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
|
||||||
assert nextComponentInvocation.type().equals(type);
|
// return type.
|
||||||
|
assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
|
||||||
// Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
|
// Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
|
||||||
final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
|
final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
|
||||||
DynamicMethod.class);
|
Object.class);
|
||||||
// Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
|
// Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
|
||||||
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
|
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
|
||||||
IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
|
IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
|
||||||
|
|
||||||
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
|
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
|
||||||
}
|
}
|
||||||
@ -695,7 +707,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
// No delegation to the next component of the composite operation; if we have a method with that name,
|
// No delegation to the next component of the composite operation; if we have a method with that name,
|
||||||
// we'll always return it at this point.
|
// we'll always return it at this point.
|
||||||
return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
|
return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
|
||||||
MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
|
MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// Can't do anything with more than 3 name components
|
// Can't do anything with more than 3 name components
|
||||||
@ -704,6 +716,30 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class MethodPair {
|
||||||
|
final MethodHandle method1;
|
||||||
|
final MethodHandle method2;
|
||||||
|
|
||||||
|
MethodPair(final MethodHandle method1, final MethodHandle method2) {
|
||||||
|
this.method1 = method1;
|
||||||
|
this.method2 = method2;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodHandle guardWithTest(final MethodHandle test) {
|
||||||
|
return MethodHandles.guardWithTest(test, method1, method2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodPair matchReturnTypes(MethodHandle m1, MethodHandle m2) {
|
||||||
|
final MethodType type1 = m1.type();
|
||||||
|
final MethodType type2 = m2.type();
|
||||||
|
final Class<?> commonRetType = TypeUtilities.getCommonLosslessConversionType(type1.returnType(),
|
||||||
|
type2.returnType());
|
||||||
|
return new MethodPair(
|
||||||
|
m1.asType(type1.changeReturnType(commonRetType)),
|
||||||
|
m2.asType(type2.changeReturnType(commonRetType)));
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
|
private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
|
||||||
if(descriptor.getMethodType().parameterCount() != paramCount) {
|
if(descriptor.getMethodType().parameterCount() != paramCount) {
|
||||||
throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
|
throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
|
||||||
@ -739,11 +775,14 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
|
private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
|
||||||
"getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
|
"getDynamicMethod", Object.class, Object.class), 1, Object.class);
|
||||||
private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
|
private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private DynamicMethod getDynamicMethod(Object name) {
|
// This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
|
||||||
|
// want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
|
||||||
|
// "dyn:getMethod" linking).
|
||||||
|
private Object getDynamicMethod(Object name) {
|
||||||
return getDynamicMethod(String.valueOf(name), methods);
|
return getDynamicMethod(String.valueOf(name), methods);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,8 +235,9 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
|||||||
} else {
|
} else {
|
||||||
checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
|
checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
|
||||||
}
|
}
|
||||||
return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
|
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
|
||||||
binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
|
nextComponent.getGuardedInvocation().getInvocation());
|
||||||
|
return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
|
||||||
gic.getValidatorClass(), gic.getValidationType());
|
gic.getValidatorClass(), gic.getValidationType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +307,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*private*/ MethodHandle bind(MethodHandle handle) {
|
/*private*/ MethodHandle bind(MethodHandle handle) {
|
||||||
return bindToFixedKey(linkerServices.asType(handle, methodType));
|
return bindToFixedKey(linkerServices.asTypeLosslessReturn(handle, methodType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private*/ MethodHandle bindTest(MethodHandle handle) {
|
/*private*/ MethodHandle bindTest(MethodHandle handle) {
|
||||||
@ -438,8 +439,9 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
|||||||
|
|
||||||
final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST :
|
final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST :
|
||||||
RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
|
RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
|
||||||
return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
|
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
|
||||||
binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
|
nextComponent.getGuardedInvocation().getInvocation());
|
||||||
|
return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
|
||||||
gic.getValidatorClass(), gic.getValidationType());
|
gic.getValidatorClass(), gic.getValidationType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,6 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("fallthrough")
|
|
||||||
@Override
|
@Override
|
||||||
public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
|
public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
|
||||||
final MethodType callSiteType = callSiteDescriptor.getMethodType();
|
final MethodType callSiteType = callSiteDescriptor.getMethodType();
|
||||||
@ -207,7 +206,7 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
|||||||
case 1: {
|
case 1: {
|
||||||
// Very lucky, we ended up with a single candidate method handle based on the call site signature; we
|
// Very lucky, we ended up with a single candidate method handle based on the call site signature; we
|
||||||
// can link it very simply by delegating to the SingleDynamicMethod.
|
// can link it very simply by delegating to the SingleDynamicMethod.
|
||||||
invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
|
return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// We have more than one candidate. We have no choice but to link to a method that resolves overloads on
|
// We have more than one candidate. We have no choice but to link to a method that resolves overloads on
|
||||||
|
@ -93,6 +93,7 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import jdk.internal.dynalink.linker.LinkerServices;
|
import jdk.internal.dynalink.linker.LinkerServices;
|
||||||
import jdk.internal.dynalink.support.Lookup;
|
import jdk.internal.dynalink.support.Lookup;
|
||||||
|
import jdk.internal.dynalink.support.TypeUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a subset of overloaded methods for a certain method name on a certain class. It can be either a fixarg or
|
* Represents a subset of overloaded methods for a certain method name on a certain class. It can be either a fixarg or
|
||||||
@ -114,13 +115,15 @@ class OverloadedMethod {
|
|||||||
OverloadedMethod(List<MethodHandle> methodHandles, OverloadedDynamicMethod parent, MethodType callSiteType,
|
OverloadedMethod(List<MethodHandle> methodHandles, OverloadedDynamicMethod parent, MethodType callSiteType,
|
||||||
LinkerServices linkerServices) {
|
LinkerServices linkerServices) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.callSiteType = callSiteType;
|
final Class<?> commonRetType = getCommonReturnType(methodHandles);
|
||||||
|
this.callSiteType = callSiteType.changeReturnType(commonRetType);
|
||||||
this.linkerServices = linkerServices;
|
this.linkerServices = linkerServices;
|
||||||
|
|
||||||
fixArgMethods = new ArrayList<>(methodHandles.size());
|
fixArgMethods = new ArrayList<>(methodHandles.size());
|
||||||
varArgMethods = new ArrayList<>(methodHandles.size());
|
varArgMethods = new ArrayList<>(methodHandles.size());
|
||||||
final int argNum = callSiteType.parameterCount();
|
final int argNum = callSiteType.parameterCount();
|
||||||
for(MethodHandle mh: methodHandles) {
|
for(MethodHandle mh: methodHandles) {
|
||||||
|
mh = mh.asType(mh.type().changeReturnType(commonRetType));
|
||||||
if(mh.isVarargsCollector()) {
|
if(mh.isVarargsCollector()) {
|
||||||
final MethodHandle asFixed = mh.asFixedArity();
|
final MethodHandle asFixed = mh.asFixedArity();
|
||||||
if(argNum == asFixed.type().parameterCount()) {
|
if(argNum == asFixed.type().parameterCount()) {
|
||||||
@ -137,7 +140,7 @@ class OverloadedMethod {
|
|||||||
final MethodHandle bound = SELECT_METHOD.bindTo(this);
|
final MethodHandle bound = SELECT_METHOD.bindTo(this);
|
||||||
final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
|
final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
|
||||||
callSiteType.changeReturnType(MethodHandle.class));
|
callSiteType.changeReturnType(MethodHandle.class));
|
||||||
invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(callSiteType), collecting);
|
invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(this.callSiteType), collecting);
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodHandle getInvoker() {
|
MethodHandle getInvoker() {
|
||||||
@ -262,4 +265,13 @@ class OverloadedMethod {
|
|||||||
b.append(classes[l - 1].getComponentType().getCanonicalName()).append("...");
|
b.append(classes[l - 1].getComponentType().getCanonicalName()).append("...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Class<?> getCommonReturnType(List<MethodHandle> methodHandles) {
|
||||||
|
final Iterator<MethodHandle> it = methodHandles.iterator();
|
||||||
|
Class<?> retType = it.next().type().returnType();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
retType = TypeUtilities.getCommonLosslessConversionType(retType, it.next().type().returnType());
|
||||||
|
}
|
||||||
|
return retType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,9 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
|||||||
/**
|
/**
|
||||||
* Given a method handle and a call site type, adapts the method handle to the call site type. Performs type
|
* Given a method handle and a call site type, adapts the method handle to the call site type. Performs type
|
||||||
* conversions as needed using the specified linker services, and in case that the method handle is a vararg
|
* conversions as needed using the specified linker services, and in case that the method handle is a vararg
|
||||||
* collector, matches it to the arity of the call site.
|
* collector, matches it to the arity of the call site. The type of the return value is only changed if it can be
|
||||||
|
* converted using a conversion that loses neither precision nor magnitude, see
|
||||||
|
* {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
|
||||||
* @param target the method handle to adapt
|
* @param target the method handle to adapt
|
||||||
* @param callSiteType the type of the call site
|
* @param callSiteType the type of the call site
|
||||||
* @param linkerServices the linker services used for type conversions
|
* @param linkerServices the linker services used for type conversions
|
||||||
@ -286,7 +288,7 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
|||||||
|
|
||||||
private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
|
private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
|
||||||
final LinkerServices linkerServices, final MethodType callSiteType) {
|
final LinkerServices linkerServices, final MethodType callSiteType) {
|
||||||
return linkerServices.asType(sizedMethod, callSiteType);
|
return linkerServices.asTypeLosslessReturn(sizedMethod, callSiteType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean typeMatchesDescription(String paramTypes, MethodType type) {
|
private static boolean typeMatchesDescription(String paramTypes, MethodType type) {
|
||||||
|
@ -90,7 +90,9 @@ import java.lang.invoke.SwitchPoint;
|
|||||||
import java.lang.invoke.WrongMethodTypeException;
|
import java.lang.invoke.WrongMethodTypeException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||||
|
import jdk.internal.dynalink.support.CatchExceptionCombinator;
|
||||||
import jdk.internal.dynalink.support.Guards;
|
import jdk.internal.dynalink.support.Guards;
|
||||||
|
import jdk.nashorn.internal.runtime.options.Options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a conditionally valid method handle. It is an immutable triple of an invocation method handle, a guard
|
* Represents a conditionally valid method handle. It is an immutable triple of an invocation method handle, a guard
|
||||||
@ -102,10 +104,23 @@ import jdk.internal.dynalink.support.Guards;
|
|||||||
* @author Attila Szegedi
|
* @author Attila Szegedi
|
||||||
*/
|
*/
|
||||||
public class GuardedInvocation {
|
public class GuardedInvocation {
|
||||||
|
private static final boolean USE_FAST_REWRITE = Options.getBooleanProperty("nashorn.fastrewrite");
|
||||||
|
|
||||||
private final MethodHandle invocation;
|
private final MethodHandle invocation;
|
||||||
private final MethodHandle guard;
|
private final MethodHandle guard;
|
||||||
|
private final Class<? extends Throwable> exception;
|
||||||
private final SwitchPoint switchPoint;
|
private final SwitchPoint switchPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new guarded invocation. This invocation is unconditional as it has no invalidations.
|
||||||
|
*
|
||||||
|
* @param invocation the method handle representing the invocation. Must not be null.
|
||||||
|
* @throws NullPointerException if invocation is null.
|
||||||
|
*/
|
||||||
|
public GuardedInvocation(MethodHandle invocation) {
|
||||||
|
this(invocation, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new guarded invocation.
|
* Creates a new guarded invocation.
|
||||||
*
|
*
|
||||||
@ -116,7 +131,18 @@ public class GuardedInvocation {
|
|||||||
* @throws NullPointerException if invocation is null.
|
* @throws NullPointerException if invocation is null.
|
||||||
*/
|
*/
|
||||||
public GuardedInvocation(MethodHandle invocation, MethodHandle guard) {
|
public GuardedInvocation(MethodHandle invocation, MethodHandle guard) {
|
||||||
this(invocation, guard, null);
|
this(invocation, guard, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new guarded invocation.
|
||||||
|
*
|
||||||
|
* @param invocation the method handle representing the invocation. Must not be null.
|
||||||
|
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
|
||||||
|
* @throws NullPointerException if invocation is null.
|
||||||
|
*/
|
||||||
|
public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint) {
|
||||||
|
this(invocation, null, switchPoint, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,25 +156,29 @@ public class GuardedInvocation {
|
|||||||
* @throws NullPointerException if invocation is null.
|
* @throws NullPointerException if invocation is null.
|
||||||
*/
|
*/
|
||||||
public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
|
public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
|
||||||
invocation.getClass(); // NPE check
|
this(invocation, guard, switchPoint, null);
|
||||||
this.invocation = invocation;
|
|
||||||
this.guard = guard;
|
|
||||||
this.switchPoint = switchPoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new guarded invocation.
|
* Creates a new guarded invocation.
|
||||||
*
|
*
|
||||||
* @param invocation the method handle representing the invocation. Must not be null.
|
* @param invocation the method handle representing the invocation. Must not be null.
|
||||||
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
|
|
||||||
* @param guard the method handle representing the guard. Must have the same method type as the invocation, except
|
* @param guard the method handle representing the guard. Must have the same method type as the invocation, except
|
||||||
* it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
|
* it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
|
||||||
* and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
|
* and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
|
||||||
|
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
|
||||||
|
* @param exception the optional exception type that is expected to be thrown by the invocation and that also
|
||||||
|
* invalidates the linkage.
|
||||||
* @throws NullPointerException if invocation is null.
|
* @throws NullPointerException if invocation is null.
|
||||||
*/
|
*/
|
||||||
public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint, MethodHandle guard) {
|
public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint, Class<? extends Throwable> exception) {
|
||||||
this(invocation, guard, switchPoint);
|
invocation.getClass(); // NPE check
|
||||||
|
this.invocation = invocation;
|
||||||
|
this.guard = guard;
|
||||||
|
this.switchPoint = switchPoint;
|
||||||
|
this.exception = exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the invocation method handle.
|
* Returns the invocation method handle.
|
||||||
*
|
*
|
||||||
@ -176,6 +206,15 @@ public class GuardedInvocation {
|
|||||||
return switchPoint;
|
return switchPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exception type that if thrown should be used to invalidate the linkage.
|
||||||
|
*
|
||||||
|
* @return the exception type that if thrown should be used to invalidate the linkage. Can be null.
|
||||||
|
*/
|
||||||
|
public Class<? extends Throwable> getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
|
* Returns true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
|
||||||
* @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
|
* @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
|
||||||
@ -206,7 +245,7 @@ public class GuardedInvocation {
|
|||||||
* @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
|
* @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
|
||||||
*/
|
*/
|
||||||
public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) {
|
public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) {
|
||||||
return new GuardedInvocation(newInvocation, newGuard, switchPoint);
|
return new GuardedInvocation(newInvocation, newGuard, switchPoint, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) {
|
private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) {
|
||||||
@ -240,6 +279,20 @@ public class GuardedInvocation {
|
|||||||
Guards.asType(linkerServices, guard, newType));
|
Guards.asType(linkerServices, guard, newType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the type of the invocation, as if {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)} was
|
||||||
|
* applied to its invocation and {@link LinkerServices#asType(MethodHandle, MethodType)} applied to its guard, if it
|
||||||
|
* has one (with return type changed to boolean, and parameter count potentially truncated for the guard). If the
|
||||||
|
* invocation doesn't change its type, returns this object.
|
||||||
|
* @param linkerServices the linker services to use for the conversion
|
||||||
|
* @param newType the new type of the invocation.
|
||||||
|
* @return a guarded invocation with the new type applied to it.
|
||||||
|
*/
|
||||||
|
public GuardedInvocation asTypeSafeReturn(LinkerServices linkerServices, MethodType newType) {
|
||||||
|
return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null :
|
||||||
|
Guards.asType(linkerServices, guard, newType));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
|
* Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
|
||||||
* and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
|
* and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
|
||||||
@ -303,9 +356,17 @@ public class GuardedInvocation {
|
|||||||
public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) {
|
public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) {
|
||||||
final MethodHandle guarded =
|
final MethodHandle guarded =
|
||||||
guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
|
guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
|
||||||
return switchPoint == null ? guarded : switchPoint.guardWithTest(guarded, switchpointFallback);
|
final MethodHandle catchGuarded = exception == null ? guarded : catchException(guarded, exception,
|
||||||
|
MethodHandles.dropArguments(guardFallback, 0, exception));
|
||||||
|
return switchPoint == null ? catchGuarded : switchPoint.guardWithTest(catchGuarded, switchpointFallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
|
||||||
|
if(USE_FAST_REWRITE) {
|
||||||
|
return CatchExceptionCombinator.catchException(target, exType, handler);
|
||||||
|
}
|
||||||
|
return MethodHandles.catchException(target, exType, handler);
|
||||||
|
}
|
||||||
private static void assertType(MethodHandle mh, MethodType type) {
|
private static void assertType(MethodHandle mh, MethodType type) {
|
||||||
if(!mh.type().equals(type)) {
|
if(!mh.type().equals(type)) {
|
||||||
throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
|
throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
|
||||||
|
@ -101,10 +101,16 @@ public interface GuardingDynamicLinker {
|
|||||||
* @return a guarded invocation with a method handle suitable for the arguments, as well as a guard condition that
|
* @return a guarded invocation with a method handle suitable for the arguments, as well as a guard condition that
|
||||||
* if fails should trigger relinking. Must return null if it can't resolve the invocation. If the returned
|
* if fails should trigger relinking. Must return null if it can't resolve the invocation. If the returned
|
||||||
* invocation is unconditional (which is actually quite rare), the guard in the return value can be null. The
|
* invocation is unconditional (which is actually quite rare), the guard in the return value can be null. The
|
||||||
* invocation can also have a switch point for asynchronous invalidation of the linkage. If the linker does not
|
* invocation can also have a switch point for asynchronous invalidation of the linkage, as well as a
|
||||||
* recognize any native language runtime contexts in arguments, or does recognize its own, but receives a call site
|
* {@link Throwable} subclass that describes an expected exception condition that also triggers relinking (often it
|
||||||
* descriptor without its recognized context in the arguments, it should invoke
|
* is faster to rely on an infrequent but expected {@link ClassCastException} than on an always evaluated
|
||||||
* {@link LinkRequest#withoutRuntimeContext()} and link for that.
|
* {@code instanceof} guard). If the linker does not recognize any native language runtime contexts in arguments, or
|
||||||
|
* does recognize its own, but receives a call site descriptor without its recognized context in the arguments, it
|
||||||
|
* should invoke {@link LinkRequest#withoutRuntimeContext()} and link for that. While the linker must produce an
|
||||||
|
* invocation with parameter types matching those in the call site descriptor of the link request, it should not try
|
||||||
|
* to match the return type expected at the call site except when it can do it with only the conversions that lose
|
||||||
|
* neither precision nor magnitude, see {@link LinkerServices#asTypeLosslessReturn(java.lang.invoke.MethodHandle,
|
||||||
|
* java.lang.invoke.MethodType)}.
|
||||||
* @throws Exception if the operation fails for whatever reason
|
* @throws Exception if the operation fails for whatever reason
|
||||||
*/
|
*/
|
||||||
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices)
|
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices)
|
||||||
|
@ -100,6 +100,17 @@ public interface LinkRequest {
|
|||||||
*/
|
*/
|
||||||
public CallSiteDescriptor getCallSiteDescriptor();
|
public CallSiteDescriptor getCallSiteDescriptor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the call site token for the call site being linked. This token is an opaque object that is guaranteed to
|
||||||
|
* have different identity for different call sites, and is also guaranteed to not become weakly reachable before
|
||||||
|
* the call site does and to become weakly reachable some time after the call site does. This makes it ideal as a
|
||||||
|
* candidate for a key in a weak hash map in which a linker might want to keep per-call site linking state (usually
|
||||||
|
* profiling information).
|
||||||
|
*
|
||||||
|
* @return the call site token for the call site being linked.
|
||||||
|
*/
|
||||||
|
public Object getCallSiteToken();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the arguments for the invocation being linked. The returned array is a clone; modifications to it won't
|
* Returns the arguments for the invocation being linked. The returned array is a clone; modifications to it won't
|
||||||
* affect the arguments in this request.
|
* affect the arguments in this request.
|
||||||
|
@ -87,7 +87,9 @@ import java.lang.invoke.MethodHandle;
|
|||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import jdk.internal.dynalink.DynamicLinker;
|
import jdk.internal.dynalink.DynamicLinker;
|
||||||
|
import jdk.internal.dynalink.DynamicLinkerFactory;
|
||||||
import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
|
import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
|
||||||
|
import jdk.internal.dynalink.support.TypeUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for services provided to {@link GuardingDynamicLinker} instances by the {@link DynamicLinker} that owns
|
* Interface for services provided to {@link GuardingDynamicLinker} instances by the {@link DynamicLinker} that owns
|
||||||
@ -103,17 +105,33 @@ public interface LinkerServices {
|
|||||||
* parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
|
* parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
|
||||||
* wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
|
* wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
|
||||||
* it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
|
* it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
|
||||||
* provided by {@link GuardingTypeConverterFactory} implementations. It doesn't use language-specific conversions on
|
* provided by {@link GuardingTypeConverterFactory} implementations.
|
||||||
* the return type.
|
|
||||||
*
|
*
|
||||||
* @param handle target method handle
|
* @param handle target method handle
|
||||||
* @param fromType the types of source arguments
|
* @param fromType the types of source arguments
|
||||||
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
|
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
|
||||||
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
|
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}, and
|
||||||
* {@link GuardingTypeConverterFactory} produced type converters as filters.
|
* {@link MethodHandles#filterReturnValue(MethodHandle, MethodHandle)} with
|
||||||
|
* {@link GuardingTypeConverterFactory}-produced type converters as filters.
|
||||||
*/
|
*/
|
||||||
public MethodHandle asType(MethodHandle handle, MethodType fromType);
|
public MethodHandle asType(MethodHandle handle, MethodType fromType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link #asType(MethodHandle, MethodType)} except it only converts the return type of the method handle
|
||||||
|
* when it can be done using a conversion that loses neither precision nor magnitude, otherwise it leaves it
|
||||||
|
* unchanged. The idea is that other conversions should not be performed by individual linkers, but instead the
|
||||||
|
* {@link DynamicLinkerFactory#setPrelinkFilter(jdk.internal.dynalink.GuardedInvocationFilter) pre-link filter of
|
||||||
|
* the dynamic linker} should implement the strategy of dealing with potentially lossy return type conversions in a
|
||||||
|
* manner specific to the language runtime.
|
||||||
|
*
|
||||||
|
* @param handle target method handle
|
||||||
|
* @param fromType the types of source arguments
|
||||||
|
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)}, and
|
||||||
|
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
|
||||||
|
* {@link GuardingTypeConverterFactory}-produced type converters as filters.
|
||||||
|
*/
|
||||||
|
public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
|
* Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
|
||||||
* case it will return an identity conversion (that might fail for some values at runtime). You rarely need to use
|
* case it will return an identity conversion (that might fail for some values at runtime). You rarely need to use
|
||||||
@ -161,4 +179,23 @@ public interface LinkerServices {
|
|||||||
* conversion.
|
* conversion.
|
||||||
*/
|
*/
|
||||||
public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);
|
public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default
|
||||||
|
* implementation. Since we can't do that, we extract common default implementations into this static class.
|
||||||
|
*/
|
||||||
|
public static class Implementation {
|
||||||
|
/**
|
||||||
|
* Default implementation for {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
|
||||||
|
* @param linkerServices the linker services that delegates to this implementation
|
||||||
|
* @param handle the passed handle
|
||||||
|
* @param fromType the passed type
|
||||||
|
* @return the converted method handle, as per the {@code asTypeSafeReturn} semantics.
|
||||||
|
*/
|
||||||
|
public static MethodHandle asTypeLosslessReturn(LinkerServices linkerServices, MethodHandle handle, MethodType fromType) {
|
||||||
|
final Class<?> handleReturnType = handle.type().returnType();
|
||||||
|
return linkerServices.asType(handle, TypeUtilities.isConvertibleWithoutLoss(handleReturnType, fromType.returnType()) ?
|
||||||
|
fromType : fromType.changeReturnType(handleReturnType));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,180 @@
|
|||||||
|
package jdk.internal.dynalink.support;
|
||||||
|
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||||
|
import jdk.internal.org.objectweb.asm.Label;
|
||||||
|
import jdk.internal.org.objectweb.asm.Type;
|
||||||
|
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
|
||||||
|
import jdk.nashorn.internal.runtime.RewriteException;
|
||||||
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates method handles that combine an invocation and a handler for a {@link RewriteException}. Always immediately
|
||||||
|
* generates compiled bytecode.
|
||||||
|
*/
|
||||||
|
public class CatchExceptionCombinator {
|
||||||
|
static {
|
||||||
|
System.err.println("*** Running with fast catch combinator handler ***");
|
||||||
|
}
|
||||||
|
private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
|
||||||
|
private static final String METHOD_HANDLE_TYPE_NAME = METHOD_HANDLE_TYPE.getInternalName();
|
||||||
|
private static final String OBJECT_TYPE_NAME = Type.getInternalName(Object.class);
|
||||||
|
|
||||||
|
private static final String HANDLER_TYPE_NAME = "java.lang.invoke.CatchExceptionCombinator$MH";
|
||||||
|
private static final String INVOKE_METHOD_NAME = "invoke";
|
||||||
|
|
||||||
|
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||||
|
|
||||||
|
private static final ConcurrentMap<CombinatorParameters, ClassTemplate> handlerClassBytes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static final class CombinatorParameters {
|
||||||
|
final MethodType targetType;
|
||||||
|
final Class<? extends Throwable> exType;
|
||||||
|
final MethodType handlerType;
|
||||||
|
|
||||||
|
CombinatorParameters(final MethodType targetType, final Class<? extends Throwable> exType, MethodType handlerType) {
|
||||||
|
this.targetType = targetType;
|
||||||
|
this.exType = exType;
|
||||||
|
this.handlerType = handlerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if(obj instanceof CombinatorParameters) {
|
||||||
|
final CombinatorParameters p = (CombinatorParameters)obj;
|
||||||
|
return targetType.equals(p.targetType) && exType.equals(p.exType) && handlerType.equals(p.handlerType);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return targetType.hashCode() ^ exType.hashCode() ^ handlerType.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Catch exception - create combinator
|
||||||
|
* @param target target
|
||||||
|
* @param exType type to check for
|
||||||
|
* @param handler catch handler
|
||||||
|
* @return target wrapped in catch handler
|
||||||
|
*/
|
||||||
|
public static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
|
||||||
|
final MethodType targetType = target.type();
|
||||||
|
final MethodType handlerType = handler.type();
|
||||||
|
|
||||||
|
final ClassTemplate classTemplate = handlerClassBytes.computeIfAbsent(
|
||||||
|
new CombinatorParameters(targetType, exType, handlerType), new Function<CombinatorParameters, ClassTemplate>() {
|
||||||
|
@Override
|
||||||
|
public ClassTemplate apply(final CombinatorParameters parameters) {
|
||||||
|
return generateClassTemplate(parameters);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return classTemplate.instantiate(target, handler, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClassTemplate {
|
||||||
|
final byte[] bytes;
|
||||||
|
final int target_cp_index;
|
||||||
|
final int handler_cp_index;
|
||||||
|
final int cp_size;
|
||||||
|
|
||||||
|
ClassTemplate(final byte[] bytes, final int target_cp_index, final int handler_cp_index, final int cp_size) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
this.target_cp_index = target_cp_index;
|
||||||
|
this.handler_cp_index = handler_cp_index;
|
||||||
|
this.cp_size = cp_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodHandle instantiate(final MethodHandle target, final MethodHandle handler, final MethodType type) {
|
||||||
|
final Object[] cpPatch = new Object[cp_size];
|
||||||
|
cpPatch[target_cp_index] = target;
|
||||||
|
cpPatch[handler_cp_index] = handler;
|
||||||
|
final Class<?> handlerClass = UNSAFE.defineAnonymousClass(CatchExceptionCombinator.class, bytes, cpPatch);
|
||||||
|
try {
|
||||||
|
return MethodHandles.lookup().findStatic(handlerClass, INVOKE_METHOD_NAME, type);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClassTemplate generateClassTemplate(final CombinatorParameters combinatorParameters) {
|
||||||
|
final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
w.visit(V1_7, ACC_PUBLIC | ACC_SUPER, HANDLER_TYPE_NAME, null, OBJECT_TYPE_NAME, null);
|
||||||
|
|
||||||
|
final MethodType targetType = combinatorParameters.targetType;
|
||||||
|
final Class<? extends Throwable> exType = combinatorParameters.exType;
|
||||||
|
final String methodDescriptor = targetType.toMethodDescriptorString();
|
||||||
|
final Class<?> returnType = targetType.returnType();
|
||||||
|
final MethodType handlerType = combinatorParameters.handlerType;
|
||||||
|
|
||||||
|
// NOTE: must use strings as placeholders in the constant pool, even if we'll be replacing them with method handles.
|
||||||
|
final String targetPlaceholder = "T_PLACEHOLDER";
|
||||||
|
final String handlerPlaceholder = "H_PLACEHOLDER";
|
||||||
|
final int target_cp_index = w.newConst(targetPlaceholder);
|
||||||
|
final int handler_cp_index = w.newConst(handlerPlaceholder);
|
||||||
|
|
||||||
|
final InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC | ACC_STATIC, INVOKE_METHOD_NAME, methodDescriptor, null, null));
|
||||||
|
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
|
||||||
|
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
|
||||||
|
mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
|
||||||
|
|
||||||
|
mv.visitCode();
|
||||||
|
|
||||||
|
final Label _try = new Label();
|
||||||
|
final Label _end_try= new Label();
|
||||||
|
|
||||||
|
mv.visitLabel(_try);
|
||||||
|
// Invoke
|
||||||
|
mv.aconst(targetPlaceholder);
|
||||||
|
mv.checkcast(METHOD_HANDLE_TYPE);
|
||||||
|
final Class<?>[] paramTypes = targetType.parameterArray();
|
||||||
|
for(int i = 0, slot = 0; i < paramTypes.length; ++i) {
|
||||||
|
final Type paramType = Type.getType(paramTypes[i]);
|
||||||
|
mv.load(slot, paramType);
|
||||||
|
slot += paramType.getSize();
|
||||||
|
}
|
||||||
|
generateInvokeBasic(mv, methodDescriptor);
|
||||||
|
final Type asmReturnType = Type.getType(returnType);
|
||||||
|
mv.areturn(asmReturnType);
|
||||||
|
|
||||||
|
mv.visitTryCatchBlock(_try, _end_try, _end_try, Type.getInternalName(exType));
|
||||||
|
mv.visitLabel(_end_try);
|
||||||
|
// Handle exception
|
||||||
|
mv.aconst(handlerPlaceholder);
|
||||||
|
mv.checkcast(METHOD_HANDLE_TYPE);
|
||||||
|
mv.swap();
|
||||||
|
final Class<?>[] handlerParamTypes = handlerType.parameterArray();
|
||||||
|
for(int i = 1, slot = 0; i < handlerParamTypes.length; ++i) {
|
||||||
|
final Type paramType = Type.getType(handlerParamTypes[i]);
|
||||||
|
mv.load(slot, paramType);
|
||||||
|
slot += paramType.getSize();
|
||||||
|
}
|
||||||
|
generateInvokeBasic(mv, handlerType.toMethodDescriptorString());
|
||||||
|
mv.areturn(asmReturnType);
|
||||||
|
|
||||||
|
mv.visitMaxs(0, 0);
|
||||||
|
mv.visitEnd();
|
||||||
|
|
||||||
|
w.visitEnd();
|
||||||
|
final byte[] bytes = w.toByteArray();
|
||||||
|
final int cp_size = (((bytes[8] & 0xFF) << 8) | (bytes[9] & 0xFF));
|
||||||
|
return new ClassTemplate(bytes, target_cp_index, handler_cp_index, cp_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void generateInvokeBasic(final InstructionAdapter mv, final String methodDesc) {
|
||||||
|
mv.invokevirtual(METHOD_HANDLE_TYPE_NAME, "invokeBasic", methodDesc, false);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is available under and governed by the GNU General Public
|
||||||
|
* License version 2 only, as published by the Free Software Foundation.
|
||||||
|
* However, the following notice accompanied the original version of this
|
||||||
|
* file, and Oracle licenses the original version of this file under the BSD
|
||||||
|
* license:
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Copyright 2009-2013 Attila Szegedi
|
||||||
|
|
||||||
|
Licensed under both the Apache License, Version 2.0 (the "Apache License")
|
||||||
|
and the BSD License (the "BSD License"), with licensee being free to
|
||||||
|
choose either of the two at their discretion.
|
||||||
|
|
||||||
|
You may not use this file except in compliance with either the Apache
|
||||||
|
License or the BSD License.
|
||||||
|
|
||||||
|
If you choose to use this file in compliance with the Apache License, the
|
||||||
|
following notice applies to you:
|
||||||
|
|
||||||
|
You may obtain a copy of the Apache License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied. See the License for the specific language governing
|
||||||
|
permissions and limitations under the License.
|
||||||
|
|
||||||
|
If you choose to use this file in compliance with the BSD License, the
|
||||||
|
following notice applies to you:
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the copyright holder nor the names of
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
|
||||||
|
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.internal.dynalink.support;
|
||||||
|
|
||||||
|
import jdk.internal.dynalink.GuardedInvocationFilter;
|
||||||
|
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||||
|
import jdk.internal.dynalink.linker.LinkRequest;
|
||||||
|
import jdk.internal.dynalink.linker.LinkerServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default filter for guarded invocation pre link filtering
|
||||||
|
*/
|
||||||
|
public class DefaultPrelinkFilter implements GuardedInvocationFilter {
|
||||||
|
@Override
|
||||||
|
public GuardedInvocation filter(GuardedInvocation inv, LinkRequest request, LinkerServices linkerServices) {
|
||||||
|
return inv.asType(linkerServices, request.getCallSiteDescriptor().getMethodType());
|
||||||
|
}
|
||||||
|
}
|
@ -95,6 +95,7 @@ import jdk.internal.dynalink.linker.LinkRequest;
|
|||||||
public class LinkRequestImpl implements LinkRequest {
|
public class LinkRequestImpl implements LinkRequest {
|
||||||
|
|
||||||
private final CallSiteDescriptor callSiteDescriptor;
|
private final CallSiteDescriptor callSiteDescriptor;
|
||||||
|
private final Object callSiteToken;
|
||||||
private final Object[] arguments;
|
private final Object[] arguments;
|
||||||
private final boolean callSiteUnstable;
|
private final boolean callSiteUnstable;
|
||||||
|
|
||||||
@ -102,11 +103,13 @@ public class LinkRequestImpl implements LinkRequest {
|
|||||||
* Creates a new link request.
|
* Creates a new link request.
|
||||||
*
|
*
|
||||||
* @param callSiteDescriptor the descriptor for the call site being linked
|
* @param callSiteDescriptor the descriptor for the call site being linked
|
||||||
|
* @param callSiteToken the opaque token for the call site being linked.
|
||||||
* @param callSiteUnstable true if the call site being linked is considered unstable
|
* @param callSiteUnstable true if the call site being linked is considered unstable
|
||||||
* @param arguments the arguments for the invocation
|
* @param arguments the arguments for the invocation
|
||||||
*/
|
*/
|
||||||
public LinkRequestImpl(CallSiteDescriptor callSiteDescriptor, boolean callSiteUnstable, Object... arguments) {
|
public LinkRequestImpl(CallSiteDescriptor callSiteDescriptor, Object callSiteToken, boolean callSiteUnstable, Object... arguments) {
|
||||||
this.callSiteDescriptor = callSiteDescriptor;
|
this.callSiteDescriptor = callSiteDescriptor;
|
||||||
|
this.callSiteToken = callSiteToken;
|
||||||
this.callSiteUnstable = callSiteUnstable;
|
this.callSiteUnstable = callSiteUnstable;
|
||||||
this.arguments = arguments;
|
this.arguments = arguments;
|
||||||
}
|
}
|
||||||
@ -126,6 +129,11 @@ public class LinkRequestImpl implements LinkRequest {
|
|||||||
return callSiteDescriptor;
|
return callSiteDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCallSiteToken() {
|
||||||
|
return callSiteToken;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCallSiteUnstable() {
|
public boolean isCallSiteUnstable() {
|
||||||
return callSiteUnstable;
|
return callSiteUnstable;
|
||||||
@ -138,6 +146,6 @@ public class LinkRequestImpl implements LinkRequest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LinkRequest replaceArguments(CallSiteDescriptor newCallSiteDescriptor, Object[] newArguments) {
|
public LinkRequest replaceArguments(CallSiteDescriptor newCallSiteDescriptor, Object[] newArguments) {
|
||||||
return new LinkRequestImpl(newCallSiteDescriptor, callSiteUnstable, newArguments);
|
return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, callSiteUnstable, newArguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,11 @@ public class LinkerServicesImpl implements LinkerServices {
|
|||||||
return typeConverterFactory.asType(handle, fromType);
|
return typeConverterFactory.asType(handle, fromType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType) {
|
||||||
|
return Implementation.asTypeLosslessReturn(this, handle, fromType);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) {
|
public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) {
|
||||||
return typeConverterFactory.getTypeConverter(sourceType, targetType);
|
return typeConverterFactory.getTypeConverter(sourceType, targetType);
|
||||||
|
@ -101,15 +101,16 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
|
|||||||
* Creates a new link request.
|
* Creates a new link request.
|
||||||
*
|
*
|
||||||
* @param callSiteDescriptor the descriptor for the call site being linked
|
* @param callSiteDescriptor the descriptor for the call site being linked
|
||||||
|
* @param callSiteToken the opaque token for the call site being linked.
|
||||||
* @param arguments the arguments for the invocation
|
* @param arguments the arguments for the invocation
|
||||||
* @param callSiteUnstable true if the call site being linked is considered unstable
|
* @param callSiteUnstable true if the call site being linked is considered unstable
|
||||||
* @param runtimeContextArgCount the number of the leading arguments on the stack that represent the language
|
* @param runtimeContextArgCount the number of the leading arguments on the stack that represent the language
|
||||||
* runtime specific context arguments.
|
* runtime specific context arguments.
|
||||||
* @throws IllegalArgumentException if runtimeContextArgCount is less than 1.
|
* @throws IllegalArgumentException if runtimeContextArgCount is less than 1.
|
||||||
*/
|
*/
|
||||||
public RuntimeContextLinkRequestImpl(CallSiteDescriptor callSiteDescriptor, boolean callSiteUnstable,
|
public RuntimeContextLinkRequestImpl(CallSiteDescriptor callSiteDescriptor, Object callSiteToken,
|
||||||
Object[] arguments, int runtimeContextArgCount) {
|
boolean callSiteUnstable, Object[] arguments, int runtimeContextArgCount) {
|
||||||
super(callSiteDescriptor, callSiteUnstable, arguments);
|
super(callSiteDescriptor, callSiteToken, callSiteUnstable, arguments);
|
||||||
if(runtimeContextArgCount < 1) {
|
if(runtimeContextArgCount < 1) {
|
||||||
throw new IllegalArgumentException("runtimeContextArgCount < 1");
|
throw new IllegalArgumentException("runtimeContextArgCount < 1");
|
||||||
}
|
}
|
||||||
@ -121,14 +122,14 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
|
|||||||
if(contextStrippedRequest == null) {
|
if(contextStrippedRequest == null) {
|
||||||
contextStrippedRequest =
|
contextStrippedRequest =
|
||||||
new LinkRequestImpl(CallSiteDescriptorFactory.dropParameterTypes(getCallSiteDescriptor(), 1,
|
new LinkRequestImpl(CallSiteDescriptorFactory.dropParameterTypes(getCallSiteDescriptor(), 1,
|
||||||
runtimeContextArgCount + 1), isCallSiteUnstable(), getTruncatedArguments());
|
runtimeContextArgCount + 1), getCallSiteToken(), isCallSiteUnstable(), getTruncatedArguments());
|
||||||
}
|
}
|
||||||
return contextStrippedRequest;
|
return contextStrippedRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LinkRequest replaceArguments(CallSiteDescriptor callSiteDescriptor, Object[] arguments) {
|
public LinkRequest replaceArguments(CallSiteDescriptor callSiteDescriptor, Object[] arguments) {
|
||||||
return new RuntimeContextLinkRequestImpl(callSiteDescriptor, isCallSiteUnstable(), arguments,
|
return new RuntimeContextLinkRequestImpl(callSiteDescriptor, getCallSiteToken(), isCallSiteUnstable(), arguments,
|
||||||
runtimeContextArgCount);
|
runtimeContextArgCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,38 +106,49 @@ public class TypeUtilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given two types represented by c1 and c2, returns a type that is their most specific common superclass or
|
* Given two types represented by c1 and c2, returns a type that is their most specific common supertype for
|
||||||
* superinterface.
|
* purposes of lossless conversions.
|
||||||
*
|
*
|
||||||
* @param c1 one type
|
* @param c1 one type
|
||||||
* @param c2 another type
|
* @param c2 another type
|
||||||
* @return their most common superclass or superinterface. If they have several unrelated superinterfaces as their
|
* @return their most common superclass or superinterface for purposes of lossless conversions. If they have several
|
||||||
* most specific common type, or the types themselves are completely unrelated interfaces, {@link java.lang.Object}
|
* unrelated superinterfaces as their most specific common type, or the types themselves are completely
|
||||||
* is returned.
|
* unrelated interfaces, {@link java.lang.Object} is returned.
|
||||||
*/
|
*/
|
||||||
public static Class<?> getMostSpecificCommonType(Class<?> c1, Class<?> c2) {
|
public static Class<?> getCommonLosslessConversionType(Class<?> c1, Class<?> c2) {
|
||||||
if(c1 == c2) {
|
if(c1 == c2) {
|
||||||
return c1;
|
return c1;
|
||||||
|
} else if(isConvertibleWithoutLoss(c2, c1)) {
|
||||||
|
return c1;
|
||||||
|
} else if(isConvertibleWithoutLoss(c1, c2)) {
|
||||||
|
return c2;
|
||||||
}
|
}
|
||||||
Class<?> c3 = c2;
|
if(c1 == void.class) {
|
||||||
if(c3.isPrimitive()) {
|
return c2;
|
||||||
if(c3 == Byte.TYPE)
|
} else if(c2 == void.class) {
|
||||||
c3 = Byte.class;
|
return c1;
|
||||||
else if(c3 == Short.TYPE)
|
|
||||||
c3 = Short.class;
|
|
||||||
else if(c3 == Character.TYPE)
|
|
||||||
c3 = Character.class;
|
|
||||||
else if(c3 == Integer.TYPE)
|
|
||||||
c3 = Integer.class;
|
|
||||||
else if(c3 == Float.TYPE)
|
|
||||||
c3 = Float.class;
|
|
||||||
else if(c3 == Long.TYPE)
|
|
||||||
c3 = Long.class;
|
|
||||||
else if(c3 == Double.TYPE)
|
|
||||||
c3 = Double.class;
|
|
||||||
}
|
}
|
||||||
Set<Class<?>> a1 = getAssignables(c1, c3);
|
if(c1.isPrimitive() && c2.isPrimitive()) {
|
||||||
Set<Class<?>> a2 = getAssignables(c3, c1);
|
if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
|
||||||
|
// byte + char = int
|
||||||
|
return int.class;
|
||||||
|
} else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
|
||||||
|
// short + char = int
|
||||||
|
return int.class;
|
||||||
|
} else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
|
||||||
|
// int + float = double
|
||||||
|
return double.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
|
||||||
|
return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(Class<?> c1, Class<?> c2) {
|
||||||
|
final Class<?> npc1 = c1.isPrimitive() ? getWrapperType(c1) : c1;
|
||||||
|
final Class<?> npc2 = c2.isPrimitive() ? getWrapperType(c2) : c2;
|
||||||
|
Set<Class<?>> a1 = getAssignables(npc1, npc2);
|
||||||
|
Set<Class<?>> a2 = getAssignables(npc2, npc1);
|
||||||
a1.retainAll(a2);
|
a1.retainAll(a2);
|
||||||
if(a1.isEmpty()) {
|
if(a1.isEmpty()) {
|
||||||
// Can happen when at least one of the arguments is an interface,
|
// Can happen when at least one of the arguments is an interface,
|
||||||
@ -168,7 +179,7 @@ public class TypeUtilities {
|
|||||||
max.add(clazz);
|
max.add(clazz);
|
||||||
}
|
}
|
||||||
if(max.size() > 1) {
|
if(max.size() > 1) {
|
||||||
return OBJECT_CLASS;
|
return Object.class;
|
||||||
}
|
}
|
||||||
return max.get(0);
|
return max.get(0);
|
||||||
}
|
}
|
||||||
@ -232,25 +243,60 @@ public class TypeUtilities {
|
|||||||
* {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
|
* {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
|
||||||
* reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
|
* reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
|
||||||
*
|
*
|
||||||
* @param callSiteType the parameter type at the call site
|
* @param sourceType the type being converted from (call site type for parameter types, method type for return types)
|
||||||
* @param methodType the parameter type in the method declaration
|
* @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
|
||||||
* @return true if callSiteType is method invocation convertible to the methodType.
|
* @return true if source type is method invocation convertible to target type.
|
||||||
*/
|
*/
|
||||||
public static boolean isMethodInvocationConvertible(Class<?> callSiteType, Class<?> methodType) {
|
public static boolean isMethodInvocationConvertible(Class<?> sourceType, Class<?> targetType) {
|
||||||
if(methodType.isAssignableFrom(callSiteType)) {
|
if(targetType.isAssignableFrom(sourceType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(callSiteType.isPrimitive()) {
|
if(sourceType.isPrimitive()) {
|
||||||
if(methodType.isPrimitive()) {
|
if(targetType.isPrimitive()) {
|
||||||
return isProperPrimitiveSubtype(callSiteType, methodType);
|
return isProperPrimitiveSubtype(sourceType, targetType);
|
||||||
}
|
}
|
||||||
// Boxing + widening reference conversion
|
// Boxing + widening reference conversion
|
||||||
return methodType.isAssignableFrom(WRAPPER_TYPES.get(callSiteType));
|
assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
|
||||||
|
return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
|
||||||
}
|
}
|
||||||
if(methodType.isPrimitive()) {
|
if(targetType.isPrimitive()) {
|
||||||
final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(callSiteType);
|
final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
|
||||||
return unboxedCallSiteType != null
|
return unboxedCallSiteType != null
|
||||||
&& (unboxedCallSiteType == methodType || isProperPrimitiveSubtype(unboxedCallSiteType, methodType));
|
&& (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a type can be converted to another without losing any
|
||||||
|
* precision.
|
||||||
|
*
|
||||||
|
* @param sourceType the source type
|
||||||
|
* @param targetType the target type
|
||||||
|
* @return true if lossess conversion is possible
|
||||||
|
*/
|
||||||
|
public static boolean isConvertibleWithoutLoss(Class<?> sourceType, Class<?> targetType) {
|
||||||
|
if(targetType.isAssignableFrom(sourceType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(sourceType.isPrimitive()) {
|
||||||
|
if(sourceType == void.class) {
|
||||||
|
return true; // Void can be losslessly represented by any type
|
||||||
|
}
|
||||||
|
if(targetType.isPrimitive()) {
|
||||||
|
return isProperPrimitiveLosslessSubtype(sourceType, targetType);
|
||||||
|
}
|
||||||
|
// Boxing + widening reference conversion
|
||||||
|
assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
|
||||||
|
return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
|
||||||
|
}
|
||||||
|
if(targetType.isPrimitive()) {
|
||||||
|
if(targetType == void.class) {
|
||||||
|
return false; // Void can't represent anything losslessly
|
||||||
|
}
|
||||||
|
final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
|
||||||
|
return unboxedCallSiteType != null
|
||||||
|
&& (unboxedCallSiteType == targetType || isProperPrimitiveLosslessSubtype(unboxedCallSiteType, targetType));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -266,7 +312,7 @@ public class TypeUtilities {
|
|||||||
*/
|
*/
|
||||||
public static boolean isPotentiallyConvertible(Class<?> callSiteType, Class<?> methodType) {
|
public static boolean isPotentiallyConvertible(Class<?> callSiteType, Class<?> methodType) {
|
||||||
// Widening or narrowing reference conversion
|
// Widening or narrowing reference conversion
|
||||||
if(methodType.isAssignableFrom(callSiteType) || callSiteType.isAssignableFrom(methodType)) {
|
if(areAssignable(callSiteType, methodType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(callSiteType.isPrimitive()) {
|
if(callSiteType.isPrimitive()) {
|
||||||
@ -286,6 +332,16 @@ public class TypeUtilities {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if either of the types is assignable from the other.
|
||||||
|
* @param c1 one of the types
|
||||||
|
* @param c2 another one of the types
|
||||||
|
* @return true if either c1 is assignable from c2 or c2 is assignable from c1.
|
||||||
|
*/
|
||||||
|
public static boolean areAssignable(Class<?> c1, Class<?> c2) {
|
||||||
|
return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict
|
* Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict
|
||||||
* or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows
|
* or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows
|
||||||
@ -353,6 +409,37 @@ public class TypeUtilities {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link #isProperPrimitiveSubtype(Class, Class)}, except it disallows conversions from int and long to
|
||||||
|
* float, and from long to double, as those can lose precision. It also disallows conversion from and to char and
|
||||||
|
* anything else (similar to boolean) as char is not meant to be an arithmetic type.
|
||||||
|
* @param subType the supposed subtype
|
||||||
|
* @param superType the supposed supertype
|
||||||
|
* @return true if subType is a proper (not identical to) primitive subtype of the superType that can be represented
|
||||||
|
* by the supertype without no precision loss.
|
||||||
|
*/
|
||||||
|
private static boolean isProperPrimitiveLosslessSubtype(Class<?> subType, Class<?> superType) {
|
||||||
|
if(superType == boolean.class || subType == boolean.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(superType == char.class || subType == char.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(subType == byte.class) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(subType == short.class) {
|
||||||
|
return superType != byte.class;
|
||||||
|
}
|
||||||
|
if(subType == int.class) {
|
||||||
|
return superType == long.class || superType == double.class;
|
||||||
|
}
|
||||||
|
if(subType == float.class) {
|
||||||
|
return superType == double.class;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
|
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
|
||||||
|
|
||||||
private static Map<Class<?>, Class<?>> createWrapperToPrimitiveTypes() {
|
private static Map<Class<?>, Class<?>> createWrapperToPrimitiveTypes() {
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
package jdk.nashorn.api.scripting;
|
package jdk.nashorn.api.scripting;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -182,7 +182,7 @@ public abstract class NashornException extends RuntimeException {
|
|||||||
if (ECMAErrors.isScriptFrame(st)) {
|
if (ECMAErrors.isScriptFrame(st)) {
|
||||||
final String className = "<" + st.getFileName() + ">";
|
final String className = "<" + st.getFileName() + ">";
|
||||||
String methodName = st.getMethodName();
|
String methodName = st.getMethodName();
|
||||||
if (methodName.equals(CompilerConstants.RUN_SCRIPT.symbolName())) {
|
if (methodName.equals(CompilerConstants.PROGRAM.symbolName())) {
|
||||||
methodName = "<program>";
|
methodName = "<program>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,10 +224,22 @@ public abstract class NashornException extends RuntimeException {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the thrown object. Subclass responsibility
|
||||||
|
* @return thrown object
|
||||||
|
*/
|
||||||
protected Object getThrown() {
|
protected Object getThrown() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization function for ECMA errors. Stores the error
|
||||||
|
* in the ecmaError field of this class. It is only initialized
|
||||||
|
* once, and then reused
|
||||||
|
*
|
||||||
|
* @param global the global
|
||||||
|
* @return initialized exception
|
||||||
|
*/
|
||||||
protected NashornException initEcmaError(final ScriptObject global) {
|
protected NashornException initEcmaError(final ScriptObject global) {
|
||||||
if (ecmaError != null) {
|
if (ecmaError != null) {
|
||||||
return this; // initialized already!
|
return this; // initialized already!
|
||||||
|
@ -61,6 +61,7 @@ import jdk.nashorn.internal.runtime.Context;
|
|||||||
import jdk.nashorn.internal.runtime.ErrorManager;
|
import jdk.nashorn.internal.runtime.ErrorManager;
|
||||||
import jdk.nashorn.internal.runtime.GlobalObject;
|
import jdk.nashorn.internal.runtime.GlobalObject;
|
||||||
import jdk.nashorn.internal.runtime.Property;
|
import jdk.nashorn.internal.runtime.Property;
|
||||||
|
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||||
@ -463,7 +464,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
|||||||
private void setContextVariables(final ScriptObject ctxtGlobal, final ScriptContext ctxt) {
|
private void setContextVariables(final ScriptObject ctxtGlobal, final ScriptContext ctxt) {
|
||||||
// set "context" global variable via contextProperty - because this
|
// set "context" global variable via contextProperty - because this
|
||||||
// property is non-writable
|
// property is non-writable
|
||||||
contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
|
contextProperty.setValue(ctxtGlobal, ctxtGlobal, ctxt, false);
|
||||||
Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
|
Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
|
||||||
if (args == null || args == UNDEFINED) {
|
if (args == null || args == UNDEFINED) {
|
||||||
args = ScriptRuntime.EMPTY_ARRAY;
|
args = ScriptRuntime.EMPTY_ARRAY;
|
||||||
@ -598,6 +599,15 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the global script environment tells us to do optimistic
|
||||||
|
* compilation
|
||||||
|
* @return true if optimistic compilation enabled
|
||||||
|
*/
|
||||||
|
public static boolean isOptimistic() {
|
||||||
|
return ScriptEnvironment.globalOptimistic();
|
||||||
|
}
|
||||||
|
|
||||||
private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException {
|
private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException {
|
||||||
return compileImpl(source, getNashornGlobalFrom(ctxt));
|
return compileImpl(source, getNashornGlobalFrom(ctxt));
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,12 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call member function
|
||||||
|
* @param functionName function name
|
||||||
|
* @param args arguments
|
||||||
|
* @return return value of function
|
||||||
|
*/
|
||||||
public Object callMember(final String functionName, final Object... args) {
|
public Object callMember(final String functionName, final Object... args) {
|
||||||
functionName.getClass(); // null check
|
functionName.getClass(); // null check
|
||||||
final ScriptObject oldGlobal = Context.getGlobal();
|
final ScriptObject oldGlobal = Context.getGlobal();
|
||||||
|
87
nashorn/src/jdk/nashorn/internal/IntDeque.java
Normal file
87
nashorn/src/jdk/nashorn/internal/IntDeque.java
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small helper class for fast int deques
|
||||||
|
*/
|
||||||
|
public class IntDeque {
|
||||||
|
private int[] deque = new int[16];
|
||||||
|
private int nextFree = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push an int value
|
||||||
|
* @param value value
|
||||||
|
*/
|
||||||
|
public void push(final int value) {
|
||||||
|
if (nextFree == deque.length) {
|
||||||
|
final int[] newDeque = new int[nextFree * 2];
|
||||||
|
System.arraycopy(deque, 0, newDeque, 0, nextFree);
|
||||||
|
deque = newDeque;
|
||||||
|
}
|
||||||
|
deque[nextFree++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pop an int value
|
||||||
|
* @return value
|
||||||
|
*/
|
||||||
|
public int pop() {
|
||||||
|
return deque[--nextFree];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peek
|
||||||
|
* @return top value
|
||||||
|
*/
|
||||||
|
public int peek() {
|
||||||
|
return deque[nextFree - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the top element and increment it.
|
||||||
|
* @return top value
|
||||||
|
*/
|
||||||
|
public int getAndIncrement() {
|
||||||
|
return deque[nextFree - 1]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement the value of the top element and return it.
|
||||||
|
* @return decremented top value
|
||||||
|
*/
|
||||||
|
public int decrementAndGet() {
|
||||||
|
return --deque[nextFree - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if deque is empty
|
||||||
|
* @return true if empty
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return nextFree == 0;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -57,7 +57,7 @@ final class BranchOptimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
|
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
|
||||||
final Expression rhs = unaryNode.rhs();
|
final Expression rhs = unaryNode.getExpression();
|
||||||
|
|
||||||
switch (unaryNode.tokenType()) {
|
switch (unaryNode.tokenType()) {
|
||||||
case NOT:
|
case NOT:
|
||||||
|
@ -54,19 +54,23 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.FunctionNode;
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
import jdk.nashorn.internal.ir.SplitNode;
|
import jdk.nashorn.internal.ir.SplitNode;
|
||||||
|
import jdk.nashorn.internal.ir.debug.NashornClassReader;
|
||||||
|
import jdk.nashorn.internal.ir.debug.NashornTextifier;
|
||||||
|
import jdk.nashorn.internal.runtime.Context;
|
||||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||||
|
import jdk.nashorn.internal.runtime.RewriteException;
|
||||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
import jdk.nashorn.internal.runtime.Source;
|
import jdk.nashorn.internal.runtime.Source;
|
||||||
@ -106,6 +110,8 @@ import jdk.nashorn.internal.runtime.Source;
|
|||||||
* @see Compiler
|
* @see Compiler
|
||||||
*/
|
*/
|
||||||
public class ClassEmitter implements Emitter {
|
public class ClassEmitter implements Emitter {
|
||||||
|
/** Default flags for class generation - public class */
|
||||||
|
private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
|
||||||
|
|
||||||
/** Sanity check flag - have we started on a class? */
|
/** Sanity check flag - have we started on a class? */
|
||||||
private boolean classStarted;
|
private boolean classStarted;
|
||||||
@ -125,9 +131,6 @@ public class ClassEmitter implements Emitter {
|
|||||||
/** The script environment */
|
/** The script environment */
|
||||||
protected final ScriptEnvironment env;
|
protected final ScriptEnvironment env;
|
||||||
|
|
||||||
/** Default flags for class generation - oublic class */
|
|
||||||
private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
|
|
||||||
|
|
||||||
/** Compile unit class name. */
|
/** Compile unit class name. */
|
||||||
private String unitClassName;
|
private String unitClassName;
|
||||||
|
|
||||||
@ -376,9 +379,19 @@ public class ClassEmitter implements Emitter {
|
|||||||
static String disassemble(final byte[] bytecode) {
|
static String disassemble(final byte[] bytecode) {
|
||||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
try (final PrintWriter pw = new PrintWriter(baos)) {
|
try (final PrintWriter pw = new PrintWriter(baos)) {
|
||||||
new ClassReader(bytecode).accept(new TraceClassVisitor(pw), 0);
|
final NashornClassReader cr = new NashornClassReader(bytecode);
|
||||||
|
final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
|
||||||
|
@Override
|
||||||
|
public Context run() {
|
||||||
|
return Context.getContext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
|
||||||
|
cr.accept(tcv, 0);
|
||||||
}
|
}
|
||||||
return new String(baos.toByteArray());
|
|
||||||
|
final String str = new String(baos.toByteArray());
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -475,16 +488,39 @@ public class ClassEmitter implements Emitter {
|
|||||||
* @return method emitter to use for weaving this method
|
* @return method emitter to use for weaving this method
|
||||||
*/
|
*/
|
||||||
MethodEmitter method(final FunctionNode functionNode) {
|
MethodEmitter method(final FunctionNode functionNode) {
|
||||||
|
final FunctionSignature signature = new FunctionSignature(functionNode);
|
||||||
final MethodVisitor mv = cw.visitMethod(
|
final MethodVisitor mv = cw.visitMethod(
|
||||||
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
|
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
|
||||||
functionNode.getName(),
|
functionNode.getName(),
|
||||||
new FunctionSignature(functionNode).toString(),
|
signature.toString(),
|
||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
|
|
||||||
return new MethodEmitter(this, mv, functionNode);
|
final MethodEmitter method = new MethodEmitter(this, mv, functionNode);
|
||||||
|
method.setParameterTypes(signature.getParamTypes());
|
||||||
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new method to the class, representing a rest-of version of the function node
|
||||||
|
*
|
||||||
|
* @param functionNode the function node to generate a method for
|
||||||
|
* @return method emitter to use for weaving this method
|
||||||
|
*/
|
||||||
|
MethodEmitter restOfMethod(final FunctionNode functionNode) {
|
||||||
|
final MethodVisitor mv = cw.visitMethod(
|
||||||
|
ACC_PUBLIC | ACC_STATIC,
|
||||||
|
functionNode.getName(),
|
||||||
|
Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
final MethodEmitter method = new MethodEmitter(this, mv, functionNode);
|
||||||
|
method.setParameterTypes(new FunctionSignature(functionNode).getParamTypes());
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start generating the <clinit> method in the class
|
* Start generating the <clinit> method in the class
|
||||||
*
|
*
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -31,13 +31,14 @@ import java.util.Collections;
|
|||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import jdk.nashorn.internal.IntDeque;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.Block;
|
import jdk.nashorn.internal.ir.Block;
|
||||||
import jdk.nashorn.internal.ir.FunctionNode;
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
import jdk.nashorn.internal.ir.LexicalContext;
|
import jdk.nashorn.internal.ir.LexicalContext;
|
||||||
import jdk.nashorn.internal.ir.LexicalContextNode;
|
import jdk.nashorn.internal.ir.LexicalContextNode;
|
||||||
import jdk.nashorn.internal.ir.Node;
|
import jdk.nashorn.internal.ir.Node;
|
||||||
|
import jdk.nashorn.internal.ir.SplitNode;
|
||||||
import jdk.nashorn.internal.ir.Symbol;
|
import jdk.nashorn.internal.ir.Symbol;
|
||||||
import jdk.nashorn.internal.ir.WithNode;
|
import jdk.nashorn.internal.ir.WithNode;
|
||||||
|
|
||||||
@ -63,6 +64,10 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
* i.e. should we keep it or throw it away */
|
* i.e. should we keep it or throw it away */
|
||||||
private final Deque<Node> discard = new ArrayDeque<>();
|
private final Deque<Node> discard = new ArrayDeque<>();
|
||||||
|
|
||||||
|
private final Deque<Map<String, Collection<Label>>> unwarrantedOptimismHandlers = new ArrayDeque<>();
|
||||||
|
private final Deque<StringBuilder> slotTypesDescriptors = new ArrayDeque<>();
|
||||||
|
private final IntDeque splitNodes = new IntDeque();
|
||||||
|
|
||||||
/** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
|
/** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
|
||||||
* currently on the lexical context stack. */
|
* currently on the lexical context stack. */
|
||||||
private int[] nextFreeSlots = new int[16];
|
private int[] nextFreeSlots = new int[16];
|
||||||
@ -75,17 +80,33 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
if (isDynamicScopeBoundary(node)) {
|
if (isDynamicScopeBoundary(node)) {
|
||||||
++dynamicScopeCount;
|
++dynamicScopeCount;
|
||||||
}
|
}
|
||||||
|
if(node instanceof FunctionNode) {
|
||||||
|
splitNodes.push(0);
|
||||||
|
} else if(node instanceof SplitNode) {
|
||||||
|
enterSplitNode();
|
||||||
|
}
|
||||||
return super.push(node);
|
return super.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enterSplitNode() {
|
||||||
|
splitNodes.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void exitSplitNode() {
|
||||||
|
splitNodes.decrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends LexicalContextNode> T pop(final T node) {
|
public <T extends LexicalContextNode> T pop(final T node) {
|
||||||
final T popped = super.pop(node);
|
final T popped = super.pop(node);
|
||||||
if (isDynamicScopeBoundary(popped)) {
|
if (isDynamicScopeBoundary(popped)) {
|
||||||
--dynamicScopeCount;
|
--dynamicScopeCount;
|
||||||
}
|
}
|
||||||
if (node instanceof Block) {
|
if(node instanceof FunctionNode) {
|
||||||
--nextFreeSlotsSize;
|
assert splitNodes.peek() == 0;
|
||||||
|
splitNodes.pop();
|
||||||
|
} else if(node instanceof SplitNode) {
|
||||||
|
exitSplitNode();
|
||||||
}
|
}
|
||||||
return popped;
|
return popped;
|
||||||
}
|
}
|
||||||
@ -108,6 +129,10 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
return dynamicScopeCount > 0;
|
return dynamicScopeCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean inSplitNode() {
|
||||||
|
return !splitNodes.isEmpty() && splitNodes.peek() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isFunctionDynamicScope(FunctionNode fn) {
|
static boolean isFunctionDynamicScope(FunctionNode fn) {
|
||||||
return fn.hasEval() && !fn.isStrict();
|
return fn.hasEval() && !fn.isStrict();
|
||||||
}
|
}
|
||||||
@ -123,6 +148,20 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
return methodEmitters.isEmpty() ? null : methodEmitters.peek();
|
return methodEmitters.isEmpty() ? null : methodEmitters.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pushUnwarrantedOptimismHandlers() {
|
||||||
|
unwarrantedOptimismHandlers.push(new HashMap<String, Collection<Label>>());
|
||||||
|
slotTypesDescriptors.push(new StringBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Collection<Label>> getUnwarrantedOptimismHandlers() {
|
||||||
|
return unwarrantedOptimismHandlers.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Collection<Label>> popUnwarrantedOptimismHandlers() {
|
||||||
|
slotTypesDescriptors.pop();
|
||||||
|
return unwarrantedOptimismHandlers.pop();
|
||||||
|
}
|
||||||
|
|
||||||
CompileUnit pushCompileUnit(final CompileUnit newUnit) {
|
CompileUnit pushCompileUnit(final CompileUnit newUnit) {
|
||||||
compileUnits.push(newUnit);
|
compileUnits.push(newUnit);
|
||||||
return newUnit;
|
return newUnit;
|
||||||
@ -167,33 +206,18 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
* Get a shared static method representing a dynamic scope get access.
|
* Get a shared static method representing a dynamic scope get access.
|
||||||
*
|
*
|
||||||
* @param unit current compile unit
|
* @param unit current compile unit
|
||||||
* @param type the type of the variable
|
|
||||||
* @param symbol the symbol
|
* @param symbol the symbol
|
||||||
|
* @param valueType the type of the variable
|
||||||
* @param flags the callsite flags
|
* @param flags the callsite flags
|
||||||
* @return an object representing a shared scope call
|
* @return an object representing a shared scope call
|
||||||
*/
|
*/
|
||||||
SharedScopeCall getScopeGet(final CompileUnit unit, final Type type, final Symbol symbol, final int flags) {
|
SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags) {
|
||||||
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
|
return getScopeCall(unit, symbol, valueType, valueType, null, flags);
|
||||||
if (scopeCalls.containsKey(scopeCall)) {
|
|
||||||
return scopeCalls.get(scopeCall);
|
|
||||||
}
|
|
||||||
scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall"));
|
|
||||||
scopeCalls.put(scopeCall, scopeCall);
|
|
||||||
return scopeCall;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void nextFreeSlot(final Block block) {
|
void nextFreeSlot(final Block block) {
|
||||||
final boolean isFunctionBody = isFunctionBody();
|
final int nextFreeSlot = isFunctionBody() ? 0 : getUsedSlotCount();
|
||||||
|
|
||||||
final int nextFreeSlot;
|
|
||||||
if (isFunctionBody) {
|
|
||||||
// On entry to function, start with slot 0
|
|
||||||
nextFreeSlot = 0;
|
|
||||||
} else {
|
|
||||||
// Otherwise, continue from previous block's first free slot
|
|
||||||
nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
|
||||||
}
|
|
||||||
if (nextFreeSlotsSize == nextFreeSlots.length) {
|
if (nextFreeSlotsSize == nextFreeSlots.length) {
|
||||||
final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
|
final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
|
||||||
System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
|
System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
|
||||||
@ -202,7 +226,18 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
|
nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int assignSlots(final Block block, final int firstSlot) {
|
int getUsedSlotCount() {
|
||||||
|
return nextFreeSlots[nextFreeSlotsSize - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseBlockSlots(boolean optimistic) {
|
||||||
|
--nextFreeSlotsSize;
|
||||||
|
if(optimistic) {
|
||||||
|
slotTypesDescriptors.peek().setLength(nextFreeSlots[nextFreeSlotsSize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int assignSlots(final Block block, final int firstSlot) {
|
||||||
int nextSlot = firstSlot;
|
int nextSlot = firstSlot;
|
||||||
for (final Symbol symbol : block.getSymbols()) {
|
for (final Symbol symbol : block.getSymbols()) {
|
||||||
if (symbol.hasSlot()) {
|
if (symbol.hasSlot()) {
|
||||||
@ -210,9 +245,33 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
nextSlot += symbol.slotCount();
|
nextSlot += symbol.slotCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
methodEmitters.peek().ensureLocalVariableCount(nextSlot);
|
||||||
return nextSlot;
|
return nextSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Type getTypeForSlotDescriptor(final char typeDesc) {
|
||||||
|
switch(typeDesc) {
|
||||||
|
case 'I': {
|
||||||
|
return Type.INT;
|
||||||
|
}
|
||||||
|
case 'J': {
|
||||||
|
return Type.LONG;
|
||||||
|
}
|
||||||
|
case 'D': {
|
||||||
|
return Type.NUMBER;
|
||||||
|
}
|
||||||
|
case 'A': {
|
||||||
|
return Type.OBJECT;
|
||||||
|
}
|
||||||
|
case 'U': {
|
||||||
|
return Type.UNKNOWN;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pushDiscard(final Node node) {
|
void pushDiscard(final Node node) {
|
||||||
discard.push(node);
|
discard.push(node);
|
||||||
}
|
}
|
||||||
@ -228,6 +287,7 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
|||||||
int quickSlot(final Symbol symbol) {
|
int quickSlot(final Symbol symbol) {
|
||||||
final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
||||||
nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
|
nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
|
||||||
|
methodEmitters.peek().ensureLocalVariableCount(quickSlot);
|
||||||
return quickSlot;
|
return quickSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,363 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
|
import jdk.nashorn.internal.ir.Optimistic;
|
||||||
|
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for managing metadata during a compilation, e.g. which phases
|
||||||
|
* should be run, should we use optimistic types, is this a lazy compilation
|
||||||
|
* and various parameter types known to the runtime system
|
||||||
|
*/
|
||||||
|
public final class CompilationEnvironment {
|
||||||
|
private final CompilationPhases phases;
|
||||||
|
private final boolean optimistic;
|
||||||
|
|
||||||
|
private final ParamTypeMap paramTypes;
|
||||||
|
|
||||||
|
private final RecompilableScriptFunctionData compiledFunction;
|
||||||
|
|
||||||
|
private boolean strict;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
|
||||||
|
* that using whatever was at program point 17 as an int failed.
|
||||||
|
*/
|
||||||
|
private final Map<Integer, Type> invalidatedProgramPoints;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the program point that should be used as the continuation entry point, as well as all previous
|
||||||
|
* continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
|
||||||
|
* we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
|
||||||
|
* point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
|
||||||
|
* point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
|
||||||
|
* set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
|
||||||
|
* one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
|
||||||
|
*/
|
||||||
|
private final int[] continuationEntryPoints;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compilation phases that a compilation goes through
|
||||||
|
*/
|
||||||
|
public static final class CompilationPhases implements Iterable<CompilationPhase> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard (non-lazy) compilation, that basically will take an entire script
|
||||||
|
* and JIT it at once. This can lead to long startup time and fewer type
|
||||||
|
* specializations
|
||||||
|
*/
|
||||||
|
final static CompilationPhase[] SEQUENCE_EAGER_ARRAY = new CompilationPhase[] {
|
||||||
|
CompilationPhase.CONSTANT_FOLDING_PHASE,
|
||||||
|
CompilationPhase.LOWERING_PHASE,
|
||||||
|
CompilationPhase.SPLITTING_PHASE,
|
||||||
|
CompilationPhase.ATTRIBUTION_PHASE,
|
||||||
|
CompilationPhase.RANGE_ANALYSIS_PHASE,
|
||||||
|
CompilationPhase.TYPE_FINALIZATION_PHASE,
|
||||||
|
CompilationPhase.BYTECODE_GENERATION_PHASE
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static List<CompilationPhase> SEQUENCE_EAGER;
|
||||||
|
static {
|
||||||
|
LinkedList<CompilationPhase> eager = new LinkedList<>();
|
||||||
|
for (final CompilationPhase phase : SEQUENCE_EAGER_ARRAY) {
|
||||||
|
eager.add(phase);
|
||||||
|
}
|
||||||
|
SEQUENCE_EAGER = Collections.unmodifiableList(eager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Singleton that describes a standard eager compilation */
|
||||||
|
public static CompilationPhases EAGER = new CompilationPhases(SEQUENCE_EAGER);
|
||||||
|
|
||||||
|
private final List<CompilationPhase> phases;
|
||||||
|
|
||||||
|
private CompilationPhases(final List<CompilationPhase> phases) {
|
||||||
|
this.phases = phases;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private CompilationPhases addFirst(final CompilationPhase phase) {
|
||||||
|
if (phases.contains(phase)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
|
||||||
|
list.addFirst(phase);
|
||||||
|
return new CompilationPhases(Collections.unmodifiableList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
|
||||||
|
final LinkedList<CompilationPhase> list = new LinkedList<>();
|
||||||
|
for (CompilationPhase p : phases) {
|
||||||
|
list.add(p);
|
||||||
|
if (p == phase) {
|
||||||
|
list.add(newPhase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new CompilationPhases(Collections.unmodifiableList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a CompilationPhases into an optimistic one. NOP if already optimistic
|
||||||
|
* @param isOptimistic should this be optimistic
|
||||||
|
* @return new CompilationPhases that is optimistic or same if already optimistic
|
||||||
|
*/
|
||||||
|
public CompilationPhases makeOptimistic(final boolean isOptimistic) {
|
||||||
|
return isOptimistic ? addAfter(CompilationPhase.LOWERING_PHASE, CompilationPhase.PROGRAM_POINT_PHASE) : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a CompilationPhases into an optimistic one. NOP if already optimistic
|
||||||
|
* @return new CompilationPhases that is optimistic or same if already optimistic
|
||||||
|
*/
|
||||||
|
public CompilationPhases makeOptimistic() {
|
||||||
|
return makeOptimistic(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean contains(final CompilationPhase phase) {
|
||||||
|
return phases.contains(phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<CompilationPhase> iterator() {
|
||||||
|
return phases.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param phases compilation phases
|
||||||
|
* @param strict strict mode
|
||||||
|
*/
|
||||||
|
public CompilationEnvironment(
|
||||||
|
final CompilationPhases phases,
|
||||||
|
final boolean strict) {
|
||||||
|
this(phases, null, null, null, null, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for compilation environment of the rest-of method
|
||||||
|
* @param phases compilation phases
|
||||||
|
* @param strict strict mode
|
||||||
|
* @param recompiledFunction recompiled function
|
||||||
|
* @param continuationEntryPoint program points used as the continuation entry points in the current rest-of sequence
|
||||||
|
* @param invalidatedProgramPoints map of invalidated program points to their type
|
||||||
|
* @param paramTypeMap known parameter types if any exist
|
||||||
|
*/
|
||||||
|
public CompilationEnvironment(
|
||||||
|
final CompilationPhases phases,
|
||||||
|
final boolean strict,
|
||||||
|
final RecompilableScriptFunctionData recompiledFunction,
|
||||||
|
final ParamTypeMap paramTypeMap,
|
||||||
|
final Map<Integer, Type> invalidatedProgramPoints,
|
||||||
|
final int[] continuationEntryPoint) {
|
||||||
|
this(phases, paramTypeMap, invalidatedProgramPoints, recompiledFunction, continuationEntryPoint, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param phases compilation phases
|
||||||
|
* @param strict strict mode
|
||||||
|
* @param recompiledFunction recompiled function
|
||||||
|
* @param paramTypeMap known parameter types
|
||||||
|
* @param invalidatedProgramPoints map of invalidated program points to their type
|
||||||
|
*/
|
||||||
|
public CompilationEnvironment(
|
||||||
|
final CompilationPhases phases,
|
||||||
|
final boolean strict,
|
||||||
|
final RecompilableScriptFunctionData recompiledFunction,
|
||||||
|
final ParamTypeMap paramTypeMap,
|
||||||
|
final Map<Integer, Type> invalidatedProgramPoints) {
|
||||||
|
this(phases, paramTypeMap, invalidatedProgramPoints, recompiledFunction, null, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("null")
|
||||||
|
private CompilationEnvironment(
|
||||||
|
final CompilationPhases phases,
|
||||||
|
final ParamTypeMap paramTypes,
|
||||||
|
final Map<Integer, Type> invalidatedProgramPoints,
|
||||||
|
final RecompilableScriptFunctionData compiledFunction,
|
||||||
|
final int[] continuationEntryPoints,
|
||||||
|
final boolean strict) {
|
||||||
|
this.phases = phases;
|
||||||
|
this.paramTypes = paramTypes;
|
||||||
|
this.continuationEntryPoints = continuationEntryPoints;
|
||||||
|
this.invalidatedProgramPoints =
|
||||||
|
invalidatedProgramPoints == null ?
|
||||||
|
Collections.unmodifiableMap(new HashMap<Integer, Type>()) :
|
||||||
|
invalidatedProgramPoints;
|
||||||
|
this.compiledFunction = compiledFunction;
|
||||||
|
this.strict = strict;
|
||||||
|
this.optimistic = phases.contains(CompilationPhase.PROGRAM_POINT_PHASE);
|
||||||
|
|
||||||
|
// If entry point array is passed, it must have at least one element
|
||||||
|
assert continuationEntryPoints == null || continuationEntryPoints.length > 0;
|
||||||
|
assert !isCompileRestOf() || isOnDemandCompilation(); // isCompileRestOf => isRecompilation
|
||||||
|
// continuation entry points must be among the invalidated program points
|
||||||
|
assert !isCompileRestOf() || (invalidatedProgramPoints != null && containsAll(invalidatedProgramPoints.keySet(), continuationEntryPoints));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean containsAll(Set<Integer> set, final int[] array) {
|
||||||
|
for(int i = 0; i < array.length; ++i) {
|
||||||
|
if(!set.contains(array[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isStrict() {
|
||||||
|
return strict;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIsStrict(final boolean strict) {
|
||||||
|
this.strict = strict;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationPhases getPhases() {
|
||||||
|
return phases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a program point was invalidated during a previous run
|
||||||
|
* of this method, i.e. we did an optimistic assumption that now is wrong.
|
||||||
|
* This is the basis of generating a wider type. getOptimisticType
|
||||||
|
* in this class will query for invalidation and suggest a wider type
|
||||||
|
* upon recompilation if this info exists.
|
||||||
|
* @param programPoint program point to check
|
||||||
|
* @return true if it was invalidated during a previous run
|
||||||
|
*/
|
||||||
|
boolean isInvalidated(final int programPoint) {
|
||||||
|
return invalidatedProgramPoints.get(programPoint) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the parameter type at a parameter position if known from previous runtime calls
|
||||||
|
* or optimistic profiles.
|
||||||
|
*
|
||||||
|
* @param functionNode function node to query
|
||||||
|
* @param pos parameter position
|
||||||
|
* @return known type of parameter 'pos' or null if no information exists
|
||||||
|
*/
|
||||||
|
Type getParamType(final FunctionNode functionNode, final int pos) {
|
||||||
|
return paramTypes == null ? null : paramTypes.get(functionNode, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a compilation that generates the rest of method
|
||||||
|
* @return true if rest of generation
|
||||||
|
*/
|
||||||
|
boolean isCompileRestOf() {
|
||||||
|
return continuationEntryPoints != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this an on-demand compilation triggered by a {@code RecompilableScriptFunctionData} - either a type
|
||||||
|
* specializing compilation, a deoptimizing recompilation, or a rest-of method compilation.
|
||||||
|
* @return true if this is an on-demand compilation, false if this is an eager compilation.
|
||||||
|
*/
|
||||||
|
boolean isOnDemandCompilation() {
|
||||||
|
return compiledFunction != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this program point one of the continuation entry points for the rest-of method being compiled?
|
||||||
|
* @param programPoint program point
|
||||||
|
* @return true if it is a continuation entry point
|
||||||
|
*/
|
||||||
|
boolean isContinuationEntryPoint(final int programPoint) {
|
||||||
|
if(continuationEntryPoints != null) {
|
||||||
|
for(int i = 0; i < continuationEntryPoints.length; ++i) {
|
||||||
|
if(continuationEntryPoints[i] == programPoint) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] getContinuationEntryPoints() {
|
||||||
|
return continuationEntryPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this program point the continuation entry points for the current rest-of method being compiled?
|
||||||
|
* @param programPoint program point
|
||||||
|
* @return true if it is the current continuation entry point
|
||||||
|
*/
|
||||||
|
boolean isCurrentContinuationEntryPoint(final int programPoint) {
|
||||||
|
return hasCurrentContinuationEntryPoint() && getCurrentContinuationEntryPoint() == programPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasCurrentContinuationEntryPoint() {
|
||||||
|
return continuationEntryPoints != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCurrentContinuationEntryPoint() {
|
||||||
|
// NOTE: we assert in the constructor that if the array is non-null, it has at least one element
|
||||||
|
return hasCurrentContinuationEntryPoint() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are optimistic types enabled ?
|
||||||
|
* @param node get the optimistic type for a node
|
||||||
|
* @return most optimistic type in current environment
|
||||||
|
*/
|
||||||
|
Type getOptimisticType(final Optimistic node) {
|
||||||
|
assert useOptimisticTypes();
|
||||||
|
final Type invalidType = invalidatedProgramPoints.get(node.getProgramPoint());
|
||||||
|
if (invalidType != null) {
|
||||||
|
return invalidType;//.nextWider();
|
||||||
|
}
|
||||||
|
return node.getMostOptimisticType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should this compilation use optimistic types in general.
|
||||||
|
* If this is false we will only set non-object types to things that can
|
||||||
|
* be statically proven to be true.
|
||||||
|
* @return true if optimistic types should be used.
|
||||||
|
*/
|
||||||
|
boolean useOptimisticTypes() {
|
||||||
|
return optimistic;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
|
||||||
|
return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,19 +8,13 @@ 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.PARSED;
|
||||||
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
|
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import jdk.nashorn.internal.codegen.types.Range;
|
import jdk.nashorn.internal.codegen.types.Range;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.CallNode;
|
|
||||||
import jdk.nashorn.internal.ir.Expression;
|
import jdk.nashorn.internal.ir.Expression;
|
||||||
import jdk.nashorn.internal.ir.FunctionNode;
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||||
@ -32,7 +26,6 @@ import jdk.nashorn.internal.ir.TemporarySymbols;
|
|||||||
import jdk.nashorn.internal.ir.debug.ASTWriter;
|
import jdk.nashorn.internal.ir.debug.ASTWriter;
|
||||||
import jdk.nashorn.internal.ir.debug.PrintVisitor;
|
import jdk.nashorn.internal.ir.debug.PrintVisitor;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
import jdk.nashorn.internal.runtime.ECMAErrors;
|
|
||||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||||
import jdk.nashorn.internal.runtime.Timing;
|
import jdk.nashorn.internal.runtime.Timing;
|
||||||
|
|
||||||
@ -41,102 +34,7 @@ import jdk.nashorn.internal.runtime.Timing;
|
|||||||
* FunctionNode into bytecode. It has an optional return value.
|
* FunctionNode into bytecode. It has an optional return value.
|
||||||
*/
|
*/
|
||||||
enum CompilationPhase {
|
enum CompilationPhase {
|
||||||
|
/**
|
||||||
/*
|
|
||||||
* Lazy initialization - tag all function nodes not the script as lazy as
|
|
||||||
* default policy. The will get trampolines and only be generated when
|
|
||||||
* called
|
|
||||||
*/
|
|
||||||
LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
|
|
||||||
@Override
|
|
||||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For lazy compilation, we might be given a node previously marked
|
|
||||||
* as lazy to compile as the outermost function node in the
|
|
||||||
* compiler. Unmark it so it can be compiled and not cause
|
|
||||||
* recursion. Make sure the return type is unknown so it can be
|
|
||||||
* correctly deduced. Return types are always Objects in Lazy nodes
|
|
||||||
* as we haven't got a change to generate code for them and decude
|
|
||||||
* its parameter specialization
|
|
||||||
*
|
|
||||||
* TODO: in the future specializations from a callsite will be
|
|
||||||
* passed here so we can generate a better non-lazy version of a
|
|
||||||
* function from a trampoline
|
|
||||||
*/
|
|
||||||
|
|
||||||
final FunctionNode outermostFunctionNode = fn;
|
|
||||||
|
|
||||||
final Set<FunctionNode> neverLazy = new HashSet<>();
|
|
||||||
final Set<FunctionNode> lazy = new HashSet<>();
|
|
||||||
|
|
||||||
FunctionNode newFunctionNode = outermostFunctionNode;
|
|
||||||
|
|
||||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
|
||||||
// self references are done with invokestatic and thus cannot
|
|
||||||
// have trampolines - never lazy
|
|
||||||
@Override
|
|
||||||
public boolean enterCallNode(final CallNode node) {
|
|
||||||
final Node callee = node.getFunction();
|
|
||||||
if (callee instanceof FunctionNode) {
|
|
||||||
neverLazy.add(((FunctionNode)callee));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//any function that isn't the outermost one must be marked as lazy
|
|
||||||
@Override
|
|
||||||
public boolean enterFunctionNode(final FunctionNode node) {
|
|
||||||
assert compiler.isLazy();
|
|
||||||
lazy.add(node);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//at least one method is non lazy - the outermost one
|
|
||||||
neverLazy.add(newFunctionNode);
|
|
||||||
|
|
||||||
for (final FunctionNode node : neverLazy) {
|
|
||||||
Compiler.LOG.fine(
|
|
||||||
"Marking ",
|
|
||||||
node.getName(),
|
|
||||||
" as non lazy, as it's a self reference");
|
|
||||||
lazy.remove(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
|
||||||
@Override
|
|
||||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
|
||||||
if (lazy.contains(functionNode)) {
|
|
||||||
Compiler.LOG.fine(
|
|
||||||
"Marking ",
|
|
||||||
functionNode.getName(),
|
|
||||||
" as lazy");
|
|
||||||
final FunctionNode parent = lc.getParentFunction(functionNode);
|
|
||||||
assert parent != null;
|
|
||||||
lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN);
|
|
||||||
lc.setBlockNeedsScope(parent.getBody());
|
|
||||||
lc.setFlag(functionNode, FunctionNode.IS_LAZY);
|
|
||||||
return functionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return functionNode.
|
|
||||||
clearFlag(lc, FunctionNode.IS_LAZY).
|
|
||||||
setReturnType(lc, Type.UNKNOWN);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return newFunctionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "[Lazy JIT Initialization]";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constant folding pass Simple constant folding that will make elementary
|
* Constant folding pass Simple constant folding that will make elementary
|
||||||
* constructs go away
|
* constructs go away
|
||||||
*/
|
*/
|
||||||
@ -152,7 +50,7 @@ enum CompilationPhase {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Lower (Control flow pass) Finalizes the control flow. Clones blocks for
|
* Lower (Control flow pass) Finalizes the control flow. Clones blocks for
|
||||||
* finally constructs and similar things. Establishes termination criteria
|
* finally constructs and similar things. Establishes termination criteria
|
||||||
* for nodes Guarantee return instructions to method making sure control
|
* for nodes Guarantee return instructions to method making sure control
|
||||||
@ -171,14 +69,58 @@ enum CompilationPhase {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* Phase used only when doing optimistic code generation. It assigns all potentially
|
||||||
|
* optimistic ops a program point so that an UnwarrantedException knows from where
|
||||||
|
* a guess went wrong when creating the continuation to roll back this execution
|
||||||
|
*/
|
||||||
|
PROGRAM_POINT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
|
||||||
|
@Override
|
||||||
|
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||||
|
return (FunctionNode)fn.accept(new ProgramPoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[Program Point Calculation]";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splitter Split the AST into several compile units based on a heuristic size calculation.
|
||||||
|
* Split IR can lead to scope information being changed.
|
||||||
|
*/
|
||||||
|
SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
|
||||||
|
@Override
|
||||||
|
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||||
|
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
|
||||||
|
|
||||||
|
final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
|
||||||
|
|
||||||
|
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
|
||||||
|
|
||||||
|
if (newFunctionNode.isStrict()) {
|
||||||
|
assert compiler.getCompilationEnvironment().isStrict();
|
||||||
|
compiler.getCompilationEnvironment().setIsStrict(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newFunctionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[Code Splitting]";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
* Attribution Assign symbols and types to all nodes.
|
* Attribution Assign symbols and types to all nodes.
|
||||||
*/
|
*/
|
||||||
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
|
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT)) {
|
||||||
@Override
|
@Override
|
||||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||||
final TemporarySymbols ts = compiler.getTemporarySymbols();
|
final TemporarySymbols ts = compiler.getTemporarySymbols();
|
||||||
final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts));
|
final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(compiler.getCompilationEnvironment(), ts));
|
||||||
if (compiler.getEnv()._print_mem_usage) {
|
if (compiler.getEnv()._print_mem_usage) {
|
||||||
Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount());
|
Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount());
|
||||||
}
|
}
|
||||||
@ -194,12 +136,6 @@ enum CompilationPhase {
|
|||||||
return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||||
@Override
|
@Override
|
||||||
public Node leaveFunctionNode(final FunctionNode node) {
|
public Node leaveFunctionNode(final FunctionNode node) {
|
||||||
if (node.isLazy()) {
|
|
||||||
FunctionNode newNode = node.setReturnType(lc, Type.OBJECT);
|
|
||||||
return ts.ensureSymbol(lc, Type.OBJECT, newNode);
|
|
||||||
}
|
|
||||||
//node may have a reference here that needs to be nulled if it was referred to by
|
|
||||||
//its outer context, if it is lazy and not attributed
|
|
||||||
return node.setReturnType(lc, Type.UNKNOWN).setSymbol(lc, null);
|
return node.setReturnType(lc, Type.UNKNOWN).setSymbol(lc, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -211,12 +147,12 @@ enum CompilationPhase {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Range analysis
|
* Range analysis
|
||||||
* Conservatively prove that certain variables can be narrower than
|
* Conservatively prove that certain variables can be narrower than
|
||||||
* the most generic number type
|
* the most generic number type
|
||||||
*/
|
*/
|
||||||
RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
|
RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, ATTR)) {
|
||||||
@Override
|
@Override
|
||||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||||
if (!compiler.getEnv()._range_analysis) {
|
if (!compiler.getEnv()._range_analysis) {
|
||||||
@ -261,13 +197,15 @@ enum CompilationPhase {
|
|||||||
final Expression expr = (Expression)node;
|
final Expression expr = (Expression)node;
|
||||||
final Symbol symbol = expr.getSymbol();
|
final Symbol symbol = expr.getSymbol();
|
||||||
if (symbol != null) {
|
if (symbol != null) {
|
||||||
final Range range = symbol.getRange();
|
final Range range = symbol.getRange();
|
||||||
final Type symbolType = symbol.getSymbolType();
|
final Type symbolType = symbol.getSymbolType();
|
||||||
if (!symbolType.isNumeric()) {
|
|
||||||
|
if (!symbolType.isUnknown() && !symbolType.isNumeric()) {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
final Type rangeType = range.getType();
|
|
||||||
if (!Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range
|
final Type rangeType = range.getType();
|
||||||
|
if (!rangeType.isUnknown() && !Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range
|
||||||
RangeAnalyzer.LOG.info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange());
|
RangeAnalyzer.LOG.info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange());
|
||||||
return expr.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols()));
|
return expr.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols()));
|
||||||
}
|
}
|
||||||
@ -296,37 +234,7 @@ enum CompilationPhase {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
/*
|
|
||||||
* Splitter Split the AST into several compile units based on a size
|
|
||||||
* heuristic Splitter needs attributed AST for weight calculations (e.g. is
|
|
||||||
* 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, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
|
|
||||||
@Override
|
|
||||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
|
||||||
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
|
|
||||||
|
|
||||||
final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
|
|
||||||
|
|
||||||
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
|
|
||||||
|
|
||||||
if (newFunctionNode.isStrict()) {
|
|
||||||
assert compiler.getStrictMode();
|
|
||||||
compiler.setStrictMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newFunctionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "[Code Splitting]";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FinalizeTypes
|
* FinalizeTypes
|
||||||
*
|
*
|
||||||
* This pass finalizes the types for nodes. If Attr created wider types than
|
* This pass finalizes the types for nodes. If Attr created wider types than
|
||||||
@ -344,7 +252,7 @@ enum CompilationPhase {
|
|||||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||||
final ScriptEnvironment env = compiler.getEnv();
|
final ScriptEnvironment env = compiler.getEnv();
|
||||||
|
|
||||||
final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes(compiler.getTemporarySymbols()));
|
final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
|
||||||
|
|
||||||
if (env._print_lower_ast) {
|
if (env._print_lower_ast) {
|
||||||
env.getErr().println(new ASTWriter(newFunctionNode));
|
env.getErr().println(new ASTWriter(newFunctionNode));
|
||||||
@ -363,7 +271,7 @@ enum CompilationPhase {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Bytecode generation:
|
* Bytecode generation:
|
||||||
*
|
*
|
||||||
* Generate the byte code class(es) resulting from the compiled FunctionNode
|
* Generate the byte code class(es) resulting from the compiled FunctionNode
|
||||||
@ -400,50 +308,12 @@ enum CompilationPhase {
|
|||||||
|
|
||||||
compiler.addClass(className, bytecode);
|
compiler.addClass(className, bytecode);
|
||||||
|
|
||||||
// should could be printed to stderr for generate class?
|
|
||||||
if (env._print_code) {
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("class: " + className).append('\n')
|
|
||||||
.append(ClassEmitter.disassemble(bytecode))
|
|
||||||
.append("=====");
|
|
||||||
env.getErr().println(sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// should we verify the generated code?
|
// should we verify the generated code?
|
||||||
if (env._verify_code) {
|
if (env._verify_code) {
|
||||||
compiler.getCodeInstaller().verify(bytecode);
|
compiler.getCodeInstaller().verify(bytecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// should code be dumped to disk - only valid in compile_only mode?
|
DumpBytecode.dumpBytecode(env, bytecode, className);
|
||||||
if (env._dest_dir != null && env._compile_only) {
|
|
||||||
final String fileName = className.replace('.', File.separatorChar) + ".class";
|
|
||||||
final int index = fileName.lastIndexOf(File.separatorChar);
|
|
||||||
|
|
||||||
final File dir;
|
|
||||||
if (index != -1) {
|
|
||||||
dir = new File(env._dest_dir, fileName.substring(0, index));
|
|
||||||
} else {
|
|
||||||
dir = new File(env._dest_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!dir.exists() && !dir.mkdirs()) {
|
|
||||||
throw new IOException(dir.toString());
|
|
||||||
}
|
|
||||||
final File file = new File(env._dest_dir, fileName);
|
|
||||||
try (final FileOutputStream fos = new FileOutputStream(file)) {
|
|
||||||
fos.write(bytecode);
|
|
||||||
}
|
|
||||||
Compiler.LOG.info("Wrote class to '" + file.getAbsolutePath() + '\'');
|
|
||||||
} catch (final IOException e) {
|
|
||||||
Compiler.LOG.warning("Skipping class dump for ",
|
|
||||||
className,
|
|
||||||
": ",
|
|
||||||
ECMAErrors.getMessage(
|
|
||||||
"io.error.cant.write",
|
|
||||||
dir.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newFunctionNode;
|
return newFunctionNode;
|
||||||
@ -468,6 +338,11 @@ enum CompilationPhase {
|
|||||||
return functionNode.hasState(pre);
|
return functionNode.hasState(pre);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a compilation phase
|
||||||
|
* @param functionNode function to compile
|
||||||
|
* @return function node
|
||||||
|
*/
|
||||||
protected FunctionNode begin(final FunctionNode functionNode) {
|
protected FunctionNode begin(final FunctionNode functionNode) {
|
||||||
if (pre != null) {
|
if (pre != null) {
|
||||||
// check that everything in pre is present
|
// check that everything in pre is present
|
||||||
@ -484,6 +359,11 @@ enum CompilationPhase {
|
|||||||
return functionNode;
|
return functionNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End a compilation phase
|
||||||
|
* @param functionNode function node to compile
|
||||||
|
* @return fucntion node
|
||||||
|
*/
|
||||||
protected FunctionNode end(final FunctionNode functionNode) {
|
protected FunctionNode end(final FunctionNode functionNode) {
|
||||||
endTime = System.currentTimeMillis();
|
endTime = System.currentTimeMillis();
|
||||||
Timing.accumulateTime(toString(), endTime - startTime);
|
Timing.accumulateTime(toString(), endTime - startTime);
|
||||||
|
@ -25,10 +25,16 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.codegen;
|
package jdk.nashorn.internal.codegen;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
|
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to track split class compilation.
|
* Used to track split class compilation.
|
||||||
*/
|
*/
|
||||||
public class CompileUnit implements Comparable<CompileUnit> {
|
public final class CompileUnit implements Comparable<CompileUnit> {
|
||||||
/** Current class name */
|
/** Current class name */
|
||||||
private final String className;
|
private final String className;
|
||||||
|
|
||||||
@ -39,6 +45,38 @@ public class CompileUnit implements Comparable<CompileUnit> {
|
|||||||
|
|
||||||
private Class<?> clazz;
|
private Class<?> clazz;
|
||||||
|
|
||||||
|
private Set<FunctionInitializer> functionInitializers = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
private static class FunctionInitializer {
|
||||||
|
final RecompilableScriptFunctionData data;
|
||||||
|
final FunctionNode functionNode;
|
||||||
|
|
||||||
|
FunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
|
||||||
|
this.data = data;
|
||||||
|
this.functionNode = functionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeCode() {
|
||||||
|
data.initializeCode(functionNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return data.hashCode() + 31 * functionNode.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if(obj == null || obj.getClass() != FunctionInitializer.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final FunctionInitializer other = (FunctionInitializer)obj;
|
||||||
|
return data == other.data && functionNode == other.functionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
CompileUnit(final String className, final ClassEmitter classEmitter) {
|
CompileUnit(final String className, final ClassEmitter classEmitter) {
|
||||||
this(className, classEmitter, 0L);
|
this(className, classEmitter, 0L);
|
||||||
}
|
}
|
||||||
@ -71,6 +109,29 @@ public class CompileUnit implements Comparable<CompileUnit> {
|
|||||||
this.classEmitter = null;
|
this.classEmitter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addFunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
|
||||||
|
functionInitializers.add(new FunctionInitializer(data, functionNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this compile unit is responsible for initializing the specified function data with specified
|
||||||
|
* function node.
|
||||||
|
* @param data the function data to check
|
||||||
|
* @param functionNode the function node to check
|
||||||
|
* @return true if this unit is responsible for initializing the function data with the function node, otherwise
|
||||||
|
* false
|
||||||
|
*/
|
||||||
|
public boolean isInitializing(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
|
||||||
|
return functionInitializers.contains(new FunctionInitializer(data, functionNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeFunctionsCode() {
|
||||||
|
for(final FunctionInitializer init: functionInitializers) {
|
||||||
|
init.initializeCode();
|
||||||
|
}
|
||||||
|
functionInitializers = Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add weight to this compile unit
|
* Add weight to this compile unit
|
||||||
* @param w weight to add
|
* @param w weight to add
|
||||||
|
@ -28,8 +28,6 @@ package jdk.nashorn.internal.codegen;
|
|||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
|
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
|
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
|
||||||
@ -41,13 +39,12 @@ import java.lang.reflect.Field;
|
|||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedActionException;
|
import java.security.PrivilegedActionException;
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@ -56,6 +53,7 @@ import java.util.TreeSet;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import jdk.internal.dynalink.support.NameCodec;
|
import jdk.internal.dynalink.support.NameCodec;
|
||||||
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
||||||
|
import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.FunctionNode;
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||||
@ -64,10 +62,10 @@ import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
|
|||||||
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
|
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
|
||||||
import jdk.nashorn.internal.runtime.CodeInstaller;
|
import jdk.nashorn.internal.runtime.CodeInstaller;
|
||||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||||
|
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
|
||||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||||
import jdk.nashorn.internal.runtime.Source;
|
import jdk.nashorn.internal.runtime.Source;
|
||||||
import jdk.nashorn.internal.runtime.Timing;
|
import jdk.nashorn.internal.runtime.Timing;
|
||||||
import jdk.nashorn.internal.runtime.options.Options;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for converting JavaScripts to java byte code. Main entry
|
* Responsible for converting JavaScripts to java byte code. Main entry
|
||||||
@ -86,6 +84,7 @@ public final class Compiler {
|
|||||||
private Source source;
|
private Source source;
|
||||||
|
|
||||||
private String sourceName;
|
private String sourceName;
|
||||||
|
private String sourceURL;
|
||||||
|
|
||||||
private final Map<String, byte[]> bytecode;
|
private final Map<String, byte[]> bytecode;
|
||||||
|
|
||||||
@ -93,14 +92,12 @@ public final class Compiler {
|
|||||||
|
|
||||||
private final ConstantData constantData;
|
private final ConstantData constantData;
|
||||||
|
|
||||||
private final CompilationSequence sequence;
|
private final CompilationEnvironment compilationEnv;
|
||||||
|
|
||||||
private final ScriptEnvironment env;
|
private final ScriptEnvironment scriptEnv;
|
||||||
|
|
||||||
private String scriptName;
|
private String scriptName;
|
||||||
|
|
||||||
private boolean strict;
|
|
||||||
|
|
||||||
private final CodeInstaller<ScriptEnvironment> installer;
|
private final CodeInstaller<ScriptEnvironment> installer;
|
||||||
|
|
||||||
private final TemporarySymbols temporarySymbols = new TemporarySymbols();
|
private final TemporarySymbols temporarySymbols = new TemporarySymbols();
|
||||||
@ -109,6 +106,12 @@ public final class Compiler {
|
|||||||
* that affect classes */
|
* that affect classes */
|
||||||
public static final DebugLogger LOG = new DebugLogger("compiler");
|
public static final DebugLogger LOG = new DebugLogger("compiler");
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (ScriptEnvironment.globalOptimistic()) {
|
||||||
|
LOG.warning("Running with optimistic types. This is experimental. To switch off, use -Dnashorn.optimistic=false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This array contains names that need to be reserved at the start
|
* This array contains names that need to be reserved at the start
|
||||||
* of a compile, to avoid conflict with variable names later introduced.
|
* of a compile, to avoid conflict with variable names later introduced.
|
||||||
@ -124,181 +127,47 @@ public final class Compiler {
|
|||||||
ARGUMENTS.symbolName()
|
ARGUMENTS.symbolName()
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
private void initCompiler(final String className, final FunctionNode functionNode) {
|
||||||
* This class makes it possible to do your own compilation sequence
|
|
||||||
* from the code generation package. There are predefined compilation
|
|
||||||
* sequences already
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
static class CompilationSequence extends LinkedList<CompilationPhase> {
|
|
||||||
|
|
||||||
CompilationSequence(final CompilationPhase... phases) {
|
|
||||||
super(Arrays.asList(phases));
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationSequence(final CompilationSequence sequence) {
|
|
||||||
this(sequence.toArray(new CompilationPhase[sequence.size()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationSequence insertAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
|
|
||||||
final CompilationSequence newSeq = new CompilationSequence();
|
|
||||||
for (final CompilationPhase elem : this) {
|
|
||||||
newSeq.add(phase);
|
|
||||||
if (elem.equals(phase)) {
|
|
||||||
newSeq.add(newPhase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert newSeq.contains(newPhase);
|
|
||||||
return newSeq;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationSequence insertBefore(final CompilationPhase phase, final CompilationPhase newPhase) {
|
|
||||||
final CompilationSequence newSeq = new CompilationSequence();
|
|
||||||
for (final CompilationPhase elem : this) {
|
|
||||||
if (elem.equals(phase)) {
|
|
||||||
newSeq.add(newPhase);
|
|
||||||
}
|
|
||||||
newSeq.add(phase);
|
|
||||||
}
|
|
||||||
assert newSeq.contains(newPhase);
|
|
||||||
return newSeq;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationSequence insertFirst(final CompilationPhase phase) {
|
|
||||||
final CompilationSequence newSeq = new CompilationSequence(this);
|
|
||||||
newSeq.addFirst(phase);
|
|
||||||
return newSeq;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationSequence insertLast(final CompilationPhase phase) {
|
|
||||||
final CompilationSequence newSeq = new CompilationSequence(this);
|
|
||||||
newSeq.addLast(phase);
|
|
||||||
return newSeq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Environment information known to the compile, e.g. params
|
|
||||||
*/
|
|
||||||
public static class Hints {
|
|
||||||
private final Type[] paramTypes;
|
|
||||||
|
|
||||||
/** singleton empty hints */
|
|
||||||
public static final Hints EMPTY = new Hints();
|
|
||||||
|
|
||||||
private Hints() {
|
|
||||||
this.paramTypes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param paramTypes known parameter types for this callsite
|
|
||||||
*/
|
|
||||||
public Hints(final Type[] paramTypes) {
|
|
||||||
this.paramTypes = paramTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the parameter type for this parameter position, or
|
|
||||||
* null if now known
|
|
||||||
* @param pos position
|
|
||||||
* @return parameter type for this callsite if known
|
|
||||||
*/
|
|
||||||
public Type getParameterType(final int pos) {
|
|
||||||
if (paramTypes != null && pos < paramTypes.length) {
|
|
||||||
return paramTypes[pos];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Standard (non-lazy) compilation, that basically will take an entire script
|
|
||||||
* and JIT it at once. This can lead to long startup time and fewer type
|
|
||||||
* specializations
|
|
||||||
*/
|
|
||||||
final static CompilationSequence SEQUENCE_EAGER = new CompilationSequence(
|
|
||||||
CompilationPhase.CONSTANT_FOLDING_PHASE,
|
|
||||||
CompilationPhase.LOWERING_PHASE,
|
|
||||||
CompilationPhase.ATTRIBUTION_PHASE,
|
|
||||||
CompilationPhase.RANGE_ANALYSIS_PHASE,
|
|
||||||
CompilationPhase.SPLITTING_PHASE,
|
|
||||||
CompilationPhase.TYPE_FINALIZATION_PHASE,
|
|
||||||
CompilationPhase.BYTECODE_GENERATION_PHASE);
|
|
||||||
|
|
||||||
final static CompilationSequence SEQUENCE_LAZY =
|
|
||||||
SEQUENCE_EAGER.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
return '$' + LAZY.symbolName() + '$' + functionNode.getName();
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param env script environment
|
|
||||||
* @param installer code installer
|
|
||||||
* @param sequence {@link Compiler.CompilationSequence} of {@link CompilationPhase}s to apply as this compilation
|
|
||||||
* @param strict should this compilation use strict mode semantics
|
|
||||||
*/
|
|
||||||
//TODO support an array of FunctionNodes for batch lazy compilation
|
|
||||||
Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final CompilationSequence sequence, final boolean strict) {
|
|
||||||
this.env = env;
|
|
||||||
this.sequence = sequence;
|
|
||||||
this.installer = installer;
|
|
||||||
this.constantData = new ConstantData();
|
|
||||||
this.compileUnits = new TreeSet<>();
|
|
||||||
this.bytecode = new LinkedHashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initCompiler(final FunctionNode functionNode) {
|
|
||||||
this.strict = strict || functionNode.isStrict();
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
|
|
||||||
append('$').
|
|
||||||
append(safeSourceName(functionNode.getSource()));
|
|
||||||
this.source = functionNode.getSource();
|
this.source = functionNode.getSource();
|
||||||
this.sourceName = functionNode.getSourceName();
|
this.sourceName = functionNode.getSourceName();
|
||||||
|
this.sourceURL = functionNode.getSourceURL();
|
||||||
|
|
||||||
|
if (functionNode.isStrict()) {
|
||||||
|
compilationEnv.setIsStrict(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(functionNode.uniqueName(className)).
|
||||||
|
append('$').
|
||||||
|
append(safeSourceName(functionNode.getSource()));
|
||||||
this.scriptName = sb.toString();
|
this.scriptName = sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Compiler(final CompilationEnvironment compilationEnv, final ScriptEnvironment scriptEnv, final CodeInstaller<ScriptEnvironment> installer) {
|
||||||
* Constructor
|
this.scriptEnv = scriptEnv;
|
||||||
*
|
this.compilationEnv = compilationEnv;
|
||||||
* @param installer code installer
|
this.installer = installer;
|
||||||
* @param strict should this compilation use strict mode semantics
|
this.constantData = new ConstantData();
|
||||||
*/
|
this.compileUnits = new TreeSet<>();
|
||||||
public Compiler(final CodeInstaller<ScriptEnvironment> installer, final boolean strict) {
|
this.bytecode = new LinkedHashMap<>();
|
||||||
this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), strict);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor - compilation will use the same strict semantics as in script environment
|
* Constructor - common entry point for generating code.
|
||||||
*
|
* @param env compilation environment
|
||||||
* @param installer code installer
|
* @param installer code installer
|
||||||
*/
|
*/
|
||||||
public Compiler(final CodeInstaller<ScriptEnvironment> installer) {
|
public Compiler(final CompilationEnvironment env, final CodeInstaller<ScriptEnvironment> installer) {
|
||||||
this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
|
this(env, installer.getOwner(), installer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor - compilation needs no installer, but uses a script environment
|
* ScriptEnvironment constructor for compiler. Used only from Shell and --compile-only flag
|
||||||
* Used in "compile only" scenarios
|
* No code installer supplied
|
||||||
* @param env a script environment
|
* @param scriptEnv script environment
|
||||||
*/
|
*/
|
||||||
public Compiler(final ScriptEnvironment env) {
|
public Compiler(final ScriptEnvironment scriptEnv) {
|
||||||
this(env, null, sequence(env._lazy_compilation), env._strict);
|
this(new CompilationEnvironment(CompilationPhases.EAGER, scriptEnv._strict), scriptEnv, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printMemoryUsage(final String phaseName, final FunctionNode functionNode) {
|
private static void printMemoryUsage(final String phaseName, final FunctionNode functionNode) {
|
||||||
@ -337,30 +206,53 @@ public final class Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompilationEnvironment getCompilationEnvironment() {
|
||||||
|
return compilationEnv;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the compilation this Compiler was created with
|
* Execute the compilation this Compiler was created with with default class name
|
||||||
* @param functionNode function node to compile from its current state
|
* @param functionNode function node to compile from its current state
|
||||||
* @throws CompilationException if something goes wrong
|
* @throws CompilationException if something goes wrong
|
||||||
* @return function node that results from code transforms
|
* @return function node that results from code transforms
|
||||||
*/
|
*/
|
||||||
public FunctionNode compile(final FunctionNode functionNode) throws CompilationException {
|
public FunctionNode compile(final FunctionNode functionNode) throws CompilationException {
|
||||||
|
return compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the compilation this Compiler was created with
|
||||||
|
* @param className class name for the compile
|
||||||
|
* @param functionNode function node to compile from its current state
|
||||||
|
* @throws CompilationException if something goes wrong
|
||||||
|
* @return function node that results from code transforms
|
||||||
|
*/
|
||||||
|
public FunctionNode compile(final String className, final FunctionNode functionNode) throws CompilationException {
|
||||||
|
try {
|
||||||
|
return compileInternal(className, functionNode);
|
||||||
|
} catch(AssertionError e) {
|
||||||
|
throw new AssertionError("Assertion failure compiling " + functionNode.getSource(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionNode compileInternal(final String className, final FunctionNode functionNode) throws CompilationException {
|
||||||
FunctionNode newFunctionNode = functionNode;
|
FunctionNode newFunctionNode = functionNode;
|
||||||
|
|
||||||
initCompiler(newFunctionNode); //TODO move this state into functionnode?
|
initCompiler(className, newFunctionNode); //TODO move this state into functionnode?
|
||||||
|
|
||||||
for (final String reservedName : RESERVED_NAMES) {
|
for (final String reservedName : RESERVED_NAMES) {
|
||||||
newFunctionNode.uniqueName(reservedName);
|
newFunctionNode.uniqueName(reservedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean fine = !LOG.levelAbove(Level.FINE);
|
final boolean fine = LOG.levelFinerThanOrEqual(Level.FINE);
|
||||||
final boolean info = !LOG.levelAbove(Level.INFO);
|
final boolean info = LOG.levelFinerThanOrEqual(Level.INFO);
|
||||||
|
|
||||||
long time = 0L;
|
long time = 0L;
|
||||||
|
|
||||||
for (final CompilationPhase phase : sequence) {
|
for (final CompilationPhase phase : compilationEnv.getPhases()) {
|
||||||
newFunctionNode = phase.apply(this, newFunctionNode);
|
newFunctionNode = phase.apply(this, newFunctionNode);
|
||||||
|
|
||||||
if (env._print_mem_usage) {
|
if (scriptEnv._print_mem_usage) {
|
||||||
printMemoryUsage(phase.toString(), newFunctionNode);
|
printMemoryUsage(phase.toString(), newFunctionNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +333,7 @@ public final class Compiler {
|
|||||||
public Class<?> install(final FunctionNode functionNode) {
|
public Class<?> install(final FunctionNode functionNode) {
|
||||||
final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
|
final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
|
||||||
|
|
||||||
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
|
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has unexpected compilation state";
|
||||||
|
|
||||||
final Map<String, Class<?>> installedClasses = new HashMap<>();
|
final Map<String, Class<?>> installedClasses = new HashMap<>();
|
||||||
|
|
||||||
@ -464,8 +356,17 @@ public final class Compiler {
|
|||||||
installedClasses.put(className, install(className, code));
|
installedClasses.put(className, install(className, code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
|
||||||
|
for(final Object constant: getConstantData().constants) {
|
||||||
|
if(constant instanceof RecompilableScriptFunctionData) {
|
||||||
|
final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
|
||||||
|
rfns.put(rfn, rfn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (final CompileUnit unit : compileUnits) {
|
for (final CompileUnit unit : compileUnits) {
|
||||||
unit.setCode(installedClasses.get(unit.getUnitClassName()));
|
unit.setCode(installedClasses.get(unit.getUnitClassName()));
|
||||||
|
unit.initializeFunctionsCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringBuilder sb;
|
final StringBuilder sb;
|
||||||
@ -503,14 +404,6 @@ public final class Compiler {
|
|||||||
return compileUnits;
|
return compileUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean getStrictMode() {
|
|
||||||
return strict;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setStrictMode(final boolean strict) {
|
|
||||||
this.strict = strict;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstantData getConstantData() {
|
ConstantData getConstantData() {
|
||||||
return constantData;
|
return constantData;
|
||||||
}
|
}
|
||||||
@ -528,7 +421,11 @@ public final class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ScriptEnvironment getEnv() {
|
ScriptEnvironment getEnv() {
|
||||||
return this.env;
|
return this.scriptEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSourceURL() {
|
||||||
|
return sourceURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String safeSourceName(final Source src) {
|
private String safeSourceName(final Source src) {
|
||||||
@ -540,7 +437,7 @@ public final class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
baseName = baseName.replace('.', '_').replace('-', '_');
|
baseName = baseName.replace('.', '_').replace('-', '_');
|
||||||
if (! env._loader_per_compile) {
|
if (! scriptEnv._loader_per_compile) {
|
||||||
baseName = baseName + installer.getUniqueScriptId();
|
baseName = baseName + installer.getUniqueScriptId();
|
||||||
}
|
}
|
||||||
final String mangled = NameCodec.encode(baseName);
|
final String mangled = NameCodec.encode(baseName);
|
||||||
@ -576,7 +473,7 @@ public final class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
|
private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
|
||||||
final ClassEmitter classEmitter = new ClassEmitter(env, sourceName, unitClassName, strict);
|
final ClassEmitter classEmitter = new ClassEmitter(scriptEnv, sourceName, unitClassName, compilationEnv.isStrict());
|
||||||
final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
|
final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
|
||||||
|
|
||||||
classEmitter.begin();
|
classEmitter.begin();
|
||||||
@ -611,23 +508,4 @@ public final class Compiler {
|
|||||||
public static String binaryName(final String name) {
|
public static String binaryName(final String name) {
|
||||||
return name.replace('/', '.');
|
return name.replace('/', '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Should we use integers for arithmetic operations as well?
|
|
||||||
* TODO: We currently generate no overflow checks so this is
|
|
||||||
* disabled
|
|
||||||
*
|
|
||||||
* @return true if arithmetic operations should not widen integer
|
|
||||||
* operands by default.
|
|
||||||
*/
|
|
||||||
static boolean shouldUseIntegerArithmetic() {
|
|
||||||
return USE_INT_ARITH;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final boolean USE_INT_ARITH;
|
|
||||||
|
|
||||||
static {
|
|
||||||
USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic");
|
|
||||||
assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,6 @@ public enum CompilerConstants {
|
|||||||
/** the __LINE__ variable */
|
/** the __LINE__ variable */
|
||||||
__LINE__,
|
__LINE__,
|
||||||
|
|
||||||
/** lazy prefix for classes of jitted methods */
|
|
||||||
LAZY("Lazy"),
|
|
||||||
|
|
||||||
/** constructor name */
|
/** constructor name */
|
||||||
INIT("<init>"),
|
INIT("<init>"),
|
||||||
|
|
||||||
@ -78,8 +75,11 @@ public enum CompilerConstants {
|
|||||||
/** function prefix for anonymous functions */
|
/** function prefix for anonymous functions */
|
||||||
ANON_FUNCTION_PREFIX("L:"),
|
ANON_FUNCTION_PREFIX("L:"),
|
||||||
|
|
||||||
/** method name for Java method that is script entry point */
|
/** method name for Java method that is the program entry point */
|
||||||
RUN_SCRIPT("runScript"),
|
PROGRAM(":program"),
|
||||||
|
|
||||||
|
/** method name for Java method that creates the script function for the program */
|
||||||
|
CREATE_PROGRAM_FUNCTION(":createProgramFunction"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "this" name symbol for a parameter representing ECMAScript "this" in static methods that are compiled
|
* "this" name symbol for a parameter representing ECMAScript "this" in static methods that are compiled
|
||||||
@ -161,7 +161,7 @@ public enum CompilerConstants {
|
|||||||
/** get map */
|
/** get map */
|
||||||
GET_MAP(":getMap"),
|
GET_MAP(":getMap"),
|
||||||
|
|
||||||
/** get map */
|
/** set map */
|
||||||
SET_MAP(":setMap"),
|
SET_MAP(":setMap"),
|
||||||
|
|
||||||
/** get array prefix */
|
/** get array prefix */
|
||||||
@ -173,7 +173,7 @@ public enum CompilerConstants {
|
|||||||
/**
|
/**
|
||||||
* Prefix used for internal methods generated in script clases.
|
* Prefix used for internal methods generated in script clases.
|
||||||
*/
|
*/
|
||||||
public static final String INTERNAL_METHOD_PREFIX = ":";
|
private static final String INTERNAL_METHOD_PREFIX = ":";
|
||||||
|
|
||||||
private final String symbolName;
|
private final String symbolName;
|
||||||
private final Class<?> type;
|
private final Class<?> type;
|
||||||
@ -198,9 +198,23 @@ public enum CompilerConstants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
|
private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
|
||||||
this.symbolName = symbolName;
|
this.symbolName = symbolName;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.slot = slot;
|
this.slot = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a name is that of a reserved compiler constnat
|
||||||
|
* @param name name
|
||||||
|
* @return true if compiler constant name
|
||||||
|
*/
|
||||||
|
public static boolean isCompilerConstant(final String name) {
|
||||||
|
for (final CompilerConstants cc : CompilerConstants.values()) {
|
||||||
|
if (name.equals(cc.symbolName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -538,6 +552,18 @@ public enum CompilerConstants {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the passed string looks like a method name of an internally generated Nashorn method. Basically,
|
||||||
|
* if it starts with a colon character {@code :} but is not the name of the program method {@code :program}.
|
||||||
|
* Program function is not considered internal as we want it to show up in exception stack traces.
|
||||||
|
* @param methodName the name of a method
|
||||||
|
* @return true if it looks like an internal Nashorn method name.
|
||||||
|
* @throws NullPointerException if passed null
|
||||||
|
*/
|
||||||
|
public static boolean isInternalMethodName(final String methodName) {
|
||||||
|
return methodName.startsWith(INTERNAL_METHOD_PREFIX) && !methodName.equals(PROGRAM.symbolName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private class representing an access. This can generate code into a method code or
|
* Private class representing an access. This can generate code into a method code or
|
||||||
* a field access.
|
* a field access.
|
||||||
|
@ -66,8 +66,7 @@ enum Condition {
|
|||||||
case GT:
|
case GT:
|
||||||
return IFGT;
|
return IFGT;
|
||||||
default:
|
default:
|
||||||
assert false;
|
throw new UnsupportedOperationException("toUnary:" + c.toString());
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,8 +85,7 @@ enum Condition {
|
|||||||
case GT:
|
case GT:
|
||||||
return IF_ICMPGT;
|
return IF_ICMPGT;
|
||||||
default:
|
default:
|
||||||
assert false;
|
throw new UnsupportedOperationException("toBinary:" + c.toString());
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,7 @@ class ConstantData {
|
|||||||
* @return the index in the constant pool that the object was given
|
* @return the index in the constant pool that the object was given
|
||||||
*/
|
*/
|
||||||
public int add(final Object object) {
|
public int add(final Object object) {
|
||||||
|
assert object != null;
|
||||||
final Object entry = object.getClass().isArray() ? new ArrayWrapper(object) : object;
|
final Object entry = object.getClass().isArray() ? new ArrayWrapper(object) : object;
|
||||||
final Integer value = objectMap.get(entry);
|
final Integer value = objectMap.get(entry);
|
||||||
|
|
||||||
|
113
nashorn/src/jdk/nashorn/internal/codegen/DumpBytecode.java
Normal file
113
nashorn/src/jdk/nashorn/internal/codegen/DumpBytecode.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import jdk.nashorn.internal.runtime.ECMAErrors;
|
||||||
|
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that facilitates dumping bytecode to disk
|
||||||
|
*/
|
||||||
|
final class DumpBytecode {
|
||||||
|
static void dumpBytecode(final ScriptEnvironment env, final byte[] bytecode, final String className) {
|
||||||
|
File dir = null;
|
||||||
|
try {
|
||||||
|
// should could be printed to stderr for generate class?
|
||||||
|
if (env._print_code) {
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("class: " + className).
|
||||||
|
append('\n').
|
||||||
|
append(ClassEmitter.disassemble(bytecode)).
|
||||||
|
append("=====");
|
||||||
|
|
||||||
|
if (env._print_code_dir != null) {
|
||||||
|
|
||||||
|
String name = className;
|
||||||
|
int dollar = name.lastIndexOf('$');
|
||||||
|
if (dollar != -1) {
|
||||||
|
name = name.substring(dollar + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = new File(env._print_code_dir);
|
||||||
|
if (!dir.exists() && !dir.mkdirs()) {
|
||||||
|
throw new IOException(dir.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
File file;
|
||||||
|
String fileName;
|
||||||
|
int uniqueId = 0;
|
||||||
|
do {
|
||||||
|
fileName = name + (uniqueId == 0 ? "" : "_" + uniqueId) + ".bytecode";
|
||||||
|
file = new File(env._print_code_dir, fileName);
|
||||||
|
uniqueId++;
|
||||||
|
} while (file.exists());
|
||||||
|
|
||||||
|
try (final PrintWriter pw = new PrintWriter(new FileOutputStream(file))) {
|
||||||
|
pw.print(sb.toString());
|
||||||
|
pw.flush();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
env.getErr().println(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// should code be dumped to disk - only valid in compile_only mode?
|
||||||
|
if (env._dest_dir != null && env._compile_only) {
|
||||||
|
final String fileName = className.replace('.', File.separatorChar) + ".class";
|
||||||
|
final int index = fileName.lastIndexOf(File.separatorChar);
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
dir = new File(env._dest_dir, fileName.substring(0, index));
|
||||||
|
} else {
|
||||||
|
dir = new File(env._dest_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir.exists() && !dir.mkdirs()) {
|
||||||
|
throw new IOException(dir.toString());
|
||||||
|
}
|
||||||
|
final File file = new File(env._dest_dir, fileName);
|
||||||
|
try (final FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
|
fos.write(bytecode);
|
||||||
|
}
|
||||||
|
Compiler.LOG.info("Wrote class to '" + file.getAbsolutePath() + '\'');
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
Compiler.LOG.warning("Skipping class dump for ",
|
||||||
|
className,
|
||||||
|
": ",
|
||||||
|
ECMAErrors.getMessage(
|
||||||
|
"io.error.cant.write",
|
||||||
|
dir.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,8 +28,9 @@ package jdk.nashorn.internal.codegen;
|
|||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
|
||||||
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName;
|
||||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
|
||||||
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
|
||||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
|
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
|
||||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
|
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
|
||||||
|
|
||||||
@ -51,19 +52,16 @@ import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
|
|||||||
* @param <T> the value type for the fields being written on object creation, e.g. Node
|
* @param <T> the value type for the fields being written on object creation, e.g. Node
|
||||||
* @see jdk.nashorn.internal.ir.Node
|
* @see jdk.nashorn.internal.ir.Node
|
||||||
*/
|
*/
|
||||||
public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
|
||||||
|
|
||||||
private String fieldObjectClassName;
|
private String fieldObjectClassName;
|
||||||
private Class<?> fieldObjectClass;
|
private Class<? extends ScriptObject> fieldObjectClass;
|
||||||
private int fieldCount;
|
private int fieldCount;
|
||||||
private int paddedFieldCount;
|
private int paddedFieldCount;
|
||||||
private int paramCount;
|
private int paramCount;
|
||||||
|
|
||||||
/** array of corresponding values to symbols (null for no values) */
|
|
||||||
private final List<T> values;
|
|
||||||
|
|
||||||
/** call site flags to be used for invocations */
|
/** call site flags to be used for invocations */
|
||||||
private final int callSiteFlags;
|
private final int callSiteFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -73,8 +71,8 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
* @param symbols symbols for fields in object
|
* @param symbols symbols for fields in object
|
||||||
* @param values list of values corresponding to keys
|
* @param values list of values corresponding to keys
|
||||||
*/
|
*/
|
||||||
FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values) {
|
FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples) {
|
||||||
this(codegen, keys, symbols, values, false, false);
|
this(codegen, tuples, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,9 +85,8 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
* @param isScope is this a scope object
|
* @param isScope is this a scope object
|
||||||
* @param hasArguments does the created object have an "arguments" property
|
* @param hasArguments does the created object have an "arguments" property
|
||||||
*/
|
*/
|
||||||
FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values, final boolean isScope, final boolean hasArguments) {
|
FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
|
||||||
super(codegen, keys, symbols, isScope, hasArguments);
|
super(codegen, tuples, isScope, hasArguments);
|
||||||
this.values = values;
|
|
||||||
this.callSiteFlags = codegen.getCallSiteFlags();
|
this.callSiteFlags = codegen.getCallSiteFlags();
|
||||||
|
|
||||||
countFields();
|
countFields();
|
||||||
@ -105,7 +102,19 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
protected void makeObject(final MethodEmitter method) {
|
protected void makeObject(final MethodEmitter method) {
|
||||||
makeMap();
|
makeMap();
|
||||||
|
|
||||||
method._new(getClassName()).dup(); // create instance
|
final String className = getClassName();
|
||||||
|
try {
|
||||||
|
// NOTE: we must load the actual structure class here, because the API operates with Nashorn Type objects,
|
||||||
|
// and Type objects need a loaded class, for better or worse. We also have to be specific and use the type
|
||||||
|
// of the actual structure class, we can't generalize it to e.g. Type.typeFor(ScriptObject.class) as the
|
||||||
|
// exact type information is needed for generating continuations in rest-of methods. If we didn't do this,
|
||||||
|
// object initializers like { x: arr[i] } would fail during deoptimizing compilation on arr[i], as the
|
||||||
|
// values restored from the RewriteException would be cast to "ScriptObject" instead of to e.g. "JO4", and
|
||||||
|
// subsequently the "PUTFIELD J04.L0" instruction in the continuation code would fail bytecode verification.
|
||||||
|
method._new(Context.forStructureClass(className.replace('/', '.'))).dup();
|
||||||
|
} catch (final ClassNotFoundException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
loadMap(method); //load the map
|
loadMap(method); //load the map
|
||||||
|
|
||||||
if (isScope()) {
|
if (isScope()) {
|
||||||
@ -113,31 +122,27 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
|
|
||||||
if (hasArguments()) {
|
if (hasArguments()) {
|
||||||
method.loadCompilerConstant(ARGUMENTS);
|
method.loadCompilerConstant(ARGUMENTS);
|
||||||
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
|
method.invoke(constructorNoLookup(className, PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
|
||||||
} else {
|
} else {
|
||||||
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
|
method.invoke(constructorNoLookup(className, PropertyMap.class, ScriptObject.class));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class));
|
method.invoke(constructorNoLookup(className, PropertyMap.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set values.
|
// Set values.
|
||||||
final Iterator<Symbol> symbolIter = symbols.iterator();
|
final Iterator<MapTuple<T>> iter = tuples.iterator();
|
||||||
final Iterator<String> keyIter = keys.iterator();
|
|
||||||
final Iterator<T> valueIter = values.iterator();
|
|
||||||
|
|
||||||
while (symbolIter.hasNext()) {
|
|
||||||
final Symbol symbol = symbolIter.next();
|
|
||||||
final String key = keyIter.next();
|
|
||||||
final T value = valueIter.next();
|
|
||||||
|
|
||||||
if (symbol != null && value != null) {
|
|
||||||
final int index = getArrayIndex(key);
|
|
||||||
|
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
final MapTuple<T> tuple = iter.next();
|
||||||
|
//we only load when we have both symbols and values (which can be == the symbol)
|
||||||
|
//if we didn't load, we need an array property
|
||||||
|
if (tuple.symbol != null && tuple.value != null) {
|
||||||
|
final int index = getArrayIndex(tuple.key);
|
||||||
if (!isValidArrayIndex(index)) {
|
if (!isValidArrayIndex(index)) {
|
||||||
putField(method, key, symbol.getFieldIndex(), value);
|
putField(method, tuple.key, tuple.symbol.getFieldIndex(), tuple);
|
||||||
} else {
|
} else {
|
||||||
putSlot(method, ArrayIndex.toLongIndex(index), value);
|
putSlot(method, ArrayIndex.toLongIndex(index), tuple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,13 +155,6 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
return propertyMap;
|
return propertyMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Technique for loading an initial value. Defined by anonymous subclasses in code gen.
|
|
||||||
*
|
|
||||||
* @param value Value to load.
|
|
||||||
*/
|
|
||||||
protected abstract void loadValue(T value);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a value in a field of the generated class object.
|
* Store a value in a field of the generated class object.
|
||||||
*
|
*
|
||||||
@ -165,12 +163,18 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
* @param fieldIndex Field number.
|
* @param fieldIndex Field number.
|
||||||
* @param value Value to store.
|
* @param value Value to store.
|
||||||
*/
|
*/
|
||||||
private void putField(final MethodEmitter method, final String key, final int fieldIndex, final T value) {
|
private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) {
|
||||||
method.dup();
|
method.dup();
|
||||||
|
|
||||||
loadValue(value);
|
loadTuple(method, tuple);
|
||||||
method.convert(OBJECT);
|
|
||||||
method.putField(getClassName(), ObjectClassGenerator.getFieldName(fieldIndex, Type.OBJECT), typeDescriptor(Object.class));
|
final boolean isPrimitive = tuple.isPrimitive();
|
||||||
|
final Type fieldType = isPrimitive ? PRIMITIVE_FIELD_TYPE : Type.OBJECT;
|
||||||
|
final String fieldClass = getClassName();
|
||||||
|
final String fieldName = getFieldName(fieldIndex, fieldType);
|
||||||
|
final String fieldDesc = typeDescriptor(fieldType.getTypeClass());
|
||||||
|
|
||||||
|
method.putField(fieldClass, fieldName, fieldDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,14 +184,14 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
* @param index Slot index.
|
* @param index Slot index.
|
||||||
* @param value Value to store.
|
* @param value Value to store.
|
||||||
*/
|
*/
|
||||||
private void putSlot(final MethodEmitter method, final long index, final T value) {
|
private void putSlot(final MethodEmitter method, final long index, final MapTuple<T> tuple) {
|
||||||
method.dup();
|
method.dup();
|
||||||
if (JSType.isRepresentableAsInt(index)) {
|
if (JSType.isRepresentableAsInt(index)) {
|
||||||
method.load((int) index);
|
method.load((int)index);
|
||||||
} else {
|
} else {
|
||||||
method.load(index);
|
method.load(index);
|
||||||
}
|
}
|
||||||
loadValue(value);
|
loadTuple(method, tuple, false); //we don't pack array like objects
|
||||||
method.dynamicSetIndex(callSiteFlags);
|
method.dynamicSetIndex(callSiteFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +224,8 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
|||||||
* Tally the number of fields and parameters.
|
* Tally the number of fields and parameters.
|
||||||
*/
|
*/
|
||||||
private void countFields() {
|
private void countFields() {
|
||||||
for (final Symbol symbol : this.symbols) {
|
for (final MapTuple<T> tuple : tuples) {
|
||||||
|
final Symbol symbol = tuple.symbol;
|
||||||
if (symbol != null) {
|
if (symbol != null) {
|
||||||
if (hasArguments() && symbol.isParam()) {
|
if (hasArguments() && symbol.isParam()) {
|
||||||
symbol.setFieldIndex(paramCount++);
|
symbol.setFieldIndex(paramCount++);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
package jdk.nashorn.internal.codegen;
|
package jdk.nashorn.internal.codegen;
|
||||||
|
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
||||||
|
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||||
|
|
||||||
import jdk.nashorn.internal.ir.BinaryNode;
|
import jdk.nashorn.internal.ir.BinaryNode;
|
||||||
@ -38,7 +39,6 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
|||||||
import jdk.nashorn.internal.ir.LexicalContext;
|
import jdk.nashorn.internal.ir.LexicalContext;
|
||||||
import jdk.nashorn.internal.ir.Node;
|
import jdk.nashorn.internal.ir.Node;
|
||||||
import jdk.nashorn.internal.ir.Symbol;
|
import jdk.nashorn.internal.ir.Symbol;
|
||||||
import jdk.nashorn.internal.ir.TemporarySymbols;
|
|
||||||
import jdk.nashorn.internal.ir.UnaryNode;
|
import jdk.nashorn.internal.ir.UnaryNode;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||||
import jdk.nashorn.internal.parser.Token;
|
import jdk.nashorn.internal.parser.Token;
|
||||||
@ -62,11 +62,8 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
|
|
||||||
private static final DebugLogger LOG = new DebugLogger("finalize");
|
private static final DebugLogger LOG = new DebugLogger("finalize");
|
||||||
|
|
||||||
private final TemporarySymbols temporarySymbols;
|
FinalizeTypes() {
|
||||||
|
|
||||||
FinalizeTypes(final TemporarySymbols temporarySymbols) {
|
|
||||||
super(new LexicalContext());
|
super(new LexicalContext());
|
||||||
this.temporarySymbols = temporarySymbols;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -106,29 +103,41 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
|
public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
|
||||||
temporarySymbols.reuse();
|
|
||||||
return expressionStatement.setExpression(discard(expressionStatement.getExpression()));
|
return expressionStatement.setExpression(discard(expressionStatement.getExpression()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||||
if (functionNode.isLazy()) {
|
// TODO: now that Splitter comes before Attr, these can probably all be moved to Attr.
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
|
// If the function doesn't need a callee, we ensure its CALLEE symbol doesn't get a slot. We can't do this
|
||||||
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
|
// earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the need for the
|
||||||
// need for the callee.
|
// callee.
|
||||||
if (!functionNode.needsCallee()) {
|
if (!functionNode.needsCallee()) {
|
||||||
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
|
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
|
||||||
}
|
}
|
||||||
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope and none of
|
// Similar reasoning applies to SCOPE symbol: if the function doesn't need either parent scope and none of its
|
||||||
// its blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope
|
// blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope
|
||||||
// earlier than this phase.
|
// earlier than this phase.
|
||||||
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
|
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
|
||||||
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
|
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
|
||||||
}
|
}
|
||||||
|
// Also, we must wait until after Splitter to see if the function ended up needing the RETURN symbol.
|
||||||
|
if (!functionNode.usesReturnSymbol()) {
|
||||||
|
functionNode.compilerConstant(RETURN).setNeedsSlot(false);
|
||||||
|
}
|
||||||
|
// Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
|
||||||
|
if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
|
||||||
|
final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
|
||||||
|
if(selfSymbol != null) {
|
||||||
|
if(selfSymbol.isFunctionSelf()) {
|
||||||
|
selfSymbol.setNeedsSlot(false);
|
||||||
|
selfSymbol.clearFlag(Symbol.IS_VAR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert functionNode.isProgram();
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,16 +192,16 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Expression discard(final Expression node) {
|
private static Expression discard(final Expression expr) {
|
||||||
if (node.getSymbol() != null) {
|
if (expr.getSymbol() != null) {
|
||||||
final UnaryNode discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node);
|
UnaryNode discard = new UnaryNode(Token.recast(expr.getToken(), TokenType.DISCARD), expr);
|
||||||
//discard never has a symbol in the discard node - then it would be a nop
|
//discard never has a symbol in the discard node - then it would be a nop
|
||||||
assert !node.isTerminal();
|
assert !expr.isTerminal();
|
||||||
return discard;
|
return discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
// node has no result (symbol) so we can keep it the way it is
|
// node has no result (symbol) so we can keep it the way it is
|
||||||
return node;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,11 +79,6 @@ final class FoldConstants extends NodeVisitor<LexicalContext> {
|
|||||||
return binaryNode;
|
return binaryNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
|
||||||
return !functionNode.isLazy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||||
return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED);
|
return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED);
|
||||||
@ -163,7 +158,7 @@ final class FoldConstants extends NodeVisitor<LexicalContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LiteralNode<?> eval() {
|
protected LiteralNode<?> eval() {
|
||||||
final Node rhsNode = parent.rhs();
|
final Node rhsNode = parent.getExpression();
|
||||||
|
|
||||||
if (!(rhsNode instanceof LiteralNode)) {
|
if (!(rhsNode instanceof LiteralNode)) {
|
||||||
return null;
|
return null;
|
||||||
@ -311,8 +306,8 @@ final class FoldConstants extends NodeVisitor<LexicalContext> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value);
|
isInteger &= JSType.isRepresentableAsInt(value) && !JSType.isNegativeZero(value);
|
||||||
isLong &= value != 0.0 && JSType.isRepresentableAsLong(value);
|
isLong &= JSType.isRepresentableAsLong(value) && !JSType.isNegativeZero(value);
|
||||||
|
|
||||||
if (isInteger) {
|
if (isInteger) {
|
||||||
return LiteralNode.newInstance(token, finish, (int)value);
|
return LiteralNode.newInstance(token, finish, (int)value);
|
||||||
|
@ -194,6 +194,14 @@ public final class FunctionSignature {
|
|||||||
return paramTypes.length;
|
return paramTypes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the param types for this function signature
|
||||||
|
* @return cloned vector of param types
|
||||||
|
*/
|
||||||
|
public Type[] getParamTypes() {
|
||||||
|
return paramTypes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the {@link MethodType} for this function signature
|
* Return the {@link MethodType} for this function signature
|
||||||
* @return the method type
|
* @return the method type
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
package jdk.nashorn.internal.codegen;
|
package jdk.nashorn.internal.codegen;
|
||||||
|
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.runtime.Debug;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction for labels, separating a label from the underlying
|
* Abstraction for labels, separating a label from the underlying
|
||||||
@ -39,19 +38,22 @@ public final class Label {
|
|||||||
//and correct opcode selection. one per label as a label may be a
|
//and correct opcode selection. one per label as a label may be a
|
||||||
//join point
|
//join point
|
||||||
static final class Stack {
|
static final class Stack {
|
||||||
|
static final int NON_LOAD = -1;
|
||||||
|
|
||||||
Type[] data = new Type[8];
|
Type[] data = new Type[8];
|
||||||
|
int[] localLoads = new int[8];
|
||||||
int sp = 0;
|
int sp = 0;
|
||||||
|
|
||||||
Stack() {
|
Stack() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stack(final Type[] type, final int sp) {
|
private Stack(final Stack original) {
|
||||||
this();
|
this();
|
||||||
this.data = new Type[type.length];
|
this.sp = original.sp;
|
||||||
this.sp = sp;
|
this.data = new Type[original.data.length];
|
||||||
for (int i = 0; i < sp; i++) {
|
System.arraycopy(original.data, 0, data, 0, sp);
|
||||||
data[i] = type[i];
|
this.localLoads = new int[original.localLoads.length];
|
||||||
}
|
System.arraycopy(original.localLoads, 0, localLoads, 0, sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isEmpty() {
|
boolean isEmpty() {
|
||||||
@ -62,7 +64,7 @@ public final class Label {
|
|||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isEquivalentTo(final Stack other) {
|
boolean isEquivalentInTypesTo(final Stack other) {
|
||||||
if (sp != other.sp) {
|
if (sp != other.sp) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -81,12 +83,15 @@ public final class Label {
|
|||||||
void push(final Type type) {
|
void push(final Type type) {
|
||||||
if (data.length == sp) {
|
if (data.length == sp) {
|
||||||
final Type[] newData = new Type[sp * 2];
|
final Type[] newData = new Type[sp * 2];
|
||||||
for (int i = 0; i < sp; i++) {
|
final int[] newLocalLoad = new int[sp * 2];
|
||||||
newData[i] = data[i];
|
System.arraycopy(data, 0, newData, 0, sp);
|
||||||
}
|
System.arraycopy(localLoads, 0, newLocalLoad, 0, sp);
|
||||||
data = newData;
|
data = newData;
|
||||||
|
localLoads = newLocalLoad;
|
||||||
}
|
}
|
||||||
data[sp++] = type;
|
data[sp] = type;
|
||||||
|
localLoads[sp] = NON_LOAD;
|
||||||
|
sp++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type peek() {
|
Type peek() {
|
||||||
@ -98,12 +103,67 @@ public final class Label {
|
|||||||
return pos < 0 ? null : data[pos];
|
return pos < 0 ? null : data[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the top <tt>count</tt> types on the stack without modifying it.
|
||||||
|
*
|
||||||
|
* @param count number of types to return
|
||||||
|
* @return array of Types
|
||||||
|
*/
|
||||||
|
Type[] getTopTypes(final int count) {
|
||||||
|
final Type[] topTypes = new Type[count];
|
||||||
|
System.arraycopy(data, sp - count, topTypes, 0, count);
|
||||||
|
return topTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] getLocalLoads(final int from, final int to) {
|
||||||
|
final int count = to - from;
|
||||||
|
final int[] topLocalLoads = new int[count];
|
||||||
|
System.arraycopy(localLoads, from, topLocalLoads, 0, count);
|
||||||
|
return topLocalLoads;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When joining branches, local loads that differ on different branches are invalidated.
|
||||||
|
* @param other the stack from the other branch.
|
||||||
|
*/
|
||||||
|
void mergeLocalLoads(final Stack other) {
|
||||||
|
final int[] otherLoads = other.localLoads;
|
||||||
|
for(int i = 0; i < sp; ++i) {
|
||||||
|
if(localLoads[i] != otherLoads[i]) {
|
||||||
|
localLoads[i] = NON_LOAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Type pop() {
|
Type pop() {
|
||||||
return data[--sp];
|
return data[--sp];
|
||||||
}
|
}
|
||||||
|
|
||||||
Stack copy() {
|
Stack copy() {
|
||||||
return new Stack(data, sp);
|
return new Stack(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTopLocalLoad() {
|
||||||
|
return localLoads[sp - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void markLocalLoad(final int slot) {
|
||||||
|
localLoads[sp - 1] = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we store a value in a local slot, it invalidates any on-stack loads from that same slot, as the values
|
||||||
|
* could have changed.
|
||||||
|
* @param slot the slot written to
|
||||||
|
* @param slotCount the size of the value, either 1 or 2 slots
|
||||||
|
*/
|
||||||
|
void markLocalStore(final int slot, final int slotCount) {
|
||||||
|
for(int i = 0; i < sp; ++i) {
|
||||||
|
final int load = localLoads[i];
|
||||||
|
if(load == slot || load == slot + slotCount - 1) {
|
||||||
|
localLoads[i] = NON_LOAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -128,6 +188,12 @@ public final class Label {
|
|||||||
/** ASM representation of this label */
|
/** ASM representation of this label */
|
||||||
private jdk.internal.org.objectweb.asm.Label label;
|
private jdk.internal.org.objectweb.asm.Label label;
|
||||||
|
|
||||||
|
/** Id for debugging purposes, remove if footprint becomes unmanageable */
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
/** Next id for debugging purposes, remove if footprint becomes unmanageable */
|
||||||
|
private static int nextId = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -136,6 +202,7 @@ public final class Label {
|
|||||||
public Label(final String name) {
|
public Label(final String name) {
|
||||||
super();
|
super();
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.id = nextId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,6 +213,7 @@ public final class Label {
|
|||||||
public Label(final Label label) {
|
public Label(final Label label) {
|
||||||
super();
|
super();
|
||||||
this.name = label.name;
|
this.name = label.name;
|
||||||
|
this.id = label.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -166,6 +234,6 @@ public final class Label {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name + '_' + Debug.id(this);
|
return name + '_' + id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen;
|
||||||
|
|
||||||
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates the information for restoring the local state when continuing execution after a rewrite triggered by
|
||||||
|
* an optimistic assumption failure. An instance of this class is specific to a program point.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LocalStateRestorationInfo {
|
||||||
|
private final Type[] localVariableTypes;
|
||||||
|
private final int[] stackLoads;
|
||||||
|
|
||||||
|
LocalStateRestorationInfo(Type[] localVariableTypes, final int[] stackLoads) {
|
||||||
|
this.localVariableTypes = localVariableTypes;
|
||||||
|
this.stackLoads = stackLoads;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the types of the local variables at the continuation of a program point.
|
||||||
|
* @return the types of the local variables at the continuation of a program point.
|
||||||
|
*/
|
||||||
|
public Type[] getLocalVariableTypes() {
|
||||||
|
return localVariableTypes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the indices of local variables that need to be loaded on stack before jumping to the continuation of the
|
||||||
|
* program point.
|
||||||
|
* @return the indices of local variables that need to be loaded on stack.
|
||||||
|
*/
|
||||||
|
public int[] getStackLoads() {
|
||||||
|
return stackLoads.clone();
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import jdk.nashorn.internal.ir.BaseNode;
|
import jdk.nashorn.internal.ir.BaseNode;
|
||||||
@ -235,12 +236,18 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
|
|||||||
newForNode = forNode.setTest(lc, null);
|
newForNode = forNode.setTest(lc, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return addStatement(checkEscape(newForNode));
|
newForNode = checkEscape(newForNode);
|
||||||
}
|
if(newForNode.isForIn()) {
|
||||||
|
// Wrap it in a block so its internally created iterator is restricted in scope
|
||||||
@Override
|
BlockStatement b = BlockStatement.createReplacement(newForNode, Collections.singletonList((Statement)newForNode));
|
||||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
if(newForNode.isTerminal()) {
|
||||||
return !functionNode.isLazy();
|
b = b.setBlock(b.getBlock().setIsTerminal(null, true));
|
||||||
|
}
|
||||||
|
addStatement(b);
|
||||||
|
} else {
|
||||||
|
addStatement(newForNode);
|
||||||
|
}
|
||||||
|
return newForNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -308,7 +315,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
|
|||||||
final long token = tryNode.getToken();
|
final long token = tryNode.getToken();
|
||||||
final int finish = tryNode.getFinish();
|
final int finish = tryNode.getFinish();
|
||||||
|
|
||||||
final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName("catch_all"));
|
final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName(CompilerConstants.EXCEPTION_PREFIX.symbolName()));
|
||||||
|
|
||||||
final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW));
|
final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW));
|
||||||
assert catchBody.isTerminal(); //ends with throw, so terminal
|
assert catchBody.isTerminal(); //ends with throw, so terminal
|
||||||
@ -340,6 +347,8 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
|
|||||||
private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
|
private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
|
||||||
assert tryNode.getFinallyBody() == null;
|
assert tryNode.getFinallyBody() == null;
|
||||||
|
|
||||||
|
final LexicalContext lowerLc = lc;
|
||||||
|
|
||||||
final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||||
final List<Node> insideTry = new ArrayList<>();
|
final List<Node> insideTry = new ArrayList<>();
|
||||||
|
|
||||||
@ -388,6 +397,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
|
|||||||
//still in the try block, store it in a result value and return it afterwards
|
//still in the try block, store it in a result value and return it afterwards
|
||||||
resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
|
resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
|
||||||
newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
|
newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
|
||||||
|
lowerLc.setFlag(lowerLc.getCurrentFunction(), FunctionNode.USES_RETURN_SYMBOL);
|
||||||
} else {
|
} else {
|
||||||
resultNode = null;
|
resultNode = null;
|
||||||
}
|
}
|
||||||
@ -620,10 +630,11 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
|
|||||||
return !escapes.isEmpty();
|
return !escapes.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoopNode checkEscape(final LoopNode loopNode) {
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T extends LoopNode> T checkEscape(final T loopNode) {
|
||||||
final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
|
final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
|
||||||
if (escapes) {
|
if (escapes) {
|
||||||
return loopNode.
|
return (T)loopNode.
|
||||||
setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
|
setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
|
||||||
setControlFlowEscapes(lc, escapes);
|
setControlFlowEscapes(lc, escapes);
|
||||||
}
|
}
|
||||||
|
@ -34,19 +34,19 @@ import jdk.nashorn.internal.ir.Symbol;
|
|||||||
import jdk.nashorn.internal.runtime.AccessorProperty;
|
import jdk.nashorn.internal.runtime.AccessorProperty;
|
||||||
import jdk.nashorn.internal.runtime.Property;
|
import jdk.nashorn.internal.runtime.Property;
|
||||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||||
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
|
import jdk.nashorn.internal.runtime.SpillProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that creates PropertyMap sent to script object constructors.
|
* Class that creates PropertyMap sent to script object constructors.
|
||||||
|
* @param <T> value type for tuples, e.g. Symbol
|
||||||
*/
|
*/
|
||||||
public class MapCreator {
|
public class MapCreator<T> {
|
||||||
/** Object structure for objects associated with this map */
|
/** Object structure for objects associated with this map */
|
||||||
private final Class<?> structure;
|
private final Class<?> structure;
|
||||||
|
|
||||||
/** key set for object map */
|
/** key set for object map */
|
||||||
final List<String> keys;
|
private final List<MapTuple<T>> tuples;
|
||||||
|
|
||||||
/** corresponding symbol set for object map */
|
|
||||||
final List<Symbol> symbols;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -55,10 +55,9 @@ public class MapCreator {
|
|||||||
* @param keys list of keys for map
|
* @param keys list of keys for map
|
||||||
* @param symbols list of symbols for map
|
* @param symbols list of symbols for map
|
||||||
*/
|
*/
|
||||||
MapCreator(final Class<?> structure, final List<String> keys, final List<Symbol> symbols) {
|
MapCreator(final Class<? extends ScriptObject> structure, final List<MapTuple<T>> tuples) {
|
||||||
this.structure = structure;
|
this.structure = structure;
|
||||||
this.keys = keys;
|
this.tuples = tuples;
|
||||||
this.symbols = symbols;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,14 +71,14 @@ public class MapCreator {
|
|||||||
*/
|
*/
|
||||||
PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
|
PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
|
||||||
final List<Property> properties = new ArrayList<>();
|
final List<Property> properties = new ArrayList<>();
|
||||||
assert keys != null;
|
assert tuples != null;
|
||||||
|
|
||||||
for (int i = 0, length = keys.size(); i < length; i++) {
|
|
||||||
final String key = keys.get(i);
|
|
||||||
final Symbol symbol = symbols.get(i);
|
|
||||||
|
|
||||||
|
for (final MapTuple<T> tuple : tuples) {
|
||||||
|
final String key = tuple.key;
|
||||||
|
final Symbol symbol = tuple.symbol;
|
||||||
|
final Class<?> initialType = tuple.getValueType();
|
||||||
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
|
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
|
||||||
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex()));
|
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex(), initialType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,14 +88,14 @@ public class MapCreator {
|
|||||||
PropertyMap makeSpillMap(final boolean hasArguments) {
|
PropertyMap makeSpillMap(final boolean hasArguments) {
|
||||||
final List<Property> properties = new ArrayList<>();
|
final List<Property> properties = new ArrayList<>();
|
||||||
int spillIndex = 0;
|
int spillIndex = 0;
|
||||||
assert keys != null;
|
assert tuples != null;
|
||||||
|
|
||||||
for (int i = 0, length = keys.size(); i < length; i++) {
|
for (final MapTuple<T> tuple : tuples) {
|
||||||
final String key = keys.get(i);
|
final String key = tuple.key;
|
||||||
final Symbol symbol = symbols.get(i);
|
final Symbol symbol = tuple.symbol;
|
||||||
|
|
||||||
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
|
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
|
||||||
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
|
properties.add(new SpillProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,17 +118,13 @@ public class MapCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasArguments) {
|
if (hasArguments) {
|
||||||
flags |= Property.IS_ALWAYS_OBJECT | Property.HAS_ARGUMENTS;
|
flags |= Property.HAS_ARGUMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol.isScope()) {
|
if (symbol.isScope()) {
|
||||||
flags |= Property.NOT_CONFIGURABLE;
|
flags |= Property.NOT_CONFIGURABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol.canBePrimitive()) {
|
|
||||||
flags |= Property.CAN_BE_PRIMITIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol.canBeUndefined()) {
|
if (symbol.canBeUndefined()) {
|
||||||
flags |= Property.CAN_BE_UNDEFINED;
|
flags |= Property.CAN_BE_UNDEFINED;
|
||||||
}
|
}
|
||||||
@ -140,5 +135,4 @@ public class MapCreator {
|
|||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
62
nashorn/src/jdk/nashorn/internal/codegen/MapTuple.java
Normal file
62
nashorn/src/jdk/nashorn/internal/codegen/MapTuple.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
|
||||||
|
import jdk.nashorn.internal.ir.Symbol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tuple of values used for map creation
|
||||||
|
* @param <T> value type
|
||||||
|
*/
|
||||||
|
class MapTuple<T> {
|
||||||
|
final String key;
|
||||||
|
final Symbol symbol;
|
||||||
|
final T value;
|
||||||
|
|
||||||
|
MapTuple(final String key, final Symbol symbol) {
|
||||||
|
this(key, symbol, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
MapTuple(final String key, final Symbol symbol, final T value) {
|
||||||
|
this.key = key;
|
||||||
|
this.symbol = symbol;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getValueType() {
|
||||||
|
return OBJECT_FIELDS_ONLY ? Object.class : null; //until proven otherwise we are undefined, see NASHORN-592 int.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isPrimitive() {
|
||||||
|
return !OBJECT_FIELDS_ONLY && getValueType().isPrimitive() && getValueType() != boolean.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[key=" + key + ", symbol=" + symbol + ", value=" + value + " (" + (value == null ? "null" : value.getClass().getSimpleName()) +")]";
|
||||||
|
}
|
||||||
|
}
|
@ -64,9 +64,14 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup
|
|||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
||||||
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
|
||||||
|
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
|
||||||
|
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
|
||||||
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import jdk.internal.dynalink.support.NameCodec;
|
import jdk.internal.dynalink.support.NameCodec;
|
||||||
@ -89,6 +94,7 @@ import jdk.nashorn.internal.runtime.ArgumentSetter;
|
|||||||
import jdk.nashorn.internal.runtime.Debug;
|
import jdk.nashorn.internal.runtime.Debug;
|
||||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||||
import jdk.nashorn.internal.runtime.JSType;
|
import jdk.nashorn.internal.runtime.JSType;
|
||||||
|
import jdk.nashorn.internal.runtime.RewriteException;
|
||||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||||
@ -126,6 +132,8 @@ public class MethodEmitter implements Emitter {
|
|||||||
/** The script environment */
|
/** The script environment */
|
||||||
private final ScriptEnvironment env;
|
private final ScriptEnvironment env;
|
||||||
|
|
||||||
|
private final List<Type> localVariableTypes = new ArrayList<>();
|
||||||
|
|
||||||
/** Threshold in chars for when string constants should be split */
|
/** Threshold in chars for when string constants should be split */
|
||||||
static final int LARGE_STRING_THRESHOLD = 32 * 1024;
|
static final int LARGE_STRING_THRESHOLD = 32 * 1024;
|
||||||
|
|
||||||
@ -153,6 +161,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
/** Bootstrap for runtime node indy:s */
|
/** Bootstrap for runtime node indy:s */
|
||||||
private static final Handle RUNTIMEBOOTSTRAP = new Handle(H_INVOKESTATIC, RuntimeCallSite.BOOTSTRAP.className(), RuntimeCallSite.BOOTSTRAP.name(), RuntimeCallSite.BOOTSTRAP.descriptor());
|
private static final Handle RUNTIMEBOOTSTRAP = new Handle(H_INVOKESTATIC, RuntimeCallSite.BOOTSTRAP.className(), RuntimeCallSite.BOOTSTRAP.name(), RuntimeCallSite.BOOTSTRAP.descriptor());
|
||||||
|
|
||||||
|
/** Bootstrap for array populators */
|
||||||
|
private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor - internal use from ClassEmitter only
|
* Constructor - internal use from ClassEmitter only
|
||||||
* @see ClassEmitter#method
|
* @see ClassEmitter#method
|
||||||
@ -203,6 +214,11 @@ public class MethodEmitter implements Emitter {
|
|||||||
classEmitter.endMethod(this);
|
classEmitter.endMethod(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createNewStack() {
|
||||||
|
assert stack == null;
|
||||||
|
newStack();
|
||||||
|
}
|
||||||
|
|
||||||
private void newStack() {
|
private void newStack() {
|
||||||
stack = new Label.Stack();
|
stack = new Label.Stack();
|
||||||
}
|
}
|
||||||
@ -216,7 +232,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* Push a type to the existing stack
|
* Push a type to the existing stack
|
||||||
* @param type the type
|
* @param type the type
|
||||||
*/
|
*/
|
||||||
private void pushType(final Type type) {
|
void pushType(final Type type) {
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
stack.push(type);
|
stack.push(type);
|
||||||
}
|
}
|
||||||
@ -230,7 +246,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the type that was retrieved
|
* @return the type that was retrieved
|
||||||
*/
|
*/
|
||||||
private Type popType(final Type expected) {
|
private Type popType(final Type expected) {
|
||||||
final Type type = stack.pop();
|
final Type type = popType();
|
||||||
assert type.isObject() && expected.isObject() ||
|
assert type.isObject() && expected.isObject() ||
|
||||||
type.isEquivalentTo(expected) : type + " is not compatible with " + expected;
|
type.isEquivalentTo(expected) : type + " is not compatible with " + expected;
|
||||||
return type;
|
return type;
|
||||||
@ -252,7 +268,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the type
|
* @return the type
|
||||||
*/
|
*/
|
||||||
private NumericType popNumeric() {
|
private NumericType popNumeric() {
|
||||||
final Type type = stack.pop();
|
final Type type = popType();
|
||||||
assert type.isNumeric() : type + " is not numeric";
|
assert type.isNumeric() : type + " is not numeric";
|
||||||
return (NumericType)type;
|
return (NumericType)type;
|
||||||
}
|
}
|
||||||
@ -264,7 +280,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the type
|
* @return the type
|
||||||
*/
|
*/
|
||||||
private BitwiseType popInteger() {
|
private BitwiseType popInteger() {
|
||||||
final Type type = stack.pop();
|
final Type type = popType();
|
||||||
assert type.isInteger() || type.isLong() : type + " is not an integer or long";
|
assert type.isInteger() || type.isLong() : type + " is not an integer or long";
|
||||||
return (BitwiseType)type;
|
return (BitwiseType)type;
|
||||||
}
|
}
|
||||||
@ -276,7 +292,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the type
|
* @return the type
|
||||||
*/
|
*/
|
||||||
private ArrayType popArray() {
|
private ArrayType popArray() {
|
||||||
final Type type = stack.pop();
|
final Type type = popType();
|
||||||
assert type.isArray() : type;
|
assert type.isArray() : type;
|
||||||
return (ArrayType)type;
|
return (ArrayType)type;
|
||||||
}
|
}
|
||||||
@ -307,13 +323,14 @@ public class MethodEmitter implements Emitter {
|
|||||||
* object type on the stack
|
* object type on the stack
|
||||||
*
|
*
|
||||||
* @param classDescriptor class descriptor for the object type
|
* @param classDescriptor class descriptor for the object type
|
||||||
|
* @param type the type of the new object
|
||||||
*
|
*
|
||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter _new(final String classDescriptor) {
|
MethodEmitter _new(final String classDescriptor, final Type type) {
|
||||||
debug("new", classDescriptor);
|
debug("new", classDescriptor);
|
||||||
method.visitTypeInsn(NEW, classDescriptor);
|
method.visitTypeInsn(NEW, classDescriptor);
|
||||||
pushType(Type.OBJECT);
|
pushType(type);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +343,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter _new(final Class<?> clazz) {
|
MethodEmitter _new(final Class<?> clazz) {
|
||||||
return _new(className(clazz));
|
return _new(className(clazz), Type.typeFor(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -358,25 +375,40 @@ public class MethodEmitter implements Emitter {
|
|||||||
debug("dup", depth);
|
debug("dup", depth);
|
||||||
|
|
||||||
switch (depth) {
|
switch (depth) {
|
||||||
case 0:
|
case 0: {
|
||||||
|
final int l0 = stack.getTopLocalLoad();
|
||||||
pushType(peekType());
|
pushType(peekType());
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
|
final int l0 = stack.getTopLocalLoad();
|
||||||
final Type p0 = popType();
|
final Type p0 = popType();
|
||||||
|
final int l1 = stack.getTopLocalLoad();
|
||||||
final Type p1 = popType();
|
final Type p1 = popType();
|
||||||
pushType(p0);
|
pushType(p0);
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
pushType(p1);
|
pushType(p1);
|
||||||
|
stack.markLocalLoad(l1);
|
||||||
pushType(p0);
|
pushType(p0);
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
|
final int l0 = stack.getTopLocalLoad();
|
||||||
final Type p0 = popType();
|
final Type p0 = popType();
|
||||||
|
final int l1 = stack.getTopLocalLoad();
|
||||||
final Type p1 = popType();
|
final Type p1 = popType();
|
||||||
|
final int l2 = stack.getTopLocalLoad();
|
||||||
final Type p2 = popType();
|
final Type p2 = popType();
|
||||||
pushType(p0);
|
pushType(p0);
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
pushType(p2);
|
pushType(p2);
|
||||||
|
stack.markLocalLoad(l2);
|
||||||
pushType(p1);
|
pushType(p1);
|
||||||
|
stack.markLocalLoad(l1);
|
||||||
pushType(p0);
|
pushType(p0);
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -398,13 +430,22 @@ public class MethodEmitter implements Emitter {
|
|||||||
debug("dup2");
|
debug("dup2");
|
||||||
|
|
||||||
if (peekType().isCategory2()) {
|
if (peekType().isCategory2()) {
|
||||||
|
final int l0 = stack.getTopLocalLoad();
|
||||||
pushType(peekType());
|
pushType(peekType());
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
} else {
|
} else {
|
||||||
final Type type = get2();
|
final int l0 = stack.getTopLocalLoad();
|
||||||
pushType(type);
|
final Type p0 = popType();
|
||||||
pushType(type);
|
final int l1 = stack.getTopLocalLoad();
|
||||||
pushType(type);
|
final Type p1 = popType();
|
||||||
pushType(type);
|
pushType(p0);
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
|
pushType(p1);
|
||||||
|
stack.markLocalLoad(l1);
|
||||||
|
pushType(p0);
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
|
pushType(p1);
|
||||||
|
stack.markLocalLoad(l1);
|
||||||
}
|
}
|
||||||
method.visitInsn(DUP2);
|
method.visitInsn(DUP2);
|
||||||
return this;
|
return this;
|
||||||
@ -454,16 +495,34 @@ public class MethodEmitter implements Emitter {
|
|||||||
MethodEmitter swap() {
|
MethodEmitter swap() {
|
||||||
debug("swap");
|
debug("swap");
|
||||||
|
|
||||||
|
final int l0 = stack.getTopLocalLoad();
|
||||||
final Type p0 = popType();
|
final Type p0 = popType();
|
||||||
|
final int l1 = stack.getTopLocalLoad();
|
||||||
final Type p1 = popType();
|
final Type p1 = popType();
|
||||||
p0.swap(method, p1);
|
p0.swap(method, p1);
|
||||||
|
|
||||||
pushType(p0);
|
pushType(p0);
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
pushType(p1);
|
pushType(p1);
|
||||||
|
stack.markLocalLoad(l1);
|
||||||
debug("after ", p0, p1);
|
debug("after ", p0, p1);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pack() {
|
||||||
|
final Type type = peekType();
|
||||||
|
if (type.isInteger()) {
|
||||||
|
convert(PRIMITIVE_FIELD_TYPE);
|
||||||
|
} else if (type.isLong()) {
|
||||||
|
//nop
|
||||||
|
} else if (type.isNumber()) {
|
||||||
|
invokestatic("java/lang/Double", "doubleToRawLongBits", "(D)J");
|
||||||
|
} else {
|
||||||
|
assert false : type + " cannot be packed!";
|
||||||
|
}
|
||||||
|
//all others are nops, objects aren't packed
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a local variable. This is a nop if the symbol has no slot
|
* Add a local variable. This is a nop if the symbol has no slot
|
||||||
*
|
*
|
||||||
@ -586,9 +645,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
*
|
*
|
||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter neg() {
|
MethodEmitter neg(final int programPoint) {
|
||||||
debug("neg");
|
debug("neg");
|
||||||
pushType(popNumeric().neg(method));
|
pushType(popNumeric().neg(method, programPoint));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,9 +658,23 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @param recovery label pointing to start of catch block
|
* @param recovery label pointing to start of catch block
|
||||||
*/
|
*/
|
||||||
void _catch(final Label recovery) {
|
void _catch(final Label recovery) {
|
||||||
stack.clear();
|
|
||||||
stack.push(Type.OBJECT);
|
|
||||||
label(recovery);
|
label(recovery);
|
||||||
|
stack.clear();
|
||||||
|
pushType(Type.typeFor(Throwable.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add any number of labels for the start of a catch block and push the exception to the
|
||||||
|
* stack
|
||||||
|
*
|
||||||
|
* @param recoveries labels pointing to start of catch block
|
||||||
|
*/
|
||||||
|
void _catch(final Collection<Label> recoveries) {
|
||||||
|
for(final Label l: recoveries) {
|
||||||
|
label(l);
|
||||||
|
}
|
||||||
|
stack.clear();
|
||||||
|
pushType(Type.OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -665,6 +738,12 @@ public class MethodEmitter implements Emitter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MethodEmitter loadForcedInitializer(final Type type) {
|
||||||
|
debug("load forced initializer ", type);
|
||||||
|
pushType(type.loadForcedInitializer(method));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push the empty value for the given type, i.e. EMPTY.
|
* Push the empty value for the given type, i.e. EMPTY.
|
||||||
*
|
*
|
||||||
@ -816,9 +895,10 @@ public class MethodEmitter implements Emitter {
|
|||||||
assert symbol != null;
|
assert symbol != null;
|
||||||
if (symbol.hasSlot()) {
|
if (symbol.hasSlot()) {
|
||||||
final int slot = symbol.getSlot();
|
final int slot = symbol.getSlot();
|
||||||
debug("load symbol", symbol.getName(), " slot=", slot);
|
debug("load symbol", symbol.getName(), " slot=", slot, "type=", symbol.getSymbolType());
|
||||||
final Type type = symbol.getSymbolType().load(method, slot);
|
load(symbol.getSymbolType(), slot);
|
||||||
pushType(type == Type.OBJECT && symbol.isThis() ? Type.THIS : type);
|
// _try(new Label("dummy"), new Label("dummy2"), recovery);
|
||||||
|
// method.visitTryCatchBlock(new Label(), arg1, arg2, arg3);
|
||||||
} else if (symbol.isParam()) {
|
} else if (symbol.isParam()) {
|
||||||
assert !symbol.isScope();
|
assert !symbol.isScope();
|
||||||
assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
|
assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
|
||||||
@ -853,7 +933,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
MethodEmitter load(final Type type, final int slot) {
|
MethodEmitter load(final Type type, final int slot) {
|
||||||
debug("explicit load", type, slot);
|
debug("explicit load", type, slot);
|
||||||
final Type loadType = type.load(method, slot);
|
final Type loadType = type.load(method, slot);
|
||||||
|
assert loadType != null;
|
||||||
pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType);
|
pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType);
|
||||||
|
stack.markLocalLoad(slot);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,7 +1031,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
if (symbol.hasSlot()) {
|
if (symbol.hasSlot()) {
|
||||||
final int slot = symbol.getSlot();
|
final int slot = symbol.getSlot();
|
||||||
debug("store symbol", symbol.getName(), " slot=", slot);
|
debug("store symbol", symbol.getName(), " slot=", slot);
|
||||||
popType(symbol.getSymbolType()).store(method, slot);
|
store(symbol.getSymbolType(), slot);
|
||||||
} else if (symbol.isParam()) {
|
} else if (symbol.isParam()) {
|
||||||
assert !symbol.isScope();
|
assert !symbol.isScope();
|
||||||
assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
|
assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
|
||||||
@ -977,8 +1059,37 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @param slot the slot
|
* @param slot the slot
|
||||||
*/
|
*/
|
||||||
void store(final Type type, final int slot) {
|
void store(final Type type, final int slot) {
|
||||||
|
debug("explicit store", type, slot);
|
||||||
popType(type);
|
popType(type);
|
||||||
type.store(method, slot);
|
type.store(method, slot);
|
||||||
|
// TODO: disable this when not running with optimistic types?
|
||||||
|
final int slotCount = type.getSlots();
|
||||||
|
ensureLocalVariableCount(slot + slotCount);
|
||||||
|
localVariableTypes.set(slot, type);
|
||||||
|
if(slotCount == 2) {
|
||||||
|
localVariableTypes.set(slot + 1, Type.SLOT_2);
|
||||||
|
}
|
||||||
|
stack.markLocalStore(slot, slotCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensureLocalVariableCount(final int slotCount) {
|
||||||
|
while(localVariableTypes.size() < slotCount) {
|
||||||
|
localVariableTypes.add(Type.UNKNOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Type> getLocalVariableTypes() {
|
||||||
|
return localVariableTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setParameterTypes(final Type... paramTypes) {
|
||||||
|
assert localVariableTypes.isEmpty();
|
||||||
|
for(final Type type: paramTypes) {
|
||||||
|
localVariableTypes.add(type);
|
||||||
|
if(type.isCategory2()) {
|
||||||
|
localVariableTypes.add(Type.SLOT_2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -999,7 +1110,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
public void athrow() {
|
public void athrow() {
|
||||||
debug("athrow");
|
debug("athrow");
|
||||||
final Type receiver = popType(Type.OBJECT);
|
final Type receiver = popType(Type.OBJECT);
|
||||||
assert receiver.isObject();
|
assert Throwable.class.isAssignableFrom(receiver.getTypeClass()) : receiver.getTypeClass();
|
||||||
method.visitInsn(ATHROW);
|
method.visitInsn(ATHROW);
|
||||||
stack = null;
|
stack = null;
|
||||||
}
|
}
|
||||||
@ -1130,11 +1241,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
popType(Type.OBJECT);
|
popType(Type.OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opcode == INVOKEINTERFACE) {
|
method.visitMethodInsn(opcode, className, methodName, methodDescriptor, opcode == INVOKEINTERFACE);
|
||||||
method.visitMethodInsn(opcode, className, methodName, methodDescriptor, true);
|
|
||||||
} else {
|
|
||||||
method.visitMethodInsn(opcode, className, methodName, methodDescriptor, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (returnType != null) {
|
if (returnType != null) {
|
||||||
pushType(returnType);
|
pushType(returnType);
|
||||||
@ -1197,7 +1304,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
*
|
*
|
||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter invokeStatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
|
MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
|
||||||
invokestatic(className, methodName, methodDescriptor);
|
invokestatic(className, methodName, methodDescriptor);
|
||||||
popType();
|
popType();
|
||||||
pushType(returnType);
|
pushType(returnType);
|
||||||
@ -1235,8 +1342,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
*/
|
*/
|
||||||
void lookupswitch(final Label defaultLabel, final int[] values, final Label... table) {//Collection<Label> table) {
|
void lookupswitch(final Label defaultLabel, final int[] values, final Label... table) {//Collection<Label> table) {
|
||||||
debug("lookupswitch", peekType());
|
debug("lookupswitch", peekType());
|
||||||
popType(Type.INT);
|
adjustStackForSwitch(defaultLabel, table);
|
||||||
method.visitLookupSwitchInsn(defaultLabel.getLabel(), values, getLabels(table));
|
method.visitLookupSwitchInsn(defaultLabel.getLabel(), values, getLabels(table));
|
||||||
|
stack = null; //whoever reaches the point after us provides the stack, because we don't
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1248,8 +1356,17 @@ public class MethodEmitter implements Emitter {
|
|||||||
*/
|
*/
|
||||||
void tableswitch(final int lo, final int hi, final Label defaultLabel, final Label... table) {
|
void tableswitch(final int lo, final int hi, final Label defaultLabel, final Label... table) {
|
||||||
debug("tableswitch", peekType());
|
debug("tableswitch", peekType());
|
||||||
popType(Type.INT);
|
adjustStackForSwitch(defaultLabel, table);
|
||||||
method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table));
|
method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table));
|
||||||
|
stack = null; //whoever reaches the point after us provides the stack, because we don't
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustStackForSwitch(final Label defaultLabel, final Label... table) {
|
||||||
|
popType(Type.INT);
|
||||||
|
mergeStackTo(defaultLabel);
|
||||||
|
for(final Label label: table) {
|
||||||
|
mergeStackTo(label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1489,7 +1606,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @param label destination label
|
* @param label destination label
|
||||||
*/
|
*/
|
||||||
void _goto(final Label label) {
|
void _goto(final Label label) {
|
||||||
//debug("goto", label);
|
debug("goto", label);
|
||||||
jump(GOTO, label, 0);
|
jump(GOTO, label, 0);
|
||||||
stack = null; //whoever reaches the point after us provides the stack, because we don't
|
stack = null; //whoever reaches the point after us provides the stack, because we don't
|
||||||
}
|
}
|
||||||
@ -1526,7 +1643,8 @@ public class MethodEmitter implements Emitter {
|
|||||||
label.setStack(stack.copy());
|
label.setStack(stack.copy());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert stack.isEquivalentTo(labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point";
|
assert stack.isEquivalentInTypesTo(labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point " + label;
|
||||||
|
stack.mergeLocalLoads(labelStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1561,13 +1679,32 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter convert(final Type to) {
|
MethodEmitter convert(final Type to) {
|
||||||
final Type type = peekType().convert(method, to);
|
final Type from = peekType();
|
||||||
|
final Type type = from.convert(method, to);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
if (!peekType().isEquivalentTo(to)) {
|
if (!from.isEquivalentTo(to)) {
|
||||||
debug("convert", peekType(), "->", to);
|
debug("convert", from, "->", to);
|
||||||
|
}
|
||||||
|
if (type != from) {
|
||||||
|
final int l0 = stack.getTopLocalLoad();
|
||||||
|
popType();
|
||||||
|
pushType(type);
|
||||||
|
// NOTE: conversions from a primitive type are considered to preserve the "load" property of the value
|
||||||
|
// on the stack. Otherwise we could introduce temporary locals in a deoptimized rest-of (e.g. doing an
|
||||||
|
// "i < x.length" where "i" is int and ".length" gets deoptimized to long would end up converting i to
|
||||||
|
// long with "ILOAD i; I2L; LSTORE tmp; LLOAD tmp;"). Such additional temporary would cause an error
|
||||||
|
// when restoring the state of the function for rest-of execution, as the not-yet deoptimized variant
|
||||||
|
// would have the (now invalidated) assumption that "x.length" is an int, so it wouldn't have the I2L,
|
||||||
|
// and therefore neither the subsequent LSTORE tmp; LLOAD tmp;. By making sure conversions from a
|
||||||
|
// primitive type don't erase the "load" information, we don't introduce temporaries in the deoptimized
|
||||||
|
// rest-of that didn't exist in the more optimistic version that triggered the deoptimization.
|
||||||
|
// NOTE: as a more general observation, we could theoretically track the operations required to
|
||||||
|
// reproduce any stack value as long as they are all local loads, constant loads, and stack operations.
|
||||||
|
// We won't go there in the current system
|
||||||
|
if(!from.isObject()) {
|
||||||
|
stack.markLocalLoad(l0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
popType();
|
|
||||||
pushType(type);
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -1613,9 +1750,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
*
|
*
|
||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter add() {
|
MethodEmitter add(final int programPoint) {
|
||||||
debug("add");
|
debug("add");
|
||||||
pushType(get2().add(method));
|
pushType(get2().add(method, programPoint));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1624,9 +1761,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
*
|
*
|
||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter sub() {
|
MethodEmitter sub(final int programPoint) {
|
||||||
debug("sub");
|
debug("sub");
|
||||||
pushType(get2n().sub(method));
|
pushType(get2n().sub(method, programPoint));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1635,9 +1772,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
*
|
*
|
||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter mul() {
|
MethodEmitter mul(final int programPoint) {
|
||||||
debug("mul ");
|
debug("mul ");
|
||||||
pushType(get2n().mul(method));
|
pushType(get2n().mul(method, programPoint));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1646,9 +1783,9 @@ public class MethodEmitter implements Emitter {
|
|||||||
*
|
*
|
||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter div() {
|
MethodEmitter div(final int programPoint) {
|
||||||
debug("div");
|
debug("div");
|
||||||
pushType(get2n().div(method));
|
pushType(get2n().div(method, programPoint));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1670,13 +1807,15 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return array of Types
|
* @return array of Types
|
||||||
*/
|
*/
|
||||||
protected Type[] getTypesFromStack(final int count) {
|
protected Type[] getTypesFromStack(final int count) {
|
||||||
final Type[] types = new Type[count];
|
return stack.getTopTypes(count);
|
||||||
int pos = 0;
|
}
|
||||||
for (int i = count - 1; i >= 0; i--) {
|
|
||||||
types[i] = stack.peek(pos++);
|
|
||||||
}
|
|
||||||
|
|
||||||
return types;
|
int[] getLocalLoadsOnStack(final int from, final int to) {
|
||||||
|
return stack.getLocalLoads(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getStackSize() {
|
||||||
|
return stack.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1712,6 +1851,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter dynamicNew(final int argCount, final int flags) {
|
MethodEmitter dynamicNew(final int argCount, final int flags) {
|
||||||
|
assert !isOptimistic(flags);
|
||||||
debug("dynamic_new", "argcount=", argCount);
|
debug("dynamic_new", "argcount=", argCount);
|
||||||
final String signature = getDynamicSignature(Type.OBJECT, argCount);
|
final String signature = getDynamicSignature(Type.OBJECT, argCount);
|
||||||
method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
|
method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
|
||||||
@ -1738,6 +1878,13 @@ public class MethodEmitter implements Emitter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MethodEmitter dynamicArrayPopulatorCall(final int argCount, final int startIndex) {
|
||||||
|
final String signature = getDynamicSignature(Type.OBJECT_ARRAY, argCount);
|
||||||
|
method.visitInvokeDynamicInsn("populateArray", signature, POPULATE_ARRAY_BOOTSTRAP, startIndex);
|
||||||
|
pushType(Type.OBJECT_ARRAY);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a dynamic call for a runtime node
|
* Generate a dynamic call for a runtime node
|
||||||
*
|
*
|
||||||
@ -1768,7 +1915,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
|
MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
|
||||||
debug("dynamic_get", name, valueType);
|
debug("dynamic_get", name, valueType, getProgramPoint(flags));
|
||||||
|
|
||||||
Type type = valueType;
|
Type type = valueType;
|
||||||
if (type.isObject() || type.isBoolean()) {
|
if (type.isObject() || type.isBoolean()) {
|
||||||
@ -1780,7 +1927,6 @@ public class MethodEmitter implements Emitter {
|
|||||||
NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
|
NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
|
||||||
|
|
||||||
pushType(type);
|
pushType(type);
|
||||||
|
|
||||||
convert(valueType); //most probably a nop
|
convert(valueType); //most probably a nop
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -1794,7 +1940,8 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @param flags call site flags
|
* @param flags call site flags
|
||||||
*/
|
*/
|
||||||
void dynamicSet(final String name, final int flags) {
|
void dynamicSet(final String name, final int flags) {
|
||||||
debug("dynamic_set", name, peekType());
|
assert !isOptimistic(flags);
|
||||||
|
debug("dynamic_set", name, peekType());
|
||||||
|
|
||||||
Type type = peekType();
|
Type type = peekType();
|
||||||
if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
|
if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
|
||||||
@ -1818,7 +1965,8 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @return the method emitter
|
* @return the method emitter
|
||||||
*/
|
*/
|
||||||
MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
|
MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
|
||||||
debug("dynamic_get_index", peekType(1), "[", peekType(), "]");
|
assert result.getTypeClass().isPrimitive() || result.getTypeClass() == Object.class;
|
||||||
|
debug("dynamic_get_index", peekType(1), "[", peekType(), "]", getProgramPoint(flags));
|
||||||
|
|
||||||
Type resultType = result;
|
Type resultType = result;
|
||||||
if (result.isBoolean()) {
|
if (result.isBoolean()) {
|
||||||
@ -1836,8 +1984,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
|
|
||||||
final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
|
final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
|
||||||
|
|
||||||
method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod",
|
method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod", signature, LINKERBOOTSTRAP, flags);
|
||||||
signature, LINKERBOOTSTRAP, flags);
|
|
||||||
pushType(resultType);
|
pushType(resultType);
|
||||||
|
|
||||||
if (result.isBoolean()) {
|
if (result.isBoolean()) {
|
||||||
@ -1847,6 +1994,14 @@ public class MethodEmitter implements Emitter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String getProgramPoint(int flags) {
|
||||||
|
if((flags & CALLSITE_OPTIMISTIC) == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return "pp=" + String.valueOf((flags & (-1 << CALLSITE_PROGRAM_POINT_SHIFT)) >> CALLSITE_PROGRAM_POINT_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamic setter for indexed structures. Pop value, index and receiver from
|
* Dynamic setter for indexed structures. Pop value, index and receiver from
|
||||||
* stack, generate appropriate signature based on types
|
* stack, generate appropriate signature based on types
|
||||||
@ -1854,6 +2009,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
* @param flags call site flags for setter
|
* @param flags call site flags for setter
|
||||||
*/
|
*/
|
||||||
void dynamicSetIndex(final int flags) {
|
void dynamicSetIndex(final int flags) {
|
||||||
|
assert !isOptimistic(flags);
|
||||||
debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
|
debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
|
||||||
|
|
||||||
Type value = peekType();
|
Type value = peekType();
|
||||||
@ -2148,7 +2304,10 @@ public class MethodEmitter implements Emitter {
|
|||||||
} else {
|
} else {
|
||||||
sb.append(t.getDescriptor());
|
sb.append(t.getDescriptor());
|
||||||
}
|
}
|
||||||
|
final int loadIndex = stack.localLoads[stack.sp - 1 - pos];
|
||||||
|
if(loadIndex != Label.Stack.NON_LOAD) {
|
||||||
|
sb.append('(').append(loadIndex).append(')');
|
||||||
|
}
|
||||||
if (pos + 1 < stack.size()) {
|
if (pos + 1 < stack.size()) {
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
}
|
}
|
||||||
@ -2193,4 +2352,7 @@ public class MethodEmitter implements Emitter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isOptimistic(final int flags) {
|
||||||
|
return (flags & CALLSITE_OPTIMISTIC) != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,27 +35,39 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
|
|||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
||||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.TYPE_INT_INDEX;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.TYPE_LONG_INDEX;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX;
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.runtime.AccessorProperty;
|
import jdk.nashorn.internal.runtime.AccessorProperty;
|
||||||
import jdk.nashorn.internal.runtime.Context;
|
import jdk.nashorn.internal.runtime.Context;
|
||||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||||
import jdk.nashorn.internal.runtime.FunctionScope;
|
import jdk.nashorn.internal.runtime.FunctionScope;
|
||||||
import jdk.nashorn.internal.runtime.JSType;
|
|
||||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
import jdk.nashorn.internal.runtime.Undefined;
|
||||||
|
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
|
||||||
import jdk.nashorn.internal.runtime.options.Options;
|
import jdk.nashorn.internal.runtime.options.Options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,9 +76,9 @@ import jdk.nashorn.internal.runtime.options.Options;
|
|||||||
public final class ObjectClassGenerator {
|
public final class ObjectClassGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marker for scope parameters.
|
* Marker for scope parameters.g
|
||||||
*/
|
*/
|
||||||
static final String SCOPE_MARKER = "P";
|
private static final String SCOPE_MARKER = "P";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum number of extra fields in an object.
|
* Minimum number of extra fields in an object.
|
||||||
@ -79,27 +91,55 @@ public final class ObjectClassGenerator {
|
|||||||
*/
|
*/
|
||||||
public static final DebugLogger LOG = new DebugLogger("fields", "nashorn.fields.debug");
|
public static final DebugLogger LOG = new DebugLogger("fields", "nashorn.fields.debug");
|
||||||
|
|
||||||
|
private static final Set<String> FIELDS_TO_INSTRUMENT;
|
||||||
|
static {
|
||||||
|
final String fields = Options.getStringProperty("nashorn.fields", null);
|
||||||
|
final Set<String> fti = new HashSet<>();
|
||||||
|
if (fields != null) {
|
||||||
|
final StringTokenizer st = new StringTokenizer(fields, ",");
|
||||||
|
while (st.hasMoreTokens()) {
|
||||||
|
fti.add(st.nextToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FIELDS_TO_INSTRUMENT = fti.isEmpty() ? null : fti;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should this particular field be instrumented with --log=fields
|
||||||
|
* Internal use only
|
||||||
|
* @param field field name
|
||||||
|
* @return true if it should be instrumented
|
||||||
|
*/
|
||||||
|
public static boolean shouldInstrument(final String field) {
|
||||||
|
//if no explicit fields to imstrument are given, instrument all fields
|
||||||
|
return FIELDS_TO_INSTRUMENT == null || FIELDS_TO_INSTRUMENT.contains(field);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* is field debugging enabled. Several modules in codegen and properties use this, hence
|
* is field debugging enabled. Several modules in codegen and properties use this, hence
|
||||||
* public access.
|
* public access.
|
||||||
*/
|
*/
|
||||||
public static final boolean DEBUG_FIELDS = LOG.isEnabled();
|
public static final boolean DEBUG_FIELDS = LOG.isEnabled();
|
||||||
|
|
||||||
|
private static final boolean EXPLICIT_OBJECT = Options.getBooleanProperty("nashorn.fields.objects");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should the runtime only use java.lang.Object slots for fields? If this is false, the representation
|
* Should the runtime only use java.lang.Object slots for fields? If this is false, the representation
|
||||||
* will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references.
|
* will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references.
|
||||||
* This introduces a larger number of method handles in the system, as we need to have different getters
|
* This introduces a larger number of method handles in the system, as we need to have different getters
|
||||||
* and setters for the different fields. Currently this introduces significant overhead in Hotspot.
|
* and setters for the different fields.
|
||||||
*
|
*
|
||||||
* This is engineered to plug into the TaggedArray implementation, when it's done.
|
* This is engineered to plug into the TaggedArray implementation, when it's done.
|
||||||
*/
|
*/
|
||||||
public static final boolean OBJECT_FIELDS_ONLY = !Options.getBooleanProperty("nashorn.fields.dual");
|
public static final boolean OBJECT_FIELDS_ONLY = EXPLICIT_OBJECT || !ScriptEnvironment.globalOptimistic();
|
||||||
|
|
||||||
/** The field types in the system */
|
/** The field types in the system */
|
||||||
private static final List<Type> FIELD_TYPES = new LinkedList<>();
|
private static final List<Type> FIELD_TYPES = new LinkedList<>();
|
||||||
|
|
||||||
/** What type is the primitive type in dual representation */
|
/** What type is the primitive type in dual representation */
|
||||||
public static final Type PRIMITIVE_TYPE = Type.LONG;
|
public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG;
|
||||||
|
|
||||||
|
private static final MethodHandle GET_DIFFERENT = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class);
|
||||||
|
private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of field types that we support - one type creates one field. This is currently either
|
* The list of field types that we support - one type creates one field. This is currently either
|
||||||
@ -107,8 +147,10 @@ public final class ObjectClassGenerator {
|
|||||||
*/
|
*/
|
||||||
static {
|
static {
|
||||||
if (!OBJECT_FIELDS_ONLY) {
|
if (!OBJECT_FIELDS_ONLY) {
|
||||||
System.err.println("WARNING!!! Running with primitive fields - there is untested functionality!");
|
LOG.warning("Running with primitive fields - there is untested functionality!");
|
||||||
FIELD_TYPES.add(PRIMITIVE_TYPE);
|
FIELD_TYPES.add(PRIMITIVE_FIELD_TYPE);
|
||||||
|
} else {
|
||||||
|
System.err.println("Running with object fields only");
|
||||||
}
|
}
|
||||||
FIELD_TYPES.add(Type.OBJECT);
|
FIELD_TYPES.add(Type.OBJECT);
|
||||||
}
|
}
|
||||||
@ -116,23 +158,6 @@ public final class ObjectClassGenerator {
|
|||||||
/** The context */
|
/** The context */
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of available accessor types in width order. This order is used for type guesses narrow{@literal ->} wide
|
|
||||||
* in the dual--fields world
|
|
||||||
*/
|
|
||||||
public static final List<Type> ACCESSOR_TYPES = Collections.unmodifiableList(
|
|
||||||
Arrays.asList(
|
|
||||||
Type.INT,
|
|
||||||
Type.LONG,
|
|
||||||
Type.NUMBER,
|
|
||||||
Type.OBJECT));
|
|
||||||
|
|
||||||
//these are hard coded for speed and so that we can switch on them
|
|
||||||
private static final int TYPE_INT_INDEX = 0; //getAccessorTypeIndex(int.class);
|
|
||||||
private static final int TYPE_LONG_INDEX = 1; //getAccessorTypeIndex(long.class);
|
|
||||||
private static final int TYPE_DOUBLE_INDEX = 2; //getAccessorTypeIndex(double.class);
|
|
||||||
private static final int TYPE_OBJECT_INDEX = 3; //getAccessorTypeIndex(Object.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -144,61 +169,19 @@ public final class ObjectClassGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a type of an accessor, return its index in [0..getNumberOfAccessorTypes())
|
* Pack a number into a primitive long field
|
||||||
*
|
* @param n number object
|
||||||
* @param type the type
|
* @return primitive long value with all the bits in the number
|
||||||
*
|
|
||||||
* @return the accessor index, or -1 if no accessor of this type exists
|
|
||||||
*/
|
*/
|
||||||
public static int getAccessorTypeIndex(final Type type) {
|
public static long pack(final Number n) {
|
||||||
return getAccessorTypeIndex(type.getTypeClass());
|
if (n instanceof Integer) {
|
||||||
}
|
return n.intValue();
|
||||||
|
} else if (n instanceof Long) {
|
||||||
/**
|
return n.longValue();
|
||||||
* Given a class of an accessor, return its index in [0..getNumberOfAccessorTypes())
|
} else if (n instanceof Double) {
|
||||||
*
|
return Double.doubleToRawLongBits(n.doubleValue());
|
||||||
* Note that this is hardcoded with respect to the dynamic contents of the accessor
|
|
||||||
* types array for speed. Hotspot got stuck with this as 5% of the runtime in
|
|
||||||
* a benchmark when it looped over values and increased an index counter. :-(
|
|
||||||
*
|
|
||||||
* @param type the type
|
|
||||||
*
|
|
||||||
* @return the accessor index, or -1 if no accessor of this type exists
|
|
||||||
*/
|
|
||||||
public static int getAccessorTypeIndex(final Class<?> type) {
|
|
||||||
if (type == int.class) {
|
|
||||||
return 0;
|
|
||||||
} else if (type == long.class) {
|
|
||||||
return 1;
|
|
||||||
} else if (type == double.class) {
|
|
||||||
return 2;
|
|
||||||
} else if (!type.isPrimitive()) {
|
|
||||||
return 3;
|
|
||||||
}
|
}
|
||||||
return -1;
|
throw new AssertionError("cannot pack" + n);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of accessor types available.
|
|
||||||
*
|
|
||||||
* @return number of accessor types in system
|
|
||||||
*/
|
|
||||||
public static int getNumberOfAccessorTypes() {
|
|
||||||
return ACCESSOR_TYPES.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the accessor type based on its index in [0..getNumberOfAccessorTypes())
|
|
||||||
* Indexes are ordered narrower{@literal ->}wider / optimistic{@literal ->}pessimistic. Invalidations always
|
|
||||||
* go to a type of higher index
|
|
||||||
*
|
|
||||||
* @param index accessor type index
|
|
||||||
*
|
|
||||||
* @return a type corresponding to the index.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static Type getAccessorType(final int index) {
|
|
||||||
return ACCESSOR_TYPES.get(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -235,7 +218,7 @@ public final class ObjectClassGenerator {
|
|||||||
public static int getFieldCount(Class<?> clazz) {
|
public static int getFieldCount(Class<?> clazz) {
|
||||||
final String name = clazz.getSimpleName();
|
final String name = clazz.getSimpleName();
|
||||||
final String prefix = JS_OBJECT_PREFIX.symbolName();
|
final String prefix = JS_OBJECT_PREFIX.symbolName();
|
||||||
if(prefix.equals(name)) {
|
if (prefix.equals(name)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
final int scopeMarker = name.indexOf(SCOPE_MARKER);
|
final int scopeMarker = name.indexOf(SCOPE_MARKER);
|
||||||
@ -324,6 +307,10 @@ public final class ObjectClassGenerator {
|
|||||||
init.returnVoid();
|
init.returnVoid();
|
||||||
init.end();
|
init.end();
|
||||||
|
|
||||||
|
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, ScriptObject.class);
|
||||||
|
initWithSpillArrays.returnVoid();
|
||||||
|
initWithSpillArrays.end();
|
||||||
|
|
||||||
newEmptyInit(classEmitter, className);
|
newEmptyInit(classEmitter, className);
|
||||||
newAllocate(classEmitter, className);
|
newAllocate(classEmitter, className);
|
||||||
|
|
||||||
@ -340,8 +327,8 @@ public final class ObjectClassGenerator {
|
|||||||
* @return Byte codes for generated class.
|
* @return Byte codes for generated class.
|
||||||
*/
|
*/
|
||||||
public byte[] generate(final int fieldCount, final int paramCount) {
|
public byte[] generate(final int fieldCount, final int paramCount) {
|
||||||
final String className = getClassName(fieldCount, paramCount);
|
final String className = getClassName(fieldCount, paramCount);
|
||||||
final String superName = className(FunctionScope.class);
|
final String superName = className(FunctionScope.class);
|
||||||
final ClassEmitter classEmitter = newClassEmitter(className, superName);
|
final ClassEmitter classEmitter = newClassEmitter(className, superName);
|
||||||
final List<String> initFields = addFields(classEmitter, fieldCount);
|
final List<String> initFields = addFields(classEmitter, fieldCount);
|
||||||
|
|
||||||
@ -350,6 +337,11 @@ public final class ObjectClassGenerator {
|
|||||||
init.returnVoid();
|
init.returnVoid();
|
||||||
init.end();
|
init.end();
|
||||||
|
|
||||||
|
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, FunctionScope.class);
|
||||||
|
initializeToUndefined(initWithSpillArrays, className, initFields);
|
||||||
|
initWithSpillArrays.returnVoid();
|
||||||
|
initWithSpillArrays.end();
|
||||||
|
|
||||||
final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
|
final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
|
||||||
initializeToUndefined(initWithArguments, className, initFields);
|
initializeToUndefined(initWithArguments, className, initFields);
|
||||||
initWithArguments.returnVoid();
|
initWithArguments.returnVoid();
|
||||||
@ -414,6 +406,18 @@ public final class ObjectClassGenerator {
|
|||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodEmitter newInitWithSpillArraysMethod(final ClassEmitter classEmitter, final Class<?> superClass) {
|
||||||
|
final MethodEmitter init = classEmitter.init(PropertyMap.class, long[].class, Object[].class);
|
||||||
|
init.begin();
|
||||||
|
init.load(Type.OBJECT, JAVA_THIS.slot());
|
||||||
|
init.load(Type.OBJECT, INIT_MAP.slot());
|
||||||
|
init.load(Type.LONG_ARRAY, 2);
|
||||||
|
init.load(Type.OBJECT_ARRAY, 3);
|
||||||
|
init.invoke(constructorNoLookup(superClass, PropertyMap.class, long[].class, Object[].class));
|
||||||
|
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate and initialize a new <init> method for scopes.
|
* Allocate and initialize a new <init> method for scopes.
|
||||||
* @param classEmitter Open class emitter.
|
* @param classEmitter Open class emitter.
|
||||||
@ -472,7 +476,7 @@ public final class ObjectClassGenerator {
|
|||||||
private static void newAllocate(final ClassEmitter classEmitter, final String className) {
|
private static void newAllocate(final ClassEmitter classEmitter, final String className) {
|
||||||
final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
|
final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
|
||||||
allocate.begin();
|
allocate.begin();
|
||||||
allocate._new(className);
|
allocate._new(className, Type.typeFor(ScriptObject.class));
|
||||||
allocate.dup();
|
allocate.dup();
|
||||||
allocate.load(Type.typeFor(PropertyMap.class), 0);
|
allocate.load(Type.typeFor(PropertyMap.class), 0);
|
||||||
allocate.invoke(constructorNoLookup(className, PropertyMap.class));
|
allocate.invoke(constructorNoLookup(className, PropertyMap.class));
|
||||||
@ -492,7 +496,7 @@ public final class ObjectClassGenerator {
|
|||||||
final byte[] code = classEmitter.toByteArray();
|
final byte[] code = classEmitter.toByteArray();
|
||||||
final ScriptEnvironment env = context.getEnv();
|
final ScriptEnvironment env = context.getEnv();
|
||||||
|
|
||||||
if (env._print_code) {
|
if (env._print_code && env._print_code_dir == null) {
|
||||||
env.getErr().println(ClassEmitter.disassemble(code));
|
env.getErr().println(ClassEmitter.disassemble(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,20 +508,172 @@ public final class ObjectClassGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Double to long bits, used with --dual-fields for primitive double values */
|
/** Double to long bits, used with --dual-fields for primitive double values */
|
||||||
private static final MethodHandle PACK_DOUBLE =
|
public static final MethodHandle PACK_DOUBLE =
|
||||||
MH.explicitCastArguments(MH.findStatic(MethodHandles.publicLookup(), Double.class, "doubleToRawLongBits", MH.type(long.class, double.class)), MH.type(long.class, double.class));
|
MH.explicitCastArguments(MH.findStatic(MethodHandles.publicLookup(), Double.class, "doubleToRawLongBits", MH.type(long.class, double.class)), MH.type(long.class, double.class));
|
||||||
|
|
||||||
/** double bits to long, used with --dual-fields for primitive double values */
|
/** double bits to long, used with --dual-fields for primitive double values */
|
||||||
private static MethodHandle UNPACK_DOUBLE =
|
public static final MethodHandle UNPACK_DOUBLE =
|
||||||
MH.findStatic(MethodHandles.publicLookup(), Double.class, "longBitsToDouble", MH.type(double.class, long.class));
|
MH.findStatic(MethodHandles.publicLookup(), Double.class, "longBitsToDouble", MH.type(double.class, long.class));
|
||||||
|
|
||||||
/** object conversion quickies with JS semantics - used for return value and parameter filter */
|
//type != forType, so use the correct getter for forType, box it and throw
|
||||||
private static MethodHandle[] CONVERT_OBJECT = {
|
@SuppressWarnings("unused")
|
||||||
JSType.TO_INT32.methodHandle(),
|
private static Object getDifferent(final Object receiver, final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
|
||||||
JSType.TO_UINT32.methodHandle(),
|
//create the sametype getter, and upcast to value. no matter what the store format is,
|
||||||
JSType.TO_NUMBER.methodHandle(),
|
//
|
||||||
null
|
final MethodHandle sameTypeGetter = getterForType(forType, primitiveGetter, objectGetter);
|
||||||
};
|
final MethodHandle mh = MH.asType(sameTypeGetter, sameTypeGetter.type().changeReturnType(Object.class));
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("cast")
|
||||||
|
final Object value = (Object)mh.invokeExact(receiver);
|
||||||
|
throw new UnwarrantedOptimismException(value, programPoint);
|
||||||
|
} catch (final Error | RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (final Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static Object getDifferentUndefined(final int programPoint) {
|
||||||
|
throw new UnwarrantedOptimismException(Undefined.getUndefined(), programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
|
||||||
|
switch (getAccessorTypeIndex(forType)) {
|
||||||
|
case TYPE_INT_INDEX:
|
||||||
|
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
|
||||||
|
return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class));
|
||||||
|
case TYPE_LONG_INDEX:
|
||||||
|
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
|
||||||
|
return primitiveGetter;
|
||||||
|
case TYPE_DOUBLE_INDEX:
|
||||||
|
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
|
||||||
|
return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
|
||||||
|
case TYPE_OBJECT_INDEX:
|
||||||
|
return objectGetter;
|
||||||
|
default:
|
||||||
|
throw new AssertionError(forType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//no optimism here. we do unconditional conversion to types
|
||||||
|
private static MethodHandle createGetterInner(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final MethodHandle[] converters, final int programPoint) {
|
||||||
|
final int fti = forType == null ? TYPE_UNDEFINED_INDEX : getAccessorTypeIndex(forType);
|
||||||
|
final int ti = getAccessorTypeIndex(type);
|
||||||
|
//this means fail if forType != type
|
||||||
|
final boolean isOptimistic = converters == CONVERT_OBJECT_OPTIMISTIC;
|
||||||
|
final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
|
||||||
|
|
||||||
|
//which is the primordial getter
|
||||||
|
final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : (isPrimitiveStorage ? primitiveGetter : objectGetter);
|
||||||
|
|
||||||
|
if (forType == null) {
|
||||||
|
if (isOptimistic) {
|
||||||
|
//return undefined if asking for object. otherwise throw UnwarrantedOptimismException
|
||||||
|
if (ti == TYPE_OBJECT_INDEX) {
|
||||||
|
return MH.dropArguments(GET_UNDEFINED[TYPE_OBJECT_INDEX], 0, Object.class);
|
||||||
|
}
|
||||||
|
//throw exception
|
||||||
|
return MH.asType(
|
||||||
|
MH.dropArguments(
|
||||||
|
MH.insertArguments(
|
||||||
|
GET_DIFFERENT_UNDEFINED,
|
||||||
|
0,
|
||||||
|
programPoint),
|
||||||
|
0,
|
||||||
|
Object.class),
|
||||||
|
getter.type().changeReturnType(type));
|
||||||
|
}
|
||||||
|
//return an undefined and coerce it to the appropriate type
|
||||||
|
return MH.dropArguments(GET_UNDEFINED[ti], 0, Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert forType != null;
|
||||||
|
assert !OBJECT_FIELDS_ONLY || forType == Object.class : forType;
|
||||||
|
|
||||||
|
if (isOptimistic) {
|
||||||
|
if (fti < ti) {
|
||||||
|
//asking for a wider type than currently stored. then it's OK to coerce.
|
||||||
|
//e.g. stored as int, ask for long or double
|
||||||
|
//e.g. stored as long, ask for double
|
||||||
|
assert fti != TYPE_UNDEFINED_INDEX;
|
||||||
|
final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
|
||||||
|
return MH.asType(tgetter, tgetter.type().changeReturnType(type));
|
||||||
|
} else if (fti == ti) {
|
||||||
|
//Fast path, never throw exception - exact getter, just unpack if needed
|
||||||
|
return getterForType(forType, primitiveGetter, objectGetter);
|
||||||
|
} else {
|
||||||
|
assert fti > ti;
|
||||||
|
//if asking for a narrower type than the storage - throw exception
|
||||||
|
//unless FTI is object, in that case we have to go through the converters
|
||||||
|
//there is no
|
||||||
|
if (fti == TYPE_OBJECT_INDEX) {
|
||||||
|
return MH.filterReturnValue(
|
||||||
|
objectGetter,
|
||||||
|
MH.insertArguments(
|
||||||
|
converters[ti],
|
||||||
|
1,
|
||||||
|
programPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
//asking for narrower primitive than we have stored, that is an
|
||||||
|
//UnwarrantedOptimismException
|
||||||
|
return MH.asType(
|
||||||
|
MH.filterArguments(
|
||||||
|
objectGetter,
|
||||||
|
0,
|
||||||
|
MH.insertArguments(
|
||||||
|
GET_DIFFERENT,
|
||||||
|
1,
|
||||||
|
forType,
|
||||||
|
primitiveGetter,
|
||||||
|
objectGetter,
|
||||||
|
programPoint)),
|
||||||
|
objectGetter.type().changeReturnType(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert !isOptimistic;
|
||||||
|
//freely coerce the result to whatever you asked for, this is e.g. Object->int for a & b
|
||||||
|
final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
|
||||||
|
if (fti == TYPE_OBJECT_INDEX) {
|
||||||
|
if (fti != ti) {
|
||||||
|
return MH.filterReturnValue(tgetter, CONVERT_OBJECT[ti]);
|
||||||
|
}
|
||||||
|
return tgetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert !OBJECT_FIELDS_ONLY;
|
||||||
|
//final MethodType pmt = primitiveGetter.type();
|
||||||
|
assert primitiveGetter != null;
|
||||||
|
final MethodType tgetterType = tgetter.type();
|
||||||
|
switch (fti) {
|
||||||
|
case TYPE_INT_INDEX:
|
||||||
|
case TYPE_LONG_INDEX:
|
||||||
|
switch (ti) {
|
||||||
|
case TYPE_INT_INDEX:
|
||||||
|
//get int while an int, truncating cast of long value
|
||||||
|
return MH.explicitCastArguments(tgetter, tgetterType.changeReturnType(int.class));
|
||||||
|
case TYPE_LONG_INDEX:
|
||||||
|
return primitiveGetter;
|
||||||
|
default:
|
||||||
|
return MH.asType(tgetter, tgetterType.changeReturnType(type));
|
||||||
|
}
|
||||||
|
case TYPE_DOUBLE_INDEX:
|
||||||
|
switch (ti) {
|
||||||
|
case TYPE_INT_INDEX:
|
||||||
|
case TYPE_LONG_INDEX:
|
||||||
|
return MH.explicitCastArguments(tgetter, tgetterType.changeReturnType(type));
|
||||||
|
case TYPE_DOUBLE_INDEX:
|
||||||
|
assert tgetterType.returnType() == double.class;
|
||||||
|
return tgetter;
|
||||||
|
default:
|
||||||
|
return MH.asType(tgetter, tgetterType.changeReturnType(Object.class));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException(forType + "=>" + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
|
* Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
|
||||||
@ -526,7 +682,7 @@ public final class ObjectClassGenerator {
|
|||||||
* and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
|
* and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
|
||||||
* which reads a long, use longBitsToDouble on the result to unpack it, and then change the
|
* which reads a long, use longBitsToDouble on the result to unpack it, and then change the
|
||||||
* return type to Object, boxing it. In the objects only world there are only object fields,
|
* return type to Object, boxing it. In the objects only world there are only object fields,
|
||||||
* primtives are boxed when asked for them and we don't need to bother with primitive encoding
|
* primitives are boxed when asked for them and we don't need to bother with primitive encoding
|
||||||
* (or even undefined, which if forType==null) representation, so we just return whatever is
|
* (or even undefined, which if forType==null) representation, so we just return whatever is
|
||||||
* in the object field. The object field is always initiated to Undefined, so here, where we have
|
* in the object field. The object field is always initiated to Undefined, so here, where we have
|
||||||
* the representation for Undefined in all our bits, this is not a problem.
|
* the representation for Undefined in all our bits, this is not a problem.
|
||||||
@ -543,110 +699,18 @@ public final class ObjectClassGenerator {
|
|||||||
* @param type type to retrieve it as
|
* @param type type to retrieve it as
|
||||||
* @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
|
* @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
|
||||||
* @param objectGetter getter to read the object version of this field
|
* @param objectGetter getter to read the object version of this field
|
||||||
|
* @param programPoint program point for getter, if program point is INVALID_PROGRAM_POINT, then this is not an optimistic getter
|
||||||
*
|
*
|
||||||
* @return getter for the given representation that returns the given type
|
* @return getter for the given representation that returns the given type
|
||||||
*/
|
*/
|
||||||
public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
|
public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
|
||||||
final int fti = forType == null ? -1 : getAccessorTypeIndex(forType);
|
return createGetterInner(
|
||||||
final int ti = getAccessorTypeIndex(type);
|
forType,
|
||||||
|
type,
|
||||||
if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) {
|
primitiveGetter,
|
||||||
if (ti == TYPE_OBJECT_INDEX) {
|
objectGetter,
|
||||||
return objectGetter;
|
isValid(programPoint) ? CONVERT_OBJECT_OPTIMISTIC : CONVERT_OBJECT,
|
||||||
}
|
programPoint);
|
||||||
|
|
||||||
return MH.filterReturnValue(objectGetter, CONVERT_OBJECT[ti]);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert !OBJECT_FIELDS_ONLY;
|
|
||||||
if (forType == null) {
|
|
||||||
return GET_UNDEFINED[ti];
|
|
||||||
}
|
|
||||||
|
|
||||||
final MethodType pmt = primitiveGetter.type();
|
|
||||||
|
|
||||||
switch (fti) {
|
|
||||||
case TYPE_INT_INDEX:
|
|
||||||
case TYPE_LONG_INDEX:
|
|
||||||
switch (ti) {
|
|
||||||
case TYPE_INT_INDEX:
|
|
||||||
//get int while an int, truncating cast of long value
|
|
||||||
return MH.explicitCastArguments(primitiveGetter, pmt.changeReturnType(int.class));
|
|
||||||
case TYPE_LONG_INDEX:
|
|
||||||
return primitiveGetter;
|
|
||||||
default:
|
|
||||||
return MH.asType(primitiveGetter, pmt.changeReturnType(type));
|
|
||||||
}
|
|
||||||
case TYPE_DOUBLE_INDEX:
|
|
||||||
final MethodHandle getPrimitiveAsDouble = MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
|
|
||||||
switch (ti) {
|
|
||||||
case TYPE_INT_INDEX:
|
|
||||||
case TYPE_LONG_INDEX:
|
|
||||||
return MH.explicitCastArguments(getPrimitiveAsDouble, pmt.changeReturnType(type));
|
|
||||||
case TYPE_DOUBLE_INDEX:
|
|
||||||
return getPrimitiveAsDouble;
|
|
||||||
default:
|
|
||||||
return MH.asType(getPrimitiveAsDouble, pmt.changeReturnType(Object.class));
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
assert false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final MethodHandle IS_TYPE_GUARD = findOwnMH("isType", boolean.class, Class.class, Object.class);
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static boolean isType(final Class<?> boxedForType, final Object x) {
|
|
||||||
return x.getClass() == boxedForType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<? extends Number> getBoxedType(final Class<?> forType) {
|
|
||||||
if (forType == int.class) {
|
|
||||||
return Integer.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forType == long.class) {
|
|
||||||
return Long.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forType == double.class) {
|
|
||||||
return Double.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If we are setting boxed types (because the compiler couldn't determine which they were) to
|
|
||||||
* a primitive field, we can reuse the primitive field getter, as long as we are setting an element
|
|
||||||
* of the same boxed type as the primitive type representation
|
|
||||||
*
|
|
||||||
* @param forType the current type
|
|
||||||
* @param primitiveSetter primitive setter for the current type with an element of the current type
|
|
||||||
* @param objectSetter the object setter
|
|
||||||
*
|
|
||||||
* @return method handle that checks if the element to be set is of the currenttype, even though it's boxed
|
|
||||||
* and instead of using the generic object setter, that would blow up the type and invalidate the map,
|
|
||||||
* unbox it and call the primitive setter instead
|
|
||||||
*/
|
|
||||||
public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
|
|
||||||
final Class<? extends Number> boxedForType = getBoxedType(forType);
|
|
||||||
//object setter that checks for primitive if current type is primitive
|
|
||||||
|
|
||||||
return MH.guardWithTest(
|
|
||||||
MH.insertArguments(
|
|
||||||
MH.dropArguments(
|
|
||||||
IS_TYPE_GUARD,
|
|
||||||
1,
|
|
||||||
Object.class),
|
|
||||||
0,
|
|
||||||
boxedForType),
|
|
||||||
MH.asType(
|
|
||||||
primitiveSetter,
|
|
||||||
objectSetter.type()),
|
|
||||||
objectSetter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -699,8 +763,30 @@ public final class ObjectClassGenerator {
|
|||||||
}
|
}
|
||||||
return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
|
return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
|
||||||
default:
|
default:
|
||||||
assert false;
|
throw new UnsupportedOperationException(forType + "=>" + type);
|
||||||
return null;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unboxed (primitive) type for an object
|
||||||
|
* @param o object
|
||||||
|
* @return primive type or Object.class if not primitive
|
||||||
|
*/
|
||||||
|
public static Class<?> unboxedFieldType(final Object o) {
|
||||||
|
if (OBJECT_FIELDS_ONLY) {
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o == null) {
|
||||||
|
return Object.class;
|
||||||
|
} else if (o.getClass() == Integer.class) {
|
||||||
|
return int.class;
|
||||||
|
} else if (o.getClass() == Long.class) {
|
||||||
|
return long.class;
|
||||||
|
} else if (o.getClass() == Double.class) {
|
||||||
|
return double.class;
|
||||||
|
} else {
|
||||||
|
return Object.class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,80 +799,9 @@ public final class ObjectClassGenerator {
|
|||||||
return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
|
return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Provide generic getters and setters for undefined types. If a type is undefined, all
|
|
||||||
// and marshals the set to the correct setter depending on the type of the value being set.
|
|
||||||
// Note that there are no actual undefined versions of int, long and double in JavaScript,
|
|
||||||
// but executing toInt32, toLong and toNumber always returns a working result, 0, 0L or NaN
|
|
||||||
//
|
|
||||||
|
|
||||||
/** The value of Undefined cast to an int32 */
|
|
||||||
public static final int UNDEFINED_INT = 0;
|
|
||||||
/** The value of Undefined cast to a long */
|
|
||||||
public static final long UNDEFINED_LONG = 0L;
|
|
||||||
/** The value of Undefined cast to a double */
|
|
||||||
public static final double UNDEFINED_DOUBLE = Double.NaN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute type name for correct undefined getter
|
|
||||||
* @param type the type
|
|
||||||
* @return name of getter
|
|
||||||
*/
|
|
||||||
private static String typeName(final Type type) {
|
|
||||||
String name = type.getTypeClass().getName();
|
|
||||||
final int dot = name.lastIndexOf('.');
|
|
||||||
if (dot != -1) {
|
|
||||||
name = name.substring(dot + 1);
|
|
||||||
}
|
|
||||||
return Character.toUpperCase(name.charAt(0)) + name.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles for undefined getters of the different types
|
|
||||||
*/
|
|
||||||
private static final MethodHandle[] GET_UNDEFINED = new MethodHandle[ObjectClassGenerator.getNumberOfAccessorTypes()];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to wrap getters for undefined values, where this matters. Currently only in dual fields.
|
|
||||||
* If an object starts out as undefined it needs special getters until it has been assigned
|
|
||||||
* something the first time
|
|
||||||
*
|
|
||||||
* @param returnType type to cast the undefined to
|
|
||||||
*
|
|
||||||
* @return undefined as returnType
|
|
||||||
*/
|
|
||||||
public static MethodHandle getUndefined(final Class<?> returnType) {
|
|
||||||
return GET_UNDEFINED[ObjectClassGenerator.getAccessorTypeIndex(returnType)];
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
int pos = 0;
|
|
||||||
for (final Type type : ACCESSOR_TYPES) {
|
|
||||||
GET_UNDEFINED[pos++] = findOwnMH("getUndefined" + typeName(type), type.getTypeClass(), Object.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static int getUndefinedInt(final Object obj) {
|
|
||||||
return UNDEFINED_INT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static long getUndefinedLong(final Object obj) {
|
|
||||||
return UNDEFINED_LONG;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static double getUndefinedDouble(final Object obj) {
|
|
||||||
return UNDEFINED_DOUBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static Object getUndefinedObject(final Object obj) {
|
|
||||||
return ScriptRuntime.UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||||
return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
|
return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,22 +28,21 @@ package jdk.nashorn.internal.codegen;
|
|||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import jdk.nashorn.internal.ir.Symbol;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||||
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for object creation code generation.
|
* Base class for object creation code generation.
|
||||||
|
* @param <T> value type
|
||||||
*/
|
*/
|
||||||
public abstract class ObjectCreator {
|
public abstract class ObjectCreator<T> {
|
||||||
|
|
||||||
/** List of keys to initiate in this ObjectCreator */
|
/** List of keys & symbols to initiate in this ObjectCreator */
|
||||||
protected final List<String> keys;
|
final List<MapTuple<T>> tuples;
|
||||||
|
|
||||||
/** List of symbols to initiate in this ObjectCreator */
|
|
||||||
protected final List<Symbol> symbols;
|
|
||||||
|
|
||||||
/** Code generator */
|
/** Code generator */
|
||||||
protected final CodeGenerator codegen;
|
final CodeGenerator codegen;
|
||||||
|
|
||||||
/** Property map */
|
/** Property map */
|
||||||
protected PropertyMap propertyMap;
|
protected PropertyMap propertyMap;
|
||||||
@ -55,15 +54,13 @@ public abstract class ObjectCreator {
|
|||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param codegen the code generator
|
* @param codegen the code generator
|
||||||
* @param keys the keys
|
* @param tuples key,symbol,value (optional) tuples
|
||||||
* @param symbols the symbols corresponding to keys, same index
|
|
||||||
* @param isScope is this object scope
|
* @param isScope is this object scope
|
||||||
* @param hasArguments does the created object have an "arguments" property
|
* @param hasArguments does the created object have an "arguments" property
|
||||||
*/
|
*/
|
||||||
protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
|
ObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
|
||||||
this.codegen = codegen;
|
this.codegen = codegen;
|
||||||
this.keys = keys;
|
this.tuples = tuples;
|
||||||
this.symbols = symbols;
|
|
||||||
this.isScope = isScope;
|
this.isScope = isScope;
|
||||||
this.hasArguments = hasArguments;
|
this.hasArguments = hasArguments;
|
||||||
}
|
}
|
||||||
@ -85,8 +82,8 @@ public abstract class ObjectCreator {
|
|||||||
* @param clazz type of MapCreator
|
* @param clazz type of MapCreator
|
||||||
* @return map creator instantiated by type
|
* @return map creator instantiated by type
|
||||||
*/
|
*/
|
||||||
protected MapCreator newMapCreator(final Class<?> clazz) {
|
protected MapCreator<?> newMapCreator(final Class<? extends ScriptObject> clazz) {
|
||||||
return new MapCreator(clazz, keys, symbols);
|
return new MapCreator<>(clazz, tuples);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,6 +104,10 @@ public abstract class ObjectCreator {
|
|||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PropertyMap getMap() {
|
||||||
|
return propertyMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this a scope object
|
* Is this a scope object
|
||||||
* @return true if scope
|
* @return true if scope
|
||||||
@ -122,4 +123,26 @@ public abstract class ObjectCreator {
|
|||||||
protected boolean hasArguments() {
|
protected boolean hasArguments() {
|
||||||
return hasArguments;
|
return hasArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Technique for loading an initial value. Defined by anonymous subclasses in code gen.
|
||||||
|
*
|
||||||
|
* @param value Value to load.
|
||||||
|
*/
|
||||||
|
protected abstract void loadValue(T value);
|
||||||
|
|
||||||
|
MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) {
|
||||||
|
loadValue(tuple.value);
|
||||||
|
if (pack && tuple.isPrimitive()) {
|
||||||
|
method.pack();
|
||||||
|
} else {
|
||||||
|
method.convert(Type.OBJECT);
|
||||||
|
}
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple) {
|
||||||
|
return loadTuple(method, tuple, true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
86
nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java
Normal file
86
nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2014, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.nashorn.internal.codegen;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data structure that maps one or several function nodes (by their unique id:s, not by
|
||||||
|
* the FunctionNode object itself, due to copy on write changing it several times through
|
||||||
|
* code generation.
|
||||||
|
*/
|
||||||
|
public class ParamTypeMap {
|
||||||
|
final Map<Integer, Type[]> map = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param functionNode functionNode
|
||||||
|
* @param type method type found at runtime corresponding to parameter guess
|
||||||
|
*/
|
||||||
|
public ParamTypeMap(final FunctionNode functionNode, final MethodType type) {
|
||||||
|
this(functionNode.getId(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param functionNodeId function node id
|
||||||
|
* @param type method type found at runtime corresponding to parameter guess
|
||||||
|
*/
|
||||||
|
public ParamTypeMap(final int functionNodeId, final MethodType type) {
|
||||||
|
final Type[] types = new Type[type.parameterCount()];
|
||||||
|
int pos = 0;
|
||||||
|
for (final Class<?> p : type.parameterArray()) {
|
||||||
|
types[pos++] = Type.typeFor(p);
|
||||||
|
}
|
||||||
|
map.put(functionNodeId, types);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParamTypeMap(final Map<FunctionNode, Type[]> typeMap) {
|
||||||
|
for (Map.Entry<FunctionNode, Type[]> entry : typeMap.entrySet()) {
|
||||||
|
map.put(entry.getKey().getId(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the parameter type for this parameter position, or
|
||||||
|
* null if now known
|
||||||
|
* @param functionNode functionNode
|
||||||
|
* @param pos position
|
||||||
|
* @return parameter type for this callsite if known
|
||||||
|
*/
|
||||||
|
Type get(final FunctionNode functionNode, final int pos) {
|
||||||
|
final Type[] types = map.get(functionNode.getId());
|
||||||
|
assert types == null || pos < types.length;
|
||||||
|
if (types != null && pos < types.length) {
|
||||||
|
return types[pos];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
110
nashorn/src/jdk/nashorn/internal/codegen/ProgramPoints.java
Normal file
110
nashorn/src/jdk/nashorn/internal/codegen/ProgramPoints.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.FIRST_PROGRAM_POINT;
|
||||||
|
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.MAX_PROGRAM_POINT_VALUE;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
import jdk.nashorn.internal.ir.AccessNode;
|
||||||
|
import jdk.nashorn.internal.ir.BinaryNode;
|
||||||
|
import jdk.nashorn.internal.ir.CallNode;
|
||||||
|
import jdk.nashorn.internal.ir.Expression;
|
||||||
|
import jdk.nashorn.internal.ir.FunctionNode;
|
||||||
|
import jdk.nashorn.internal.ir.IdentNode;
|
||||||
|
import jdk.nashorn.internal.ir.IndexNode;
|
||||||
|
import jdk.nashorn.internal.ir.LexicalContext;
|
||||||
|
import jdk.nashorn.internal.ir.Node;
|
||||||
|
import jdk.nashorn.internal.ir.Optimistic;
|
||||||
|
import jdk.nashorn.internal.ir.UnaryNode;
|
||||||
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find program points in the code that are needed for optimistic assumptions
|
||||||
|
*/
|
||||||
|
class ProgramPoints extends NodeVisitor<LexicalContext> {
|
||||||
|
|
||||||
|
private final Deque<int[]> nextProgramPoint = new ArrayDeque<>();
|
||||||
|
|
||||||
|
ProgramPoints() {
|
||||||
|
super(new LexicalContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int next() {
|
||||||
|
final int next = nextProgramPoint.peek()[0]++;
|
||||||
|
if(next > MAX_PROGRAM_POINT_VALUE) {
|
||||||
|
throw new AssertionError("Function has more than " + MAX_PROGRAM_POINT_VALUE + " program points");
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||||
|
nextProgramPoint.push(new int[] { FIRST_PROGRAM_POINT });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||||
|
nextProgramPoint.pop();
|
||||||
|
return functionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optimistic setProgramPoint(final Optimistic optimistic, final int programPoint) {
|
||||||
|
final Expression node = (Expression)optimistic.setProgramPoint(programPoint);
|
||||||
|
return (Optimistic)node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node leaveIdentNode(final IdentNode identNode) {
|
||||||
|
return (Node)setProgramPoint(identNode, next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node leaveCallNode(final CallNode callNode) {
|
||||||
|
return (Node)setProgramPoint(callNode, next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node leaveAccessNode(final AccessNode accessNode) {
|
||||||
|
return (Node)setProgramPoint(accessNode, next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node leaveIndexNode(final IndexNode indexNode) {
|
||||||
|
return (Node)setProgramPoint(indexNode, next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node leaveBinaryNode(final BinaryNode binaryNode) {
|
||||||
|
return (Node)setProgramPoint(binaryNode, next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node leaveUnaryNode(final UnaryNode unaryNode) {
|
||||||
|
return (Node)setProgramPoint(unaryNode, next());
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ package jdk.nashorn.internal.codegen;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import jdk.nashorn.internal.codegen.types.Range;
|
import jdk.nashorn.internal.codegen.types.Range;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.Assignment;
|
import jdk.nashorn.internal.ir.Assignment;
|
||||||
@ -242,7 +243,7 @@ final class RangeAnalyzer extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) {
|
private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) {
|
||||||
setRange(node.rhs(), range);
|
setRange(node.getExpression(), range);
|
||||||
setRange(node, range);
|
setRange(node, range);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -307,19 +308,18 @@ final class RangeAnalyzer extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
switch (node.tokenType()) {
|
switch (node.tokenType()) {
|
||||||
case DECPREFIX:
|
case DECPREFIX:
|
||||||
case DECPOSTFIX:
|
case DECPOSTFIX:
|
||||||
return leaveSelfModifyingAssign(node, RANGE.sub(node.rhs().getSymbol().getRange(), Range.createRange(1)));
|
return leaveSelfModifyingAssign(node, RANGE.sub(node.getExpression().getSymbol().getRange(), Range.createRange(1)));
|
||||||
case INCPREFIX:
|
case INCPREFIX:
|
||||||
case INCPOSTFIX:
|
case INCPOSTFIX:
|
||||||
return leaveSelfModifyingAssign(node, RANGE.add(node.rhs().getSymbol().getRange(), Range.createRange(1)));
|
return leaveSelfModifyingAssign(node, RANGE.add(node.getExpression().getSymbol().getRange(), Range.createRange(1)));
|
||||||
default:
|
default:
|
||||||
assert false;
|
throw new UnsupportedOperationException("" + node.tokenType());
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveADD(final UnaryNode node) {
|
public Node leaveADD(final UnaryNode node) {
|
||||||
Range range = node.rhs().getSymbol().getRange();
|
Range range = node.getExpression().getSymbol().getRange();
|
||||||
if (!range.getType().isNumeric()) {
|
if (!range.getType().isNumeric()) {
|
||||||
range = Range.createTypeRange(Type.NUMBER);
|
range = Range.createTypeRange(Type.NUMBER);
|
||||||
}
|
}
|
||||||
@ -341,7 +341,7 @@ final class RangeAnalyzer extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveSUB(final UnaryNode node) {
|
public Node leaveSUB(final UnaryNode node) {
|
||||||
setRange(node, RANGE.neg(node.rhs().getSymbol().getRange()));
|
setRange(node, RANGE.neg(node.getExpression().getSymbol().getRange()));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,6 @@ import jdk.nashorn.internal.codegen.types.Type;
|
|||||||
import jdk.nashorn.internal.ir.RuntimeNode;
|
import jdk.nashorn.internal.ir.RuntimeNode;
|
||||||
import jdk.nashorn.internal.ir.RuntimeNode.Request;
|
import jdk.nashorn.internal.ir.RuntimeNode.Request;
|
||||||
import jdk.nashorn.internal.lookup.Lookup;
|
import jdk.nashorn.internal.lookup.Lookup;
|
||||||
import jdk.nashorn.internal.lookup.MethodHandleFactory;
|
|
||||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||||
|
|
||||||
@ -59,7 +58,7 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
|||||||
public final class RuntimeCallSite extends MutableCallSite {
|
public final class RuntimeCallSite extends MutableCallSite {
|
||||||
static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "runtimeBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
|
static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "runtimeBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
|
||||||
|
|
||||||
private static final MethodHandle NEXT = findOwnMH("next", MethodHandle.class, String.class);
|
private static final MethodHandle NEXT = findOwnMH_V("next", MethodHandle.class, String.class);
|
||||||
|
|
||||||
private final RuntimeNode.Request request;
|
private final RuntimeNode.Request request;
|
||||||
|
|
||||||
@ -89,7 +88,7 @@ public final class RuntimeCallSite extends MutableCallSite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first type to try to use for this genrated runtime node
|
* The first type to try to use for this generated runtime node
|
||||||
*
|
*
|
||||||
* @return a type
|
* @return a type
|
||||||
*/
|
*/
|
||||||
@ -351,19 +350,19 @@ public final class RuntimeCallSite extends MutableCallSite {
|
|||||||
/** Unbox cache */
|
/** Unbox cache */
|
||||||
private static final Map<Class<?>, MethodHandle> UNBOX;
|
private static final Map<Class<?>, MethodHandle> UNBOX;
|
||||||
|
|
||||||
private static final MethodHandle CHECKCAST = findOwnMH("checkcast", boolean.class, Class.class, Object.class);
|
private static final MethodHandle CHECKCAST = findOwnMH_S("checkcast", boolean.class, Class.class, Object.class);
|
||||||
private static final MethodHandle CHECKCAST2 = findOwnMH("checkcast", boolean.class, Class.class, Object.class, Object.class);
|
private static final MethodHandle CHECKCAST2 = findOwnMH_S("checkcast", boolean.class, Class.class, Object.class, Object.class);
|
||||||
private static final MethodHandle ADDCHECK = findOwnMH("ADDcheck", boolean.class, int.class, int.class);
|
private static final MethodHandle ADDCHECK = findOwnMH_S("ADDcheck", boolean.class, int.class, int.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build maps of correct boxing operations
|
* Build maps of correct boxing operations
|
||||||
*/
|
*/
|
||||||
static {
|
static {
|
||||||
UNBOX = new HashMap<>();
|
UNBOX = new HashMap<>();
|
||||||
UNBOX.put(Boolean.class, findOwnMH("unboxZ", int.class, Object.class));
|
UNBOX.put(Boolean.class, findOwnMH_S("unboxZ", int.class, Object.class));
|
||||||
UNBOX.put(Integer.class, findOwnMH("unboxI", int.class, Object.class));
|
UNBOX.put(Integer.class, findOwnMH_S("unboxI", int.class, Object.class));
|
||||||
UNBOX.put(Long.class, findOwnMH("unboxJ", long.class, Object.class));
|
UNBOX.put(Long.class, findOwnMH_S("unboxJ", long.class, Object.class));
|
||||||
UNBOX.put(Number.class, findOwnMH("unboxD", double.class, Object.class));
|
UNBOX.put(Number.class, findOwnMH_S("unboxD", double.class, Object.class));
|
||||||
|
|
||||||
METHODS = new HashMap<>();
|
METHODS = new HashMap<>();
|
||||||
|
|
||||||
@ -375,9 +374,9 @@ public final class RuntimeCallSite extends MutableCallSite {
|
|||||||
|
|
||||||
final boolean isCmp = Request.isComparison(req);
|
final boolean isCmp = Request.isComparison(req);
|
||||||
|
|
||||||
METHODS.put(req.name() + "int", findOwnMH(req.name(), (isCmp ? boolean.class : int.class), int.class, int.class));
|
METHODS.put(req.name() + "int", findOwnMH_S(req.name(), (isCmp ? boolean.class : int.class), int.class, int.class));
|
||||||
METHODS.put(req.name() + "long", findOwnMH(req.name(), (isCmp ? boolean.class : long.class), long.class, long.class));
|
METHODS.put(req.name() + "long", findOwnMH_S(req.name(), (isCmp ? boolean.class : long.class), long.class, long.class));
|
||||||
METHODS.put(req.name() + "double", findOwnMH(req.name(), (isCmp ? boolean.class : double.class), double.class, double.class));
|
METHODS.put(req.name() + "double", findOwnMH_S(req.name(), (isCmp ? boolean.class : double.class), double.class, double.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,12 +673,11 @@ public final class RuntimeCallSite extends MutableCallSite {
|
|||||||
return ((Number)obj).doubleValue();
|
return ((Number)obj).doubleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||||
try {
|
return MH.findStatic(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
|
||||||
return MH.findStatic(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
|
|
||||||
} catch (final MethodHandleFactory.LookupException e) {
|
|
||||||
return MH.findVirtual(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||||
|
return MH.findVirtual(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.codegen;
|
package jdk.nashorn.internal.codegen;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
@ -81,6 +83,7 @@ class SharedScopeCall {
|
|||||||
this.valueType = valueType;
|
this.valueType = valueType;
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
this.paramTypes = paramTypes;
|
this.paramTypes = paramTypes;
|
||||||
|
assert (flags & CALLSITE_OPTIMISTIC) == 0;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
// If paramTypes is not null this is a call, otherwise it's just a get.
|
// If paramTypes is not null this is a call, otherwise it's just a get.
|
||||||
this.isCall = paramTypes != null;
|
this.isCall = paramTypes != null;
|
||||||
@ -150,7 +153,10 @@ class SharedScopeCall {
|
|||||||
method._goto(parentLoopStart);
|
method._goto(parentLoopStart);
|
||||||
method.label(parentLoopDone);
|
method.label(parentLoopDone);
|
||||||
|
|
||||||
method.dynamicGet(valueType, symbol.getName(), flags, isCall);
|
assert !isCall || valueType.isObject(); // Callables are always objects
|
||||||
|
// If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously
|
||||||
|
// only apply to the call.
|
||||||
|
method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall);
|
||||||
|
|
||||||
// If this is a get we're done, otherwise call the value as function.
|
// If this is a get we're done, otherwise call the value as function.
|
||||||
if (isCall) {
|
if (isCall) {
|
||||||
@ -164,6 +170,7 @@ class SharedScopeCall {
|
|||||||
slot++;
|
slot++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Shared scope calls disabled in optimistic world.
|
||||||
method.dynamicCall(returnType, 2 + paramTypes.length, flags);
|
method.dynamicCall(returnType, 2 + paramTypes.length, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,17 +186,16 @@ class SharedScopeCall {
|
|||||||
final Type[] params = new Type[paramTypes.length + 2];
|
final Type[] params = new Type[paramTypes.length + 2];
|
||||||
params[0] = Type.typeFor(ScriptObject.class);
|
params[0] = Type.typeFor(ScriptObject.class);
|
||||||
params[1] = Type.INT;
|
params[1] = Type.INT;
|
||||||
int i = 2;
|
System.arraycopy(paramTypes, 0, params, 2, paramTypes.length);
|
||||||
for (Type type : paramTypes) {
|
|
||||||
if (type.isObject()) {
|
|
||||||
type = Type.OBJECT;
|
|
||||||
}
|
|
||||||
params[i++] = type;
|
|
||||||
}
|
|
||||||
staticSignature = Type.getMethodDescriptor(returnType, params);
|
staticSignature = Type.getMethodDescriptor(returnType, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return staticSignature;
|
return staticSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return methodName + " " + staticSignature;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,18 +27,18 @@ package jdk.nashorn.internal.codegen;
|
|||||||
|
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
||||||
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
|
||||||
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.unboxedFieldType;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
|
||||||
import jdk.nashorn.internal.ir.Expression;
|
import jdk.nashorn.internal.ir.Expression;
|
||||||
import jdk.nashorn.internal.ir.LiteralNode;
|
import jdk.nashorn.internal.ir.LiteralNode;
|
||||||
import jdk.nashorn.internal.ir.Symbol;
|
|
||||||
import jdk.nashorn.internal.runtime.Property;
|
import jdk.nashorn.internal.runtime.Property;
|
||||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||||
|
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||||
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
|
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
|
||||||
import jdk.nashorn.internal.scripts.JO;
|
import jdk.nashorn.internal.scripts.JO;
|
||||||
@ -46,21 +46,16 @@ import jdk.nashorn.internal.scripts.JO;
|
|||||||
/**
|
/**
|
||||||
* An object creator that uses spill properties.
|
* An object creator that uses spill properties.
|
||||||
*/
|
*/
|
||||||
public class SpillObjectCreator extends ObjectCreator {
|
public final class SpillObjectCreator extends ObjectCreator<Expression> {
|
||||||
|
|
||||||
private final List<Expression> values;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param codegen code generator
|
* @param codegen code generator
|
||||||
* @param keys keys for fields in object
|
* @param tuples tuples for key, symbol, value
|
||||||
* @param symbols symbols for fields in object
|
|
||||||
* @param values list of values corresponding to keys
|
|
||||||
*/
|
*/
|
||||||
protected SpillObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<Expression> values) {
|
SpillObjectCreator(final CodeGenerator codegen, final List<MapTuple<Expression>> tuples) {
|
||||||
super(codegen, keys, symbols, false, false);
|
super(codegen, tuples, false, false);
|
||||||
this.values = values;
|
|
||||||
makeMap();
|
makeMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,82 +63,120 @@ public class SpillObjectCreator extends ObjectCreator {
|
|||||||
protected void makeObject(final MethodEmitter method) {
|
protected void makeObject(final MethodEmitter method) {
|
||||||
assert !isScope() : "spill scope objects are not currently supported";
|
assert !isScope() : "spill scope objects are not currently supported";
|
||||||
|
|
||||||
final int length = keys.size();
|
final int length = tuples.size();
|
||||||
final Object[] presetValues = new Object[length];
|
final long[] jpresetValues = new long[ScriptObject.spillAllocationLength(length)];
|
||||||
|
final Object[] opresetValues = new Object[ScriptObject.spillAllocationLength(length)];
|
||||||
final Set<Integer> postsetValues = new LinkedHashSet<>();
|
final Set<Integer> postsetValues = new LinkedHashSet<>();
|
||||||
final int callSiteFlags = codegen.getCallSiteFlags();
|
final int callSiteFlags = codegen.getCallSiteFlags();
|
||||||
ArrayData arrayData = ArrayData.allocate(new Object[0]);
|
ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY);
|
||||||
|
|
||||||
// Compute constant property values
|
// Compute constant property values
|
||||||
for (int i = 0; i < length; i++) {
|
int pos = 0;
|
||||||
final String key = keys.get(i);
|
for (final MapTuple<Expression> tuple : tuples) {
|
||||||
final Expression value = values.get(i);
|
final String key = tuple.key;
|
||||||
|
final Expression value = tuple.value;
|
||||||
|
|
||||||
if (value == null) {
|
if (value != null) {
|
||||||
continue; // getter or setter
|
final Object constantValue = LiteralNode.objectAsConstant(value);
|
||||||
}
|
if (constantValue == LiteralNode.POSTSET_MARKER) {
|
||||||
|
postsetValues.add(pos);
|
||||||
|
} else {
|
||||||
|
final Property property = propertyMap.findProperty(key);
|
||||||
|
if (property != null) {
|
||||||
|
// normal property key
|
||||||
|
property.setCurrentType(unboxedFieldType(constantValue));
|
||||||
|
final int slot = property.getSlot();
|
||||||
|
if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) {
|
||||||
|
jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
|
||||||
|
} else {
|
||||||
|
opresetValues[slot] = constantValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// array index key
|
||||||
|
final long oldLength = arrayData.length();
|
||||||
|
final int index = ArrayIndex.getArrayIndex(key);
|
||||||
|
final long longIndex = ArrayIndex.toLongIndex(index);
|
||||||
|
|
||||||
final Object constantValue = LiteralNode.objectAsConstant(value);
|
assert ArrayIndex.isValidArrayIndex(index);
|
||||||
if (constantValue == LiteralNode.POSTSET_MARKER) {
|
|
||||||
postsetValues.add(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Property property = propertyMap.findProperty(key);
|
if (longIndex >= oldLength) {
|
||||||
if (property != null) {
|
arrayData = arrayData.ensure(longIndex);
|
||||||
// normal property key
|
}
|
||||||
presetValues[property.getSlot()] = constantValue;
|
|
||||||
} else {
|
//avoid blowing up the array if we can
|
||||||
// array index key
|
if (constantValue instanceof Integer) {
|
||||||
final long oldLength = arrayData.length();
|
arrayData = arrayData.set(index, ((Integer)constantValue).intValue(), false);
|
||||||
final int index = ArrayIndex.getArrayIndex(key);
|
} else if (constantValue instanceof Long) {
|
||||||
assert ArrayIndex.isValidArrayIndex(index);
|
arrayData = arrayData.set(index, ((Long)constantValue).longValue(), false);
|
||||||
final long longIndex = ArrayIndex.toLongIndex(index);
|
} else if (constantValue instanceof Double) {
|
||||||
if (longIndex >= oldLength) {
|
arrayData = arrayData.set(index, ((Double)constantValue).doubleValue(), false);
|
||||||
arrayData = arrayData.ensure(longIndex);
|
} else {
|
||||||
}
|
arrayData = arrayData.set(index, constantValue, false);
|
||||||
arrayData = arrayData.set(index, constantValue, false);
|
}
|
||||||
if (longIndex > oldLength) {
|
|
||||||
arrayData = arrayData.delete(oldLength, longIndex - 1);
|
if (longIndex > oldLength) {
|
||||||
|
arrayData = arrayData.delete(oldLength, longIndex - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//assert postsetValues.isEmpty() : "test me " + postsetValues;
|
||||||
|
|
||||||
// create object and invoke constructor
|
// create object and invoke constructor
|
||||||
method._new(JO.class).dup();
|
method._new(JO.class).dup();
|
||||||
codegen.loadConstant(propertyMap);
|
codegen.loadConstant(propertyMap);
|
||||||
method.invoke(constructorNoLookup(JO.class, PropertyMap.class));
|
|
||||||
|
|
||||||
// Set spill array with preset values
|
//load primitive values to j spill array
|
||||||
method.dup();
|
codegen.loadConstant(jpresetValues);
|
||||||
codegen.loadConstant(presetValues);
|
for (final int i : postsetValues) {
|
||||||
method.putField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
|
final MapTuple<Expression> tuple = tuples.get(i);
|
||||||
|
final Property property = propertyMap.findProperty(tuple.key);
|
||||||
|
if (property != null && tuple.isPrimitive()) {
|
||||||
|
method.dup();
|
||||||
|
method.load(property.getSlot());
|
||||||
|
loadTuple(method, tuple);
|
||||||
|
method.arraystore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set array data if any
|
//load object values to o spill array
|
||||||
|
codegen.loadConstant(opresetValues);
|
||||||
|
for (final int i : postsetValues) {
|
||||||
|
final MapTuple<Expression> tuple = tuples.get(i);
|
||||||
|
final Property property = propertyMap.findProperty(tuple.key);
|
||||||
|
if (property != null && !tuple.isPrimitive()) {
|
||||||
|
method.dup();
|
||||||
|
method.load(property.getSlot());
|
||||||
|
loadTuple(method, tuple);
|
||||||
|
method.arraystore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//instantiate the script object with spill objects
|
||||||
|
method.invoke(constructorNoLookup(JO.class, PropertyMap.class, long[].class, Object[].class));
|
||||||
|
|
||||||
|
// Set prefix array data if any
|
||||||
if (arrayData.length() > 0) {
|
if (arrayData.length() > 0) {
|
||||||
method.dup();
|
method.dup();
|
||||||
codegen.loadConstant(arrayData);
|
codegen.loadConstant(arrayData);
|
||||||
method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray",void.class, ArrayData.class));
|
method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray", void.class, ArrayData.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create properties with non-constant values
|
// set postfix
|
||||||
for (int i : postsetValues) {
|
for (final int i : postsetValues) {
|
||||||
final String key = keys.get(i);
|
final MapTuple<Expression> tuple = tuples.get(i);
|
||||||
final Property property = propertyMap.findProperty(key);
|
final Property property = propertyMap.findProperty(tuple.key);
|
||||||
|
|
||||||
if (property == null) {
|
if (property == null) {
|
||||||
final int index = ArrayIndex.getArrayIndex(key);
|
final int index = ArrayIndex.getArrayIndex(tuple.key);
|
||||||
assert ArrayIndex.isValidArrayIndex(index);
|
assert ArrayIndex.isValidArrayIndex(index);
|
||||||
method.dup();
|
method.dup();
|
||||||
method.load(ArrayIndex.toLongIndex(index));
|
method.load(ArrayIndex.toLongIndex(index));
|
||||||
codegen.load(values.get(i));
|
//method.println("putting " + tuple + " into arraydata");
|
||||||
|
loadTuple(method, tuple);
|
||||||
method.dynamicSetIndex(callSiteFlags);
|
method.dynamicSetIndex(callSiteFlags);
|
||||||
} else {
|
|
||||||
method.dup();
|
|
||||||
method.getField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
|
|
||||||
method.load(property.getSlot());
|
|
||||||
codegen.load(values.get(i), OBJECT);
|
|
||||||
method.arraystore();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,14 +184,12 @@ public class SpillObjectCreator extends ObjectCreator {
|
|||||||
@Override
|
@Override
|
||||||
protected PropertyMap makeMap() {
|
protected PropertyMap makeMap() {
|
||||||
assert propertyMap == null : "property map already initialized";
|
assert propertyMap == null : "property map already initialized";
|
||||||
|
propertyMap = new MapCreator<>(JO.class, tuples).makeSpillMap(false);
|
||||||
propertyMap = new MapCreator(JO.class, keys, symbols) {
|
|
||||||
@Override
|
|
||||||
protected int getPropertyFlags(Symbol symbol, boolean hasArguments) {
|
|
||||||
return super.getPropertyFlags(symbol, hasArguments) | Property.IS_SPILL | Property.IS_ALWAYS_OBJECT;
|
|
||||||
}
|
|
||||||
}.makeSpillMap(false);
|
|
||||||
|
|
||||||
return propertyMap;
|
return propertyMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadValue(final Expression node) {
|
||||||
|
codegen.load(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,20 +81,16 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the split
|
* Execute the split.
|
||||||
|
* @param fn the function to split
|
||||||
|
* @param top whether this is the topmost compiled function (it's either a program, or we're doing a recompilation).
|
||||||
*/
|
*/
|
||||||
FunctionNode split(final FunctionNode fn) {
|
FunctionNode split(final FunctionNode fn, boolean top) {
|
||||||
FunctionNode functionNode = fn;
|
FunctionNode functionNode = fn;
|
||||||
|
|
||||||
if (functionNode.isLazy()) {
|
|
||||||
LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
|
|
||||||
return functionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.finest("Initiating split of '", functionNode.getName(), "'");
|
LOG.finest("Initiating split of '", functionNode.getName(), "'");
|
||||||
|
|
||||||
long weight = WeighNodes.weigh(functionNode);
|
long weight = WeighNodes.weigh(functionNode);
|
||||||
final boolean top = fn.isProgram(); //compiler.getFunctionNode() == outermost;
|
|
||||||
|
|
||||||
// We know that our LexicalContext is empty outside the call to functionNode.accept(this) below,
|
// We know that our LexicalContext is empty outside the call to functionNode.accept(this) below,
|
||||||
// so we can pass null to all methods expecting a LexicalContext parameter.
|
// so we can pass null to all methods expecting a LexicalContext parameter.
|
||||||
@ -138,7 +134,7 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveFunctionNode(final FunctionNode nestedFunction) {
|
public Node leaveFunctionNode(final FunctionNode nestedFunction) {
|
||||||
FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
|
FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction, false);
|
||||||
lc.replace(nestedFunction, split);
|
lc.replace(nestedFunction, split);
|
||||||
return split;
|
return split;
|
||||||
}
|
}
|
||||||
@ -319,10 +315,7 @@ final class Splitter extends NodeVisitor<LexicalContext> {
|
|||||||
@Override
|
@Override
|
||||||
public boolean enterFunctionNode(final FunctionNode node) {
|
public boolean enterFunctionNode(final FunctionNode node) {
|
||||||
//only go into the function node for this splitter. any subfunctions are rejected
|
//only go into the function node for this splitter. any subfunctions are rejected
|
||||||
if (node == outermost && !node.isLazy()) {
|
return node == outermost;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
|
||||||
import jdk.nashorn.internal.ir.AccessNode;
|
import jdk.nashorn.internal.ir.AccessNode;
|
||||||
import jdk.nashorn.internal.ir.BinaryNode;
|
import jdk.nashorn.internal.ir.BinaryNode;
|
||||||
import jdk.nashorn.internal.ir.Block;
|
import jdk.nashorn.internal.ir.Block;
|
||||||
@ -69,24 +68,25 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
* Weight constants.
|
* Weight constants.
|
||||||
*/
|
*/
|
||||||
static final long FUNCTION_WEIGHT = 40;
|
static final long FUNCTION_WEIGHT = 40;
|
||||||
static final long AASTORE_WEIGHT = 2;
|
static final long AASTORE_WEIGHT = 2;
|
||||||
static final long ACCESS_WEIGHT = 4;
|
static final long ACCESS_WEIGHT = 4;
|
||||||
static final long ADD_WEIGHT = 10;
|
static final long ADD_WEIGHT = 10;
|
||||||
static final long BREAK_WEIGHT = 1;
|
static final long BREAK_WEIGHT = 1;
|
||||||
static final long CALL_WEIGHT = 10;
|
static final long CALL_WEIGHT = 10;
|
||||||
static final long CATCH_WEIGHT = 10;
|
static final long CATCH_WEIGHT = 10;
|
||||||
static final long CONTINUE_WEIGHT = 1;
|
static final long COMPARE_WEIGHT = 6;
|
||||||
static final long IF_WEIGHT = 2;
|
static final long CONTINUE_WEIGHT = 1;
|
||||||
|
static final long IF_WEIGHT = 2;
|
||||||
static final long LITERAL_WEIGHT = 10;
|
static final long LITERAL_WEIGHT = 10;
|
||||||
static final long LOOP_WEIGHT = 4;
|
static final long LOOP_WEIGHT = 4;
|
||||||
static final long NEW_WEIGHT = 6;
|
static final long NEW_WEIGHT = 6;
|
||||||
static final long FUNC_EXPR_WEIGHT = 20;
|
static final long FUNC_EXPR_WEIGHT = 20;
|
||||||
static final long RETURN_WEIGHT = 2;
|
static final long RETURN_WEIGHT = 2;
|
||||||
static final long SPLIT_WEIGHT = 40;
|
static final long SPLIT_WEIGHT = 40;
|
||||||
static final long SWITCH_WEIGHT = 8;
|
static final long SWITCH_WEIGHT = 8;
|
||||||
static final long THROW_WEIGHT = 2;
|
static final long THROW_WEIGHT = 2;
|
||||||
static final long VAR_WEIGHT = 40;
|
static final long VAR_WEIGHT = 40;
|
||||||
static final long WITH_WEIGHT = 8;
|
static final long WITH_WEIGHT = 8;
|
||||||
|
|
||||||
/** Accumulated weight. */
|
/** Accumulated weight. */
|
||||||
private long weight;
|
private long weight;
|
||||||
@ -446,22 +446,22 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveEQ(final BinaryNode binaryNode) {
|
public Node leaveEQ(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
|
public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveGE(final BinaryNode binaryNode) {
|
public Node leaveGE(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveGT(final BinaryNode binaryNode) {
|
public Node leaveGT(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -478,12 +478,12 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveLE(final BinaryNode binaryNode) {
|
public Node leaveLE(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveLT(final BinaryNode binaryNode) {
|
public Node leaveLT(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -498,12 +498,12 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveNE(final BinaryNode binaryNode) {
|
public Node leaveNE(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node leaveNE_STRICT(final BinaryNode binaryNode) {
|
public Node leaveNE_STRICT(final BinaryNode binaryNode) {
|
||||||
return runtimeNodeWeight(binaryNode);
|
return compareWeight(binaryNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -546,8 +546,8 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
|||||||
return unaryNode;
|
return unaryNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node runtimeNodeWeight(final BinaryNode binaryNode) {
|
private Node compareWeight(final BinaryNode binaryNode) {
|
||||||
weight += Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()).isObject() ? CALL_WEIGHT : 1;
|
weight += COMPARE_WEIGHT;
|
||||||
return binaryNode;
|
return binaryNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,10 +56,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
|
|||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
|
||||||
import jdk.nashorn.internal.runtime.JSType;
|
import jdk.nashorn.internal.runtime.JSType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,9 +85,20 @@ public final class BooleanType extends Type {
|
|||||||
return Boolean.class;
|
return Boolean.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getBytecodeStackType() {
|
||||||
|
return 'I';
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type loadUndefined(final MethodVisitor method) {
|
public Type loadUndefined(final MethodVisitor method) {
|
||||||
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_INT);
|
method.visitLdcInsn(UNDEFINED_INT);
|
||||||
|
return BOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type loadForcedInitializer(MethodVisitor method) {
|
||||||
|
method.visitInsn(ICONST_0);
|
||||||
return BOOLEAN;
|
return BOOLEAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,30 +135,29 @@ public final class BooleanType extends Type {
|
|||||||
|
|
||||||
if (to.isNumber()) {
|
if (to.isNumber()) {
|
||||||
convert(method, OBJECT);
|
convert(method, OBJECT);
|
||||||
invokeStatic(method, JSType.TO_NUMBER);
|
invokestatic(method, JSType.TO_NUMBER);
|
||||||
} else if (to.isInteger()) {
|
} else if (to.isInteger()) {
|
||||||
return to; // do nothing.
|
return to; // do nothing.
|
||||||
} else if (to.isLong()) {
|
} else if (to.isLong()) {
|
||||||
convert(method, OBJECT);
|
convert(method, OBJECT);
|
||||||
invokeStatic(method, JSType.TO_UINT32);
|
invokestatic(method, JSType.TO_UINT32);
|
||||||
} else if (to.isLong()) {
|
} else if (to.isLong()) {
|
||||||
convert(method, OBJECT);
|
convert(method, OBJECT);
|
||||||
invokeStatic(method, JSType.TO_LONG);
|
invokestatic(method, JSType.TO_LONG);
|
||||||
} else if (to.isString()) {
|
} else if (to.isString()) {
|
||||||
invokeStatic(method, VALUE_OF);
|
invokestatic(method, VALUE_OF);
|
||||||
invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING);
|
invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
|
||||||
} else if (to.isObject()) {
|
} else if (to.isObject()) {
|
||||||
invokeStatic(method, VALUE_OF);
|
invokestatic(method, VALUE_OF);
|
||||||
} else {
|
} else {
|
||||||
assert false : "Illegal conversion " + this + " -> " + to;
|
throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to);
|
||||||
}
|
}
|
||||||
|
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type add(final MethodVisitor method) {
|
public Type add(final MethodVisitor method, final int programPoint) {
|
||||||
assert false : "unsupported operation";
|
throw new UnsupportedOperationException("add");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,47 +36,52 @@ interface BytecodeNumericOps {
|
|||||||
* Pop and negate the value on top of the stack and push the result
|
* Pop and negate the value on top of the stack and push the result
|
||||||
*
|
*
|
||||||
* @param method method visitor
|
* @param method method visitor
|
||||||
*
|
* @param programPoint program point id
|
||||||
* @return result type
|
* @return result type
|
||||||
*/
|
*/
|
||||||
Type neg(MethodVisitor method);
|
Type neg(MethodVisitor method, int programPoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop two values on top of the stack and subtract the first from the
|
* Pop two values on top of the stack and subtract the first from the
|
||||||
* second, pushing the result on the stack
|
* second, pushing the result on the stack
|
||||||
*
|
*
|
||||||
* @param method method visitor
|
* @param method method visitor
|
||||||
*
|
* @param programPoint program point id
|
||||||
* @return result type
|
* @return result type
|
||||||
*/
|
*/
|
||||||
Type sub(MethodVisitor method);
|
Type sub(MethodVisitor method, int programPoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop and multiply the two values on top of the stack and push the result
|
* Pop and multiply the two values on top of the stack and push the result
|
||||||
* on the stack
|
* on the stack
|
||||||
*
|
*
|
||||||
* @param method method visitor
|
* @param method method visitor
|
||||||
*
|
* @param programPoint program point id
|
||||||
* @return result type
|
* @return result type
|
||||||
*/
|
*/
|
||||||
Type mul(MethodVisitor method);
|
Type mul(MethodVisitor method, int programPoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop two values on top of the stack and divide the first with the second,
|
* Pop two values on top of the stack and divide the first with the second,
|
||||||
* pushing the result on the stack
|
* pushing the result on the stack
|
||||||
*
|
*
|
||||||
* @param method method visitor
|
* @param method method visitor
|
||||||
*
|
* @param programPoint program point id
|
||||||
* @return result type
|
* @return result type
|
||||||
*/
|
*/
|
||||||
Type div(MethodVisitor method);
|
Type div(MethodVisitor method, int programPoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop two values on top of the stack and compute the modulo of the first
|
* Pop two values on top of the stack and compute the modulo of the first
|
||||||
* with the second, pushing the result on the stack
|
* with the second, pushing the result on the stack
|
||||||
*
|
*
|
||||||
* @param method method visitor
|
* Note that the rem method never takes a program point, because it
|
||||||
|
* can never be more optimistic than its widest operand - an int/int
|
||||||
|
* rem operation or a long/long rem operation can never return a
|
||||||
|
* winder remainder than the int or the long
|
||||||
*
|
*
|
||||||
|
* @param method method visitor
|
||||||
|
* @param programPoint program point id
|
||||||
* @return result type
|
* @return result type
|
||||||
*/
|
*/
|
||||||
Type rem(MethodVisitor method);
|
Type rem(MethodVisitor method);
|
||||||
|
@ -85,9 +85,10 @@ interface BytecodeOps {
|
|||||||
* first to the second, pushing the result on the stack
|
* first to the second, pushing the result on the stack
|
||||||
*
|
*
|
||||||
* @param method method visitor
|
* @param method method visitor
|
||||||
|
* @param programPoint program point id
|
||||||
* @return result type
|
* @return result type
|
||||||
*/
|
*/
|
||||||
Type add(MethodVisitor method);
|
Type add(MethodVisitor method, int programPoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a variable from a local slot to the stack
|
* Load a variable from a local slot to the stack
|
||||||
@ -128,6 +129,17 @@ interface BytecodeOps {
|
|||||||
*/
|
*/
|
||||||
Type loadUndefined(MethodVisitor method);
|
Type loadUndefined(MethodVisitor method);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the "forced initializer" value to the stack, used to ensure that a local variable has a value when it is
|
||||||
|
* read by the unwarranted optimism catch block.
|
||||||
|
*
|
||||||
|
* @param method method visitor.
|
||||||
|
*
|
||||||
|
* @return the forced initialization type at the top of the stack
|
||||||
|
*/
|
||||||
|
Type loadForcedInitializer(MethodVisitor method);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the "empty" value to the stack.
|
* Load the "empty" value to the stack.
|
||||||
*
|
*
|
||||||
|
@ -28,7 +28,6 @@ package jdk.nashorn.internal.codegen.types;
|
|||||||
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
|
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.I2D;
|
import static jdk.internal.org.objectweb.asm.Opcodes.I2D;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.I2L;
|
import static jdk.internal.org.objectweb.asm.Opcodes.I2L;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IADD;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IAND;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IAND;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
|
||||||
@ -37,25 +36,20 @@ import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_3;
|
|||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IDIV;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IMUL;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.INEG;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IOR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IOR;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IREM;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IREM;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ISHL;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ISHL;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ISHR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ISHR;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
|
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.ISUB;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IUSHR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IUSHR;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IXOR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IXOR;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
|
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type class: INT
|
* Type class: INT
|
||||||
@ -79,6 +73,11 @@ class IntType extends BitwiseType {
|
|||||||
return Integer.class;
|
return Integer.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getBytecodeStackType() {
|
||||||
|
return 'I';
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type ldc(final MethodVisitor method, final Object c) {
|
public Type ldc(final MethodVisitor method, final Object c) {
|
||||||
assert c instanceof Integer;
|
assert c instanceof Integer;
|
||||||
@ -86,36 +85,36 @@ class IntType extends BitwiseType {
|
|||||||
final int value = ((Integer) c).intValue();
|
final int value = ((Integer) c).intValue();
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case -1:
|
case -1:
|
||||||
method.visitInsn(ICONST_M1);
|
method.visitInsn(ICONST_M1);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
method.visitInsn(ICONST_0);
|
method.visitInsn(ICONST_0);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
method.visitInsn(ICONST_1);
|
method.visitInsn(ICONST_1);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
method.visitInsn(ICONST_2);
|
method.visitInsn(ICONST_2);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
method.visitInsn(ICONST_3);
|
method.visitInsn(ICONST_3);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
method.visitInsn(ICONST_4);
|
method.visitInsn(ICONST_4);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
method.visitInsn(ICONST_5);
|
method.visitInsn(ICONST_5);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (value == (byte) value) {
|
if (value == (byte) value) {
|
||||||
method.visitIntInsn(BIPUSH, value);
|
method.visitIntInsn(BIPUSH, value);
|
||||||
} else if (value == (short) value) {
|
} else if (value == (short) value) {
|
||||||
method.visitIntInsn(SIPUSH, value);
|
method.visitIntInsn(SIPUSH, value);
|
||||||
} else {
|
} else {
|
||||||
method.visitLdcInsn(c);
|
method.visitLdcInsn(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Type.INT;
|
return Type.INT;
|
||||||
@ -134,19 +133,19 @@ class IntType extends BitwiseType {
|
|||||||
} else if (to.isBoolean()) {
|
} else if (to.isBoolean()) {
|
||||||
//nop
|
//nop
|
||||||
} else if (to.isString()) {
|
} else if (to.isString()) {
|
||||||
invokeStatic(method, TO_STRING);
|
invokestatic(method, TO_STRING);
|
||||||
} else if (to.isObject()) {
|
} else if (to.isObject()) {
|
||||||
invokeStatic(method, VALUE_OF);
|
invokestatic(method, VALUE_OF);
|
||||||
} else {
|
} else {
|
||||||
assert false : "Illegal conversion " + this + " -> " + to;
|
throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to);
|
||||||
}
|
}
|
||||||
|
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type add(final MethodVisitor method) {
|
public Type add(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(IADD);
|
method.visitInvokeDynamicInsn("iadd", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,20 +199,20 @@ class IntType extends BitwiseType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type sub(final MethodVisitor method) {
|
public Type sub(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(ISUB);
|
method.visitInvokeDynamicInsn("isub", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type mul(final MethodVisitor method) {
|
public Type mul(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(IMUL);
|
method.visitInvokeDynamicInsn("imul", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type div(final MethodVisitor method) {
|
public Type div(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(IDIV);
|
method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,8 +223,8 @@ class IntType extends BitwiseType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type neg(final MethodVisitor method) {
|
public Type neg(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(INEG);
|
method.visitInvokeDynamicInsn("ineg", "(I)I", MATHBOOTSTRAP, programPoint);
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,19 +235,24 @@ class IntType extends BitwiseType {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type loadUndefined(final MethodVisitor method) {
|
public Type loadUndefined(final MethodVisitor method) {
|
||||||
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_INT);
|
method.visitLdcInsn(UNDEFINED_INT);
|
||||||
|
return INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type loadForcedInitializer(MethodVisitor method) {
|
||||||
|
method.visitInsn(ICONST_0);
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
|
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
|
||||||
assert false : "unsupported operation";
|
throw new UnsupportedOperationException("cmp" + (isCmpG ? 'g' : 'l'));
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type cmp(final MethodVisitor method) {
|
public Type cmp(final MethodVisitor method) {
|
||||||
assert false : "unsupported operation";
|
throw new UnsupportedOperationException("cmp");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,29 +27,25 @@ package jdk.nashorn.internal.codegen.types;
|
|||||||
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.L2D;
|
import static jdk.internal.org.objectweb.asm.Opcodes.L2D;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.L2I;
|
import static jdk.internal.org.objectweb.asm.Opcodes.L2I;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LADD;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LAND;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LAND;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LCMP;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LCMP;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_0;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_0;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_1;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_1;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LDIV;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LMUL;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LNEG;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LOR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LOR;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LREM;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LREM;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LSHL;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LSHL;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LSHR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LSHR;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LSTORE;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LSTORE;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LSUB;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LUSHR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LUSHR;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.LXOR;
|
import static jdk.internal.org.objectweb.asm.Opcodes.LXOR;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_LONG;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
import jdk.nashorn.internal.runtime.JSType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type class: LONG
|
* Type class: LONG
|
||||||
@ -76,6 +72,11 @@ class LongType extends BitwiseType {
|
|||||||
return Long.class;
|
return Long.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getBytecodeStackType() {
|
||||||
|
return 'J';
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type cmp(final MethodVisitor method) {
|
public Type cmp(final MethodVisitor method) {
|
||||||
method.visitInsn(LCMP);
|
method.visitInsn(LCMP);
|
||||||
@ -121,11 +122,11 @@ class LongType extends BitwiseType {
|
|||||||
if (to.isNumber()) {
|
if (to.isNumber()) {
|
||||||
method.visitInsn(L2D);
|
method.visitInsn(L2D);
|
||||||
} else if (to.isInteger()) {
|
} else if (to.isInteger()) {
|
||||||
method.visitInsn(L2I);
|
invokestatic(method, JSType.TO_INT32_L);
|
||||||
} else if (to.isBoolean()) {
|
} else if (to.isBoolean()) {
|
||||||
method.visitInsn(L2I);
|
method.visitInsn(L2I);
|
||||||
} else if (to.isObject()) {
|
} else if (to.isObject()) {
|
||||||
invokeStatic(method, VALUE_OF);
|
invokestatic(method, VALUE_OF);
|
||||||
} else {
|
} else {
|
||||||
assert false : "Illegal conversion " + this + " -> " + to;
|
assert false : "Illegal conversion " + this + " -> " + to;
|
||||||
}
|
}
|
||||||
@ -134,26 +135,26 @@ class LongType extends BitwiseType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type add(final MethodVisitor method) {
|
public Type add(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(LADD);
|
method.visitInvokeDynamicInsn("ladd", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||||
return LONG;
|
return LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type sub(final MethodVisitor method) {
|
public Type sub(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(LSUB);
|
method.visitInvokeDynamicInsn("lsub", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||||
return LONG;
|
return LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type mul(final MethodVisitor method) {
|
public Type mul(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(LMUL);
|
method.visitInvokeDynamicInsn("lmul", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||||
return LONG;
|
return LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type div(final MethodVisitor method) {
|
public Type div(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(LDIV);
|
method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||||
return LONG;
|
return LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +201,8 @@ class LongType extends BitwiseType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type neg(final MethodVisitor method) {
|
public Type neg(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(LNEG);
|
method.visitInvokeDynamicInsn("lneg", "(J)J", MATHBOOTSTRAP, programPoint);
|
||||||
return LONG;
|
return LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +213,13 @@ class LongType extends BitwiseType {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type loadUndefined(final MethodVisitor method) {
|
public Type loadUndefined(final MethodVisitor method) {
|
||||||
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_LONG);
|
method.visitLdcInsn(UNDEFINED_LONG);
|
||||||
|
return LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type loadForcedInitializer(MethodVisitor method) {
|
||||||
|
method.visitInsn(LCONST_0);
|
||||||
return LONG;
|
return LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +39,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
|
|||||||
import static jdk.internal.org.objectweb.asm.Opcodes.DSTORE;
|
import static jdk.internal.org.objectweb.asm.Opcodes.DSTORE;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.DSUB;
|
import static jdk.internal.org.objectweb.asm.Opcodes.DSUB;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
||||||
|
import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
|
||||||
import jdk.nashorn.internal.runtime.JSType;
|
import jdk.nashorn.internal.runtime.JSType;
|
||||||
|
|
||||||
class NumberType extends NumericType {
|
class NumberType extends NumericType {
|
||||||
@ -63,6 +63,11 @@ class NumberType extends NumericType {
|
|||||||
return Double.class;
|
return Double.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getBytecodeStackType() {
|
||||||
|
return 'D';
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
|
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
|
||||||
method.visitInsn(isCmpG ? DCMPG : DCMPL);
|
method.visitInsn(isCmpG ? DCMPG : DCMPL);
|
||||||
@ -84,7 +89,13 @@ class NumberType extends NumericType {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type loadUndefined(final MethodVisitor method) {
|
public Type loadUndefined(final MethodVisitor method) {
|
||||||
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_DOUBLE);
|
method.visitLdcInsn(UNDEFINED_DOUBLE);
|
||||||
|
return NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type loadForcedInitializer(MethodVisitor method) {
|
||||||
|
method.visitInsn(DCONST_0);
|
||||||
return NUMBER;
|
return NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,42 +123,42 @@ class NumberType extends NumericType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (to.isInteger()) {
|
if (to.isInteger()) {
|
||||||
invokeStatic(method, JSType.TO_INT32_D);
|
invokestatic(method, JSType.TO_INT32_D);
|
||||||
} else if (to.isLong()) {
|
} else if (to.isLong()) {
|
||||||
invokeStatic(method, JSType.TO_INT64_D);
|
invokestatic(method, JSType.TO_LONG_D);
|
||||||
} else if (to.isBoolean()) {
|
} else if (to.isBoolean()) {
|
||||||
invokeStatic(method, JSType.TO_BOOLEAN_D);
|
invokestatic(method, JSType.TO_BOOLEAN_D);
|
||||||
} else if (to.isString()) {
|
} else if (to.isString()) {
|
||||||
invokeStatic(method, JSType.TO_STRING_D);
|
invokestatic(method, JSType.TO_STRING_D);
|
||||||
} else if (to.isObject()) {
|
} else if (to.isObject()) {
|
||||||
invokeStatic(method, VALUE_OF);
|
invokestatic(method, VALUE_OF);
|
||||||
} else {
|
} else {
|
||||||
assert false : "Illegal conversion " + this + " -> " + to;
|
throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to);
|
||||||
}
|
}
|
||||||
|
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type add(final MethodVisitor method) {
|
public Type add(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(DADD);
|
method.visitInsn(DADD);
|
||||||
return NUMBER;
|
return NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type sub(final MethodVisitor method) {
|
public Type sub(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(DSUB);
|
method.visitInsn(DSUB);
|
||||||
return NUMBER;
|
return NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type mul(final MethodVisitor method) {
|
public Type mul(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(DMUL);
|
method.visitInsn(DMUL);
|
||||||
return NUMBER;
|
return NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type div(final MethodVisitor method) {
|
public Type div(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(DDIV);
|
method.visitInsn(DDIV);
|
||||||
return NUMBER;
|
return NUMBER;
|
||||||
}
|
}
|
||||||
@ -159,7 +170,7 @@ class NumberType extends NumericType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type neg(final MethodVisitor method) {
|
public Type neg(final MethodVisitor method, final int programPoint) {
|
||||||
method.visitInsn(DNEG);
|
method.visitInsn(DNEG);
|
||||||
return NUMBER;
|
return NUMBER;
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,13 @@ class ObjectType extends Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type add(final MethodVisitor method) {
|
public String getShortDescriptor() {
|
||||||
invokeStatic(method, ScriptRuntime.ADD);
|
return getTypeClass() == Object.class ? "Object" : getTypeClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type add(final MethodVisitor method, final int programPoint) {
|
||||||
|
invokestatic(method, ScriptRuntime.ADD);
|
||||||
return Type.OBJECT;
|
return Type.OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +79,7 @@ class ObjectType extends Type {
|
|||||||
public Type load(final MethodVisitor method, final int slot) {
|
public Type load(final MethodVisitor method, final int slot) {
|
||||||
assert slot != -1;
|
assert slot != -1;
|
||||||
method.visitVarInsn(ALOAD, slot);
|
method.visitVarInsn(ALOAD, slot);
|
||||||
return Type.OBJECT;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -86,13 +91,21 @@ class ObjectType extends Type {
|
|||||||
@Override
|
@Override
|
||||||
public Type loadUndefined(final MethodVisitor method) {
|
public Type loadUndefined(final MethodVisitor method) {
|
||||||
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "UNDEFINED", typeDescriptor(Undefined.class));
|
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "UNDEFINED", typeDescriptor(Undefined.class));
|
||||||
|
return UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type loadForcedInitializer(MethodVisitor method) {
|
||||||
|
method.visitInsn(ACONST_NULL);
|
||||||
|
// TODO: do we need a special type for null, e.g. Type.NULL? It should be assignable to any other object type
|
||||||
|
// without a checkast in convert.
|
||||||
return OBJECT;
|
return OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type loadEmpty(final MethodVisitor method) {
|
public Type loadEmpty(final MethodVisitor method) {
|
||||||
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "EMPTY", typeDescriptor(Undefined.class));
|
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "EMPTY", typeDescriptor(Undefined.class));
|
||||||
return OBJECT;
|
return UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -108,10 +121,10 @@ class ObjectType extends Type {
|
|||||||
method.visitLdcInsn(c);
|
method.visitLdcInsn(c);
|
||||||
return Type.typeFor(MethodHandle.class);
|
return Type.typeFor(MethodHandle.class);
|
||||||
} else {
|
} else {
|
||||||
assert false : "implementation missing for class " + c.getClass() + " value=" + c;
|
throw new UnsupportedOperationException("implementation missing for class " + c.getClass() + " value=" + c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OBJECT;
|
return Type.OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -138,6 +151,10 @@ class ObjectType extends Type {
|
|||||||
}
|
}
|
||||||
return to;
|
return to;
|
||||||
} else if (to.isObject()) {
|
} else if (to.isObject()) {
|
||||||
|
final Class<?> toClass = to.getTypeClass();
|
||||||
|
if(!toClass.isAssignableFrom(getTypeClass())) {
|
||||||
|
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(toClass));
|
||||||
|
}
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
} else if (isString()) {
|
} else if (isString()) {
|
||||||
@ -145,17 +162,17 @@ class ObjectType extends Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (to.isInteger()) {
|
if (to.isInteger()) {
|
||||||
invokeStatic(method, JSType.TO_INT32);
|
invokestatic(method, JSType.TO_INT32);
|
||||||
} else if (to.isNumber()) {
|
} else if (to.isNumber()) {
|
||||||
invokeStatic(method, JSType.TO_NUMBER);
|
invokestatic(method, JSType.TO_NUMBER);
|
||||||
} else if (to.isLong()) {
|
} else if (to.isLong()) {
|
||||||
invokeStatic(method, JSType.TO_INT64);
|
invokestatic(method, JSType.TO_LONG);
|
||||||
} else if (to.isBoolean()) {
|
} else if (to.isBoolean()) {
|
||||||
invokeStatic(method, JSType.TO_BOOLEAN);
|
invokestatic(method, JSType.TO_BOOLEAN);
|
||||||
} else if (to.isString()) {
|
} else if (to.isString()) {
|
||||||
invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING);
|
invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
|
||||||
} else {
|
} else {
|
||||||
assert false : "Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString;
|
throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return to;
|
return to;
|
||||||
@ -165,4 +182,9 @@ class ObjectType extends Type {
|
|||||||
public void _return(final MethodVisitor method) {
|
public void _return(final MethodVisitor method) {
|
||||||
method.visitInsn(ARETURN);
|
method.visitInsn(ARETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getBytecodeStackType() {
|
||||||
|
return 'A';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X1;
|
|||||||
import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X2;
|
import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X2;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X1;
|
import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X1;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X2;
|
import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X2;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
|
import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
|
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
|
||||||
@ -45,13 +46,20 @@ import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
|
|||||||
import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
|
import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
|
import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
|
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
|
||||||
|
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
||||||
|
|
||||||
|
import java.lang.invoke.CallSite;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import jdk.internal.org.objectweb.asm.Handle;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
|
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
|
||||||
|
import jdk.nashorn.internal.runtime.Undefined;
|
||||||
|
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the representation of a JavaScript type, disassociated from java
|
* This is the representation of a JavaScript type, disassociated from java
|
||||||
@ -97,6 +105,10 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
/** Set way below Integer.MAX_VALUE to prevent overflow when adding weights. Objects are still heaviest. */
|
/** Set way below Integer.MAX_VALUE to prevent overflow when adding weights. Objects are still heaviest. */
|
||||||
protected static final int MAX_WEIGHT = 20;
|
protected static final int MAX_WEIGHT = 20;
|
||||||
|
|
||||||
|
static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "mathBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class);
|
||||||
|
|
||||||
|
static final Handle MATHBOOTSTRAP = new Handle(H_INVOKESTATIC, BOOTSTRAP.className(), "mathBootstrap", BOOTSTRAP.descriptor());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -148,6 +160,17 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the character describing the bytecode type for this value on the stack or local variable, identical to
|
||||||
|
* what would be used as the prefix for a bytecode {@code LOAD} or {@code STORE} instruction, therefore it must be
|
||||||
|
* one of {@code A, F, D, I, L}. Also, the special value {@code U} is used for local variable slots that haven't
|
||||||
|
* been initialized yet (it can't appear for a value pushed to the operand stack, those always have known values).
|
||||||
|
* Note that while we allow all JVM internal types, Nashorn doesn't necessarily use them all - currently we don't
|
||||||
|
* have floats, only doubles, but that might change in the future.
|
||||||
|
* @return the character describing the bytecode type for this value on the stack.
|
||||||
|
*/
|
||||||
|
public abstract char getBytecodeStackType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a method descriptor given a return type and a param array
|
* Generate a method descriptor given a return type and a param array
|
||||||
*
|
*
|
||||||
@ -199,7 +222,11 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
case jdk.internal.org.objectweb.asm.Type.DOUBLE:
|
case jdk.internal.org.objectweb.asm.Type.DOUBLE:
|
||||||
return NUMBER;
|
return NUMBER;
|
||||||
case jdk.internal.org.objectweb.asm.Type.OBJECT:
|
case jdk.internal.org.objectweb.asm.Type.OBJECT:
|
||||||
return OBJECT;
|
try {
|
||||||
|
return Type.typeFor(Class.forName(itype.getClassName()));
|
||||||
|
} catch(ClassNotFoundException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
case jdk.internal.org.objectweb.asm.Type.VOID:
|
case jdk.internal.org.objectweb.asm.Type.VOID:
|
||||||
return null;
|
return null;
|
||||||
case jdk.internal.org.objectweb.asm.Type.ARRAY:
|
case jdk.internal.org.objectweb.asm.Type.ARRAY:
|
||||||
@ -260,7 +287,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
return jdk.internal.org.objectweb.asm.Type.getType(type);
|
return jdk.internal.org.objectweb.asm.Type.getType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void invokeStatic(final MethodVisitor method, final Call call) {
|
static void invokestatic(final MethodVisitor method, final Call call) {
|
||||||
method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor(), false);
|
method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,6 +399,14 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
return this instanceof ObjectType;
|
return this instanceof ObjectType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a primitive type (e.g int, long, double, boolean)
|
||||||
|
* @return true if primitive
|
||||||
|
*/
|
||||||
|
public boolean isPrimitive() {
|
||||||
|
return !isObject();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether a type is a STRING type
|
* Determines whether a type is a STRING type
|
||||||
*
|
*
|
||||||
@ -470,7 +505,25 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
* @return the widest type
|
* @return the widest type
|
||||||
*/
|
*/
|
||||||
public static Type narrowest(final Type type0, final Type type1) {
|
public static Type narrowest(final Type type0, final Type type1) {
|
||||||
return type0.weight() < type1.weight() ? type0 : type1;
|
return type0.narrowerThan(type1) ? type0 : type1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this type is strictly narrower than another one
|
||||||
|
* @param type type to check against
|
||||||
|
* @return true if this type is strictly narrower
|
||||||
|
*/
|
||||||
|
public boolean narrowerThan(final Type type) {
|
||||||
|
return weight() < type.weight();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this type is strictly wider than another one
|
||||||
|
* @param type type to check against
|
||||||
|
* @return true if this type is strictly wider
|
||||||
|
*/
|
||||||
|
public boolean widerThan(final Type type) {
|
||||||
|
return weight() > type.weight();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -549,6 +602,16 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the descriptor of a type, short version
|
||||||
|
* Used mainly for debugging purposes
|
||||||
|
*
|
||||||
|
* @return the short descriptor
|
||||||
|
*/
|
||||||
|
public String getShortDescriptor() {
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
@ -710,6 +773,11 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
*/
|
*/
|
||||||
public static final Type OBJECT = putInCache(new ObjectType());
|
public static final Type OBJECT = putInCache(new ObjectType());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A undefined singleton
|
||||||
|
*/
|
||||||
|
public static final Type UNDEFINED = putInCache(new ObjectType(Undefined.class));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the singleton for integer arrays
|
* This is the singleton for integer arrays
|
||||||
*/
|
*/
|
||||||
@ -820,55 +888,83 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
|||||||
// EMPTY - used as a class that is absolutely not compatible with a type to represent "unknown"
|
// EMPTY - used as a class that is absolutely not compatible with a type to represent "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private abstract static class ValueLessType extends Type {
|
||||||
|
|
||||||
|
ValueLessType(final String name) {
|
||||||
|
super(name, Unknown.class, MIN_WEIGHT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type load(final MethodVisitor method, final int slot) {
|
||||||
|
throw new UnsupportedOperationException("load " + slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(final MethodVisitor method, final int slot) {
|
||||||
|
throw new UnsupportedOperationException("store " + slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type ldc(final MethodVisitor method, final Object c) {
|
||||||
|
throw new UnsupportedOperationException("ldc " + c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type loadUndefined(final MethodVisitor method) {
|
||||||
|
throw new UnsupportedOperationException("load undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type loadForcedInitializer(final MethodVisitor method) {
|
||||||
|
throw new UnsupportedOperationException("load forced initializer");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type convert(final MethodVisitor method, final Type to) {
|
||||||
|
throw new UnsupportedOperationException("convert => " + to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void _return(final MethodVisitor method) {
|
||||||
|
throw new UnsupportedOperationException("return");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type add(final MethodVisitor method, final int programPoint) {
|
||||||
|
throw new UnsupportedOperationException("add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the unknown type which is used as initial type for type
|
* This is the unknown type which is used as initial type for type
|
||||||
* inference. It has the minimum type width
|
* inference. It has the minimum type width
|
||||||
*/
|
*/
|
||||||
public static final Type UNKNOWN = new Type("<unknown>", Unknown.class, MIN_WEIGHT, 1) {
|
public static final Type UNKNOWN = new ValueLessType("<unknown>") {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescriptor() {
|
public String getDescriptor() {
|
||||||
return "<unknown>";
|
return "<unknown>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type load(final MethodVisitor method, final int slot) {
|
public char getBytecodeStackType() {
|
||||||
assert false : "unsupported operation";
|
return 'U';
|
||||||
return null;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the unknown type which is used as initial type for type
|
||||||
|
* inference. It has the minimum type width
|
||||||
|
*/
|
||||||
|
public static final Type SLOT_2 = new ValueLessType("<slot_2>") {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptor() {
|
||||||
|
return "<slot_2>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(final MethodVisitor method, final int slot) {
|
public char getBytecodeStackType() {
|
||||||
assert false : "unsupported operation";
|
throw new UnsupportedOperationException("getBytecodeStackType");
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type ldc(final MethodVisitor method, final Object c) {
|
|
||||||
assert false : "unsupported operation";
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type loadUndefined(final MethodVisitor method) {
|
|
||||||
assert false : "unsupported operation";
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type convert(final MethodVisitor method, final Type to) {
|
|
||||||
assert false : "unsupported operation";
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void _return(final MethodVisitor method) {
|
|
||||||
assert false : "unsupported operation";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type add(final MethodVisitor method) {
|
|
||||||
assert false : "unsupported operation";
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.ir;
|
package jdk.nashorn.internal.ir;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||||
|
|
||||||
|
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||||
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
|
|
||||||
@ -49,8 +53,8 @@ public final class AccessNode extends BaseNode {
|
|||||||
this.property = property.setIsPropertyName();
|
this.property = property.setIsPropertyName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction) {
|
private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction, final Type optimisticType, final boolean isOptimistic, final int id) {
|
||||||
super(accessNode, base, isFunction);
|
super(accessNode, base, isFunction, optimisticType, isOptimistic, id);
|
||||||
this.property = property;
|
this.property = property;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +76,8 @@ public final class AccessNode extends BaseNode {
|
|||||||
public void toString(final StringBuilder sb) {
|
public void toString(final StringBuilder sb) {
|
||||||
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
|
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
|
||||||
|
|
||||||
|
Node.optimisticType(this, sb);
|
||||||
|
|
||||||
if (needsParen) {
|
if (needsParen) {
|
||||||
sb.append('(');
|
sb.append('(');
|
||||||
}
|
}
|
||||||
@ -99,22 +105,49 @@ public final class AccessNode extends BaseNode {
|
|||||||
if (this.base == base) {
|
if (this.base == base) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new AccessNode(this, base, property, isFunction());
|
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccessNode setProperty(final IdentNode property) {
|
private AccessNode setProperty(final IdentNode property) {
|
||||||
if (this.property == property) {
|
if (this.property == property) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new AccessNode(this, base, property, isFunction());
|
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseNode setIsFunction() {
|
public AccessNode setType(final TemporarySymbols ts, final Type optimisticType) {
|
||||||
|
if (this.optimisticType == optimisticType) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), optimisticType)) {
|
||||||
|
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", optimisticType, " instead of ", getType());
|
||||||
|
}
|
||||||
|
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessNode setProgramPoint(int programPoint) {
|
||||||
|
if (this.programPoint == programPoint) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessNode setIsFunction() {
|
||||||
if (isFunction()) {
|
if (isFunction()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new AccessNode(this, base, property, true);
|
return new AccessNode(this, base, property, true, optimisticType, isOptimistic, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessNode setIsOptimistic(final boolean isOptimistic) {
|
||||||
|
if (this.isOptimistic == isOptimistic) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.ir;
|
package jdk.nashorn.internal.ir;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||||
|
|
||||||
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,13 +37,22 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
|
|||||||
* @see IndexNode
|
* @see IndexNode
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public abstract class BaseNode extends Expression implements FunctionCall {
|
public abstract class BaseNode extends Expression implements FunctionCall, Optimistic {
|
||||||
|
|
||||||
/** Base Node. */
|
/** Base Node. */
|
||||||
protected final Expression base;
|
protected final Expression base;
|
||||||
|
|
||||||
private final boolean isFunction;
|
private final boolean isFunction;
|
||||||
|
|
||||||
|
/** Callsite type for this node, if overriden optimistically or conservatively depending on coercion */
|
||||||
|
protected final Type optimisticType;
|
||||||
|
|
||||||
|
/** Does this node have a callsite type, and it is optimistic rather than inferred from coercion semantics */
|
||||||
|
protected final boolean isOptimistic;
|
||||||
|
|
||||||
|
/** Program point id */
|
||||||
|
protected final int programPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -51,8 +63,11 @@ public abstract class BaseNode extends Expression implements FunctionCall {
|
|||||||
*/
|
*/
|
||||||
public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction) {
|
public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction) {
|
||||||
super(token, base.getStart(), finish);
|
super(token, base.getStart(), finish);
|
||||||
this.base = base;
|
this.base = base;
|
||||||
this.isFunction = isFunction;
|
this.isFunction = isFunction;
|
||||||
|
this.optimisticType = null;
|
||||||
|
this.programPoint = INVALID_PROGRAM_POINT;
|
||||||
|
this.isOptimistic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,11 +75,17 @@ public abstract class BaseNode extends Expression implements FunctionCall {
|
|||||||
* @param baseNode node to inherit from
|
* @param baseNode node to inherit from
|
||||||
* @param base base
|
* @param base base
|
||||||
* @param isFunction is this a function
|
* @param isFunction is this a function
|
||||||
|
* @param callSiteType the callsite type for this base node, either optimistic or conservative
|
||||||
|
* @param isOptimistic is the callsite type optimistic rather than based on statically known coercion semantics
|
||||||
|
* @param programPoint program point id
|
||||||
*/
|
*/
|
||||||
protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction) {
|
protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final Type callSiteType, final boolean isOptimistic, final int programPoint) {
|
||||||
super(baseNode);
|
super(baseNode);
|
||||||
this.base = base;
|
this.base = base;
|
||||||
this.isFunction = isFunction;
|
this.isFunction = isFunction;
|
||||||
|
this.optimisticType = callSiteType;
|
||||||
|
this.programPoint = programPoint;
|
||||||
|
this.isOptimistic = isOptimistic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,6 +101,37 @@ public abstract class BaseNode extends Expression implements FunctionCall {
|
|||||||
return isFunction;
|
return isFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Type getType() {
|
||||||
|
return optimisticType == null ? super.getType() : optimisticType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProgramPoint() {
|
||||||
|
return programPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostOptimisticType() {
|
||||||
|
return Type.INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostPessimisticType() {
|
||||||
|
return Type.OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeOptimistic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOptimistic() {
|
||||||
|
return isOptimistic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark this node as being the callee operand of a {@link CallNode}.
|
* 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.
|
* @return a base node identical to this one in all aspects except with its function flag set.
|
||||||
|
@ -25,7 +25,13 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.ir;
|
package jdk.nashorn.internal.ir;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
|
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
import jdk.nashorn.internal.parser.TokenType;
|
import jdk.nashorn.internal.parser.TokenType;
|
||||||
@ -34,12 +40,34 @@ import jdk.nashorn.internal.parser.TokenType;
|
|||||||
* BinaryNode nodes represent two operand operations.
|
* BinaryNode nodes represent two operand operations.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public final class BinaryNode extends Expression implements Assignment<Expression> {
|
public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic {
|
||||||
/** Left hand side argument. */
|
/** Left hand side argument. */
|
||||||
private final Expression lhs;
|
private final Expression lhs;
|
||||||
|
|
||||||
private final Expression rhs;
|
private final Expression rhs;
|
||||||
|
|
||||||
|
private final int programPoint;
|
||||||
|
|
||||||
|
private final boolean isOptimistic;
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
private static final List<TokenType> CAN_OVERFLOW =
|
||||||
|
Collections.unmodifiableList(
|
||||||
|
Arrays.asList(new TokenType[] {
|
||||||
|
TokenType.ADD,
|
||||||
|
TokenType.DIV,
|
||||||
|
TokenType.MOD,
|
||||||
|
TokenType.MUL,
|
||||||
|
TokenType.SUB,
|
||||||
|
TokenType.ASSIGN_ADD,
|
||||||
|
TokenType.ASSIGN_DIV,
|
||||||
|
TokenType.ASSIGN_MOD,
|
||||||
|
TokenType.ASSIGN_MUL,
|
||||||
|
TokenType.ASSIGN_SUB
|
||||||
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -51,12 +79,18 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
|||||||
super(token, lhs.getStart(), rhs.getFinish());
|
super(token, lhs.getStart(), rhs.getFinish());
|
||||||
this.lhs = lhs;
|
this.lhs = lhs;
|
||||||
this.rhs = rhs;
|
this.rhs = rhs;
|
||||||
|
this.programPoint = INVALID_PROGRAM_POINT;
|
||||||
|
this.isOptimistic = false;
|
||||||
|
this.type = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs) {
|
private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint, final boolean isOptimistic) {
|
||||||
super(binaryNode);
|
super(binaryNode);
|
||||||
this.lhs = lhs;
|
this.lhs = lhs;
|
||||||
this.rhs = rhs;
|
this.rhs = rhs;
|
||||||
|
this.programPoint = programPoint;
|
||||||
|
this.isOptimistic = isOptimistic;
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -109,6 +143,9 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
|||||||
case ASSIGN_SUB:
|
case ASSIGN_SUB:
|
||||||
return Type.NUMBER;
|
return Type.NUMBER;
|
||||||
default:
|
default:
|
||||||
|
if (isComparison()) {
|
||||||
|
return Type.BOOLEAN;
|
||||||
|
}
|
||||||
return Type.OBJECT;
|
return Type.OBJECT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,10 +247,10 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toString(final StringBuilder sb) {
|
public void toString(final StringBuilder sb) {
|
||||||
final TokenType type = tokenType();
|
final TokenType tokenType = tokenType();
|
||||||
|
|
||||||
final boolean lhsParen = type.needsParens(lhs().tokenType(), true);
|
final boolean lhsParen = tokenType.needsParens(lhs().tokenType(), true);
|
||||||
final boolean rhsParen = type.needsParens(rhs().tokenType(), false);
|
final boolean rhsParen = tokenType.needsParens(rhs().tokenType(), false);
|
||||||
|
|
||||||
if (lhsParen) {
|
if (lhsParen) {
|
||||||
sb.append('(');
|
sb.append('(');
|
||||||
@ -227,7 +264,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
|||||||
|
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
|
|
||||||
switch (type) {
|
switch (tokenType) {
|
||||||
case COMMALEFT:
|
case COMMALEFT:
|
||||||
sb.append(",<");
|
sb.append(",<");
|
||||||
break;
|
break;
|
||||||
@ -239,10 +276,14 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
|||||||
sb.append("++");
|
sb.append("++");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sb.append(type.getName());
|
sb.append(tokenType.getName());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isOptimistic()) {
|
||||||
|
sb.append(Node.OPT_IDENTIFIER);
|
||||||
|
}
|
||||||
|
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
|
|
||||||
if (rhsParen) {
|
if (rhsParen) {
|
||||||
@ -279,7 +320,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
|||||||
if (this.lhs == lhs) {
|
if (this.lhs == lhs) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new BinaryNode(this, lhs, rhs);
|
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -291,7 +332,64 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
|||||||
if (this.rhs == rhs) {
|
if (this.rhs == rhs) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new BinaryNode(this, lhs, rhs);
|
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProgramPoint() {
|
||||||
|
return programPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeOptimistic() {
|
||||||
|
return getMostOptimisticType() != getMostPessimisticType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BinaryNode setProgramPoint(final int programPoint) {
|
||||||
|
if (this.programPoint == programPoint) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BinaryNode setIsOptimistic(final boolean isOptimistic) {
|
||||||
|
if (this.isOptimistic == isOptimistic) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
assert isOptimistic;
|
||||||
|
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostOptimisticType() {
|
||||||
|
if (CAN_OVERFLOW.contains(tokenType())) {
|
||||||
|
return Type.widest(Type.INT, Type.widest(lhs.getType(), rhs.getType()));
|
||||||
|
}
|
||||||
|
return getMostPessimisticType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostPessimisticType() {
|
||||||
|
return getWidestOperationType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOptimistic() {
|
||||||
|
return isOptimistic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return type == null ? super.getType() : type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BinaryNode setType(TemporarySymbols ts, Type type) {
|
||||||
|
if (this.type == type) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.ir;
|
package jdk.nashorn.internal.ir;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -33,14 +35,11 @@ import java.util.Comparator;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import jdk.nashorn.internal.codegen.Label;
|
import jdk.nashorn.internal.codegen.Label;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
|
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IR representation for a list of statements.
|
* IR representation for a list of statements.
|
||||||
*/
|
*/
|
||||||
@ -53,7 +52,7 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
|
|||||||
protected final Map<String, Symbol> symbols;
|
protected final Map<String, Symbol> symbols;
|
||||||
|
|
||||||
/** Entry label. */
|
/** Entry label. */
|
||||||
protected final Label entryLabel;
|
private final Label entryLabel;
|
||||||
|
|
||||||
/** Break label. */
|
/** Break label. */
|
||||||
private final Label breakLabel;
|
private final Label breakLabel;
|
||||||
@ -65,13 +64,11 @@ public class Block extends Node implements BreakableNode, Flags<Block> {
|
|||||||
public static final int NEEDS_SCOPE = 1 << 0;
|
public static final int NEEDS_SCOPE = 1 << 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag indicating whether this block needs
|
* Flag indicating whether this block uses the self symbol for the function. This is used only for blocks that are
|
||||||
* self symbol assignment at the start. This is used only for
|
* bodies of function nodes who refer to themselves by name. It causes Attr to insert a var [fn_name] = __callee__
|
||||||
* blocks that are the bodies of function nodes who refer to themselves
|
|
||||||
* by name. It causes codegen to insert a var [fn_name] = __callee__
|
|
||||||
* at the start of the body
|
* at the start of the body
|
||||||
*/
|
*/
|
||||||
public static final int NEEDS_SELF_SYMBOL = 1 << 1;
|
public static final int USES_SELF_SYMBOL = 1 << 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this block tagged as terminal based on its contents
|
* Is this block tagged as terminal based on its contents
|
||||||
|
@ -58,7 +58,7 @@ public class BlockStatement extends Statement {
|
|||||||
* @return a block statement with the new statements. It will have the line number, token, and finish of the
|
* @return a block statement with the new statements. It will have the line number, token, and finish of the
|
||||||
* original statement.
|
* original statement.
|
||||||
*/
|
*/
|
||||||
public static Statement createReplacement(final Statement stmt, final List<Statement> newStmts) {
|
public static BlockStatement createReplacement(final Statement stmt, final List<Statement> newStmts) {
|
||||||
return createReplacement(stmt, stmt.getFinish(), newStmts);
|
return createReplacement(stmt, stmt.getFinish(), newStmts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ public class BlockStatement extends Statement {
|
|||||||
* @return a block statement with the new statements. It will have the line number, and token of the
|
* @return a block statement with the new statements. It will have the line number, and token of the
|
||||||
* original statement.
|
* original statement.
|
||||||
*/
|
*/
|
||||||
public static Statement createReplacement(final Statement stmt, int finish, final List<Statement> newStmts) {
|
public static BlockStatement createReplacement(final Statement stmt, int finish, final List<Statement> newStmts) {
|
||||||
return new BlockStatement(stmt.getLineNumber(), new Block(stmt.getToken(), finish, newStmts));
|
return new BlockStatement(stmt.getLineNumber(), new Block(stmt.getToken(), finish, newStmts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,11 +32,13 @@ import jdk.nashorn.internal.ir.annotations.Ignore;
|
|||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IR representation for a function call.
|
* IR representation for a function call.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public final class CallNode extends LexicalContextExpression {
|
public final class CallNode extends LexicalContextExpression implements Optimistic {
|
||||||
|
|
||||||
/** Function identifier or function body. */
|
/** Function identifier or function body. */
|
||||||
private final Expression function;
|
private final Expression function;
|
||||||
@ -45,12 +47,19 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
private final List<Expression> args;
|
private final List<Expression> args;
|
||||||
|
|
||||||
/** Is this a "new" operation */
|
/** Is this a "new" operation */
|
||||||
public static final int IS_NEW = 0x1;
|
public static final int IS_NEW = 1 << 0;
|
||||||
|
|
||||||
|
/** Is the callsite type for this call optimistic rather than based on statically known coercion semantics */
|
||||||
|
public static final int OPTIMISTIC = 1 << 1;
|
||||||
|
|
||||||
private final int flags;
|
private final int flags;
|
||||||
|
|
||||||
private final int lineNumber;
|
private final int lineNumber;
|
||||||
|
|
||||||
|
private final int programPoint;
|
||||||
|
|
||||||
|
private final Type optimisticType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments to be passed to builtin {@code eval} function
|
* Arguments to be passed to builtin {@code eval} function
|
||||||
*/
|
*/
|
||||||
@ -145,20 +154,24 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args) {
|
public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args) {
|
||||||
super(token, finish);
|
super(token, finish);
|
||||||
|
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.flags = 0;
|
this.flags = 0;
|
||||||
this.evalArgs = null;
|
this.evalArgs = null;
|
||||||
this.lineNumber = lineNumber;
|
this.lineNumber = lineNumber;
|
||||||
|
this.programPoint = INVALID_PROGRAM_POINT;
|
||||||
|
this.optimisticType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final EvalArgs evalArgs) {
|
private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type optimisticType, final EvalArgs evalArgs, final int programPoint) {
|
||||||
super(callNode);
|
super(callNode);
|
||||||
this.lineNumber = callNode.lineNumber;
|
this.lineNumber = callNode.lineNumber;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
this.evalArgs = evalArgs;
|
this.evalArgs = evalArgs;
|
||||||
|
this.programPoint = programPoint;
|
||||||
|
this.optimisticType = optimisticType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,7 +184,15 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT;
|
return optimisticType == null ? super.getType() : optimisticType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optimistic setType(TemporarySymbols ts, Type optimisticType) {
|
||||||
|
if (this.optimisticType == optimisticType) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,7 +208,6 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
|
final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
|
||||||
setFunction((Expression)function.accept(visitor)).
|
setFunction((Expression)function.accept(visitor)).
|
||||||
setArgs(Node.accept(visitor, Expression.class, args)).
|
setArgs(Node.accept(visitor, Expression.class, args)).
|
||||||
setFlags(flags).
|
|
||||||
setEvalArgs(evalArgs == null ?
|
setEvalArgs(evalArgs == null ?
|
||||||
null :
|
null :
|
||||||
evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)).
|
evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)).
|
||||||
@ -204,6 +224,7 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toString(final StringBuilder sb) {
|
public void toString(final StringBuilder sb) {
|
||||||
|
Node.optimisticType(this, sb);
|
||||||
function.toString(sb);
|
function.toString(sb);
|
||||||
|
|
||||||
sb.append('(');
|
sb.append('(');
|
||||||
@ -239,7 +260,7 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
if (this.args == args) {
|
if (this.args == args) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new CallNode(this, function, args, flags, evalArgs);
|
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,7 +282,7 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
if (this.evalArgs == evalArgs) {
|
if (this.evalArgs == evalArgs) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new CallNode(this, function, args, flags, evalArgs);
|
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -289,7 +310,7 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
if (this.function == function) {
|
if (this.function == function) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new CallNode(this, function, args, flags, evalArgs);
|
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -312,6 +333,47 @@ public final class CallNode extends LexicalContextExpression {
|
|||||||
if (this.flags == flags) {
|
if (this.flags == flags) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new CallNode(this, function, args, flags, evalArgs);
|
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProgramPoint() {
|
||||||
|
return programPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallNode setProgramPoint(final int programPoint) {
|
||||||
|
if (this.programPoint == programPoint) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostOptimisticType() {
|
||||||
|
return Type.INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostPessimisticType() {
|
||||||
|
return Type.OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeOptimistic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOptimistic() {
|
||||||
|
return (flags & OPTIMISTIC) == OPTIMISTIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optimistic setIsOptimistic(final boolean isOptimistic) {
|
||||||
|
if (isOptimistic() == isOptimistic) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new CallNode(this, function, args, isOptimistic ? (flags | OPTIMISTIC) : (flags & ~OPTIMISTIC), optimisticType, evalArgs, programPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ public final class CatchNode extends Statement {
|
|||||||
*/
|
*/
|
||||||
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Expression exceptionCondition, final Block body, final int flags) {
|
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Expression exceptionCondition, final Block body, final int flags) {
|
||||||
super(lineNumber, token, finish);
|
super(lineNumber, token, finish);
|
||||||
this.exception = exception;
|
this.exception = exception == null ? null : exception.setIsInitializedHere();
|
||||||
this.exceptionCondition = exceptionCondition;
|
this.exceptionCondition = exceptionCondition;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
|
@ -35,15 +35,15 @@ import jdk.nashorn.internal.codegen.types.Type;
|
|||||||
public abstract class Expression extends Node {
|
public abstract class Expression extends Node {
|
||||||
private Symbol symbol;
|
private Symbol symbol;
|
||||||
|
|
||||||
Expression(long token, int start, int finish) {
|
Expression(final long token, final int start, final int finish) {
|
||||||
super(token, start, finish);
|
super(token, start, finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression(long token, int finish) {
|
Expression(final long token, final int finish) {
|
||||||
super(token, finish);
|
super(token, finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression(Expression expr) {
|
Expression(final Expression expr) {
|
||||||
super(expr);
|
super(expr);
|
||||||
this.symbol = expr.symbol;
|
this.symbol = expr.symbol;
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,18 @@
|
|||||||
package jdk.nashorn.internal.ir;
|
package jdk.nashorn.internal.ir;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface used by AccessNodes, IndexNodes and IdentNodes to signal
|
* Interface used by AccessNodes, IndexNodes and IdentNodes to signal that when evaluated, their value will be treated
|
||||||
* that they are function calls
|
* as a function and immediately invoked, e.g. {@code foo()}, {@code foo.bar()} or {@code foo[bar]()}. Used to customize
|
||||||
|
* the priority of composite dynamic operations when emitting {@code INVOKEDYNAMIC} instructions that implement them,
|
||||||
|
* namely prioritize {@code getMethod} over {@code getElem} or {@code getProp}. An access or ident node with isFunction
|
||||||
|
* set to true will be emitted as {@code dyn:getMethod|getProp|getElem} while one with it set to false will be emitted
|
||||||
|
* as {@code dyn:getProp|getElem|getMethod}. Similarly, an index node with isFunction set to true will be emitted as
|
||||||
|
* {@code dyn:getMethod|getElem|getProp} while the one set to false will be emitted as {@code dyn:getElem|getProp|getMethod}.
|
||||||
*/
|
*/
|
||||||
public interface FunctionCall {
|
public interface FunctionCall {
|
||||||
/**
|
/**
|
||||||
* Return true if this function call implementor is a function
|
* Returns true if the value of this expression will be treated as a function and immediately invoked.
|
||||||
*
|
* @return true if the value of this expression will be treated as a function and immediately invoked.
|
||||||
* @return true if implements a function call
|
|
||||||
*/
|
*/
|
||||||
public boolean isFunction();
|
public boolean isFunction();
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -89,14 +90,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
/** Source of entity. */
|
/** Source of entity. */
|
||||||
private final Source source;
|
private final Source source;
|
||||||
|
|
||||||
|
/** Unique ID used for recompilation among other things */
|
||||||
|
private final int id;
|
||||||
|
|
||||||
/** External function identifier. */
|
/** External function identifier. */
|
||||||
@Ignore
|
@Ignore
|
||||||
private final IdentNode ident;
|
private final IdentNode ident;
|
||||||
|
|
||||||
/** Parsed version of functionNode */
|
|
||||||
@Ignore
|
|
||||||
private final FunctionNode snapshot;
|
|
||||||
|
|
||||||
/** The body of the function node */
|
/** The body of the function node */
|
||||||
private final Block body;
|
private final Block body;
|
||||||
|
|
||||||
@ -129,9 +129,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
@Ignore
|
@Ignore
|
||||||
private final EnumSet<CompilationState> compilationState;
|
private final EnumSet<CompilationState> compilationState;
|
||||||
|
|
||||||
@Ignore
|
|
||||||
private final Compiler.Hints hints;
|
|
||||||
|
|
||||||
/** Properties of this object assigned in this function */
|
/** Properties of this object assigned in this function */
|
||||||
@Ignore
|
@Ignore
|
||||||
private HashSet<String> thisProperties;
|
private HashSet<String> thisProperties;
|
||||||
@ -156,7 +153,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
/** Does the function use the "arguments" identifier ? */
|
/** Does the function use the "arguments" identifier ? */
|
||||||
public static final int USES_ARGUMENTS = 1 << 3;
|
public static final int USES_ARGUMENTS = 1 << 3;
|
||||||
|
|
||||||
/** Has this node been split because it was too large? */
|
/** Has this function been split because it was too large? */
|
||||||
public static final int IS_SPLIT = 1 << 4;
|
public static final int IS_SPLIT = 1 << 4;
|
||||||
|
|
||||||
/** Does the function call eval? If it does, then all variables in this function might be get/set by it and it can
|
/** Does the function call eval? If it does, then all variables in this function might be get/set by it and it can
|
||||||
@ -164,11 +161,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
public static final int HAS_EVAL = 1 << 5;
|
public static final int HAS_EVAL = 1 << 5;
|
||||||
|
|
||||||
/** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */
|
/** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */
|
||||||
public static final int HAS_NESTED_EVAL = 1 << 6;
|
public static final int HAS_NESTED_EVAL = 1 << 6;
|
||||||
|
|
||||||
/** Does this function have any blocks that create a scope? This is used to determine if the function needs to
|
/** Does this function have any blocks that create a scope? This is used to determine if the function needs to
|
||||||
* have a local variable slot for the scope symbol. */
|
* have a local variable slot for the scope symbol. */
|
||||||
public static final int HAS_SCOPE_BLOCK = 1 << 7;
|
public static final int HAS_SCOPE_BLOCK = 1 << 7;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
|
* Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
|
||||||
@ -182,33 +179,43 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
/** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
|
/** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
|
||||||
public static final int USES_ANCESTOR_SCOPE = 1 << 9;
|
public static final int USES_ANCESTOR_SCOPE = 1 << 9;
|
||||||
|
|
||||||
/** Is this function lazily compiled? */
|
|
||||||
public static final int IS_LAZY = 1 << 10;
|
|
||||||
|
|
||||||
/** Does this function have lazy, yet uncompiled children */
|
|
||||||
public static final int HAS_LAZY_CHILDREN = 1 << 11;
|
|
||||||
|
|
||||||
/** Does this function have lazy, yet uncompiled children */
|
|
||||||
public static final int IS_PROGRAM = 1 << 12;
|
|
||||||
|
|
||||||
/** Does this function have nested declarations? */
|
/** Does this function have nested declarations? */
|
||||||
public static final int HAS_FUNCTION_DECLARATIONS = 1 << 13;
|
public static final int HAS_FUNCTION_DECLARATIONS = 1 << 10;
|
||||||
|
|
||||||
/** Can this function be specialized? */
|
/** Can this function be specialized? */
|
||||||
public static final int CAN_SPECIALIZE = 1 << 14;
|
public static final int CAN_SPECIALIZE = 1 << 11;
|
||||||
|
|
||||||
|
/** Does this function have optimistic expressions? */
|
||||||
|
public static final int IS_OPTIMISTIC = 1 << 12;
|
||||||
|
|
||||||
|
/** Does this function explicitly use the {@link CompilerConstants#RETURN} symbol? Some functions are known to
|
||||||
|
* always use the return symbol, namely a function that is a program (as it must track its last executed expression
|
||||||
|
* statement's value) as well as functions that are split (to communicate return values from inner to outer
|
||||||
|
* partitions). Other functions normally don't use the return symbol (so we optimize away its slot), except in some
|
||||||
|
* very special cases, e.g. when containing a return statement in a finally block. These special cases set this
|
||||||
|
* flag. */
|
||||||
|
public static final int USES_RETURN_SYMBOL = 1 << 13;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this function the top-level program?
|
||||||
|
*/
|
||||||
|
public static final int IS_PROGRAM = 1 << 14;
|
||||||
|
|
||||||
/** Does this function or any nested functions contain an eval? */
|
/** Does this function or any nested functions contain an eval? */
|
||||||
private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
|
private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
|
||||||
|
|
||||||
/** Does this function need to store all its variables in scope? */
|
/** Does this function need to store all its variables in scope? */
|
||||||
private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
|
private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_EVAL;
|
||||||
|
|
||||||
/** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
|
/** 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;
|
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 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 eval.
|
||||||
* We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
|
* 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_EVAL | HAS_LAZY_CHILDREN;
|
private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL;
|
||||||
|
|
||||||
|
/** Where to start assigning global and unique function node ids */
|
||||||
|
public static final int FIRST_FUNCTION_ID = 1;
|
||||||
|
|
||||||
/** What is the return type of this function? */
|
/** What is the return type of this function? */
|
||||||
private Type returnType = Type.UNKNOWN;
|
private Type returnType = Type.UNKNOWN;
|
||||||
@ -217,6 +224,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param source the source
|
* @param source the source
|
||||||
|
* @param id unique id
|
||||||
* @param lineNumber line number
|
* @param lineNumber line number
|
||||||
* @param token token
|
* @param token token
|
||||||
* @param finish finish
|
* @param finish finish
|
||||||
@ -231,6 +239,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
*/
|
*/
|
||||||
public FunctionNode(
|
public FunctionNode(
|
||||||
final Source source,
|
final Source source,
|
||||||
|
final int id,
|
||||||
final int lineNumber,
|
final int lineNumber,
|
||||||
final long token,
|
final long token,
|
||||||
final int finish,
|
final int finish,
|
||||||
@ -245,6 +254,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
super(token, finish);
|
super(token, finish);
|
||||||
|
|
||||||
this.source = source;
|
this.source = source;
|
||||||
|
this.id = id;
|
||||||
this.lineNumber = lineNumber;
|
this.lineNumber = lineNumber;
|
||||||
this.ident = ident;
|
this.ident = ident;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -259,23 +269,19 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
this.sourceURL = sourceURL;
|
this.sourceURL = sourceURL;
|
||||||
this.compileUnit = null;
|
this.compileUnit = null;
|
||||||
this.body = null;
|
this.body = null;
|
||||||
this.snapshot = null;
|
|
||||||
this.hints = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private FunctionNode(
|
private FunctionNode(
|
||||||
final FunctionNode functionNode,
|
final FunctionNode functionNode,
|
||||||
final long lastToken,
|
final long lastToken,
|
||||||
final int flags,
|
final int flags,
|
||||||
final String sourceURL,
|
String sourceURL,
|
||||||
final String name,
|
final String name,
|
||||||
final Type returnType,
|
final Type returnType,
|
||||||
final CompileUnit compileUnit,
|
final CompileUnit compileUnit,
|
||||||
final EnumSet<CompilationState> compilationState,
|
final EnumSet<CompilationState> compilationState,
|
||||||
final Block body,
|
final Block body,
|
||||||
final List<IdentNode> parameters,
|
final List<IdentNode> parameters) {
|
||||||
final FunctionNode snapshot,
|
|
||||||
final Compiler.Hints hints) {
|
|
||||||
super(functionNode);
|
super(functionNode);
|
||||||
this.lineNumber = functionNode.lineNumber;
|
this.lineNumber = functionNode.lineNumber;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
@ -287,11 +293,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
this.compilationState = compilationState;
|
this.compilationState = compilationState;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
this.snapshot = snapshot;
|
|
||||||
this.hints = hints;
|
|
||||||
|
|
||||||
// the fields below never change - they are final and assigned in constructor
|
// the fields below never change - they are final and assigned in constructor
|
||||||
this.source = functionNode.source;
|
this.source = functionNode.source;
|
||||||
|
this.id = functionNode.id;
|
||||||
this.ident = functionNode.ident;
|
this.ident = functionNode.ident;
|
||||||
this.namespace = functionNode.namespace;
|
this.namespace = functionNode.namespace;
|
||||||
this.declaredSymbols = functionNode.declaredSymbols;
|
this.declaredSymbols = functionNode.declaredSymbols;
|
||||||
@ -316,6 +321,14 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unique ID for this function
|
||||||
|
* @return the id
|
||||||
|
*/
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get source name - sourceURL or name derived from Source.
|
* get source name - sourceURL or name derived from Source.
|
||||||
*
|
*
|
||||||
@ -345,7 +358,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -356,51 +369,12 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
return lineNumber;
|
return lineNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the version of this function node's code as it looked upon construction
|
|
||||||
* i.e typically parsed and nothing else
|
|
||||||
* @return initial version of function node
|
|
||||||
*/
|
|
||||||
public FunctionNode getSnapshot() {
|
|
||||||
return snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throw away the snapshot, if any, to save memory. Used when heuristic
|
|
||||||
* determines that a method is not worth specializing
|
|
||||||
*
|
|
||||||
* @param lc lexical context
|
|
||||||
* @return new function node if a snapshot was present, now with snapsnot null
|
|
||||||
*/
|
|
||||||
public FunctionNode clearSnapshot(final LexicalContext lc) {
|
|
||||||
if (this.snapshot == null) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Take a snapshot of this function node at a given point in time
|
|
||||||
* and store it in the function node
|
|
||||||
* @param lc lexical context
|
|
||||||
* @return function node
|
|
||||||
*/
|
|
||||||
public FunctionNode snapshot(final LexicalContext lc) {
|
|
||||||
if (this.snapshot == this) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
if (isProgram() || parameters.isEmpty()) {
|
|
||||||
return this; //never specialize anything that won't be recompiled
|
|
||||||
}
|
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, this, hints));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can this function node be regenerated with more specific type args?
|
* Can this function node be regenerated with more specific type args?
|
||||||
* @return true if specialization is possible
|
* @return true if specialization is possible
|
||||||
*/
|
*/
|
||||||
public boolean canSpecialize() {
|
public boolean canSpecialize() {
|
||||||
return snapshot != null && getFlag(CAN_SPECIALIZE);
|
return (flags & (IS_OPTIMISTIC | CAN_SPECIALIZE)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -450,28 +424,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
}
|
}
|
||||||
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
|
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
|
||||||
newState.add(state);
|
newState.add(state);
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, snapshot, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get any compiler hints that may associated with the function
|
|
||||||
* @return compiler hints
|
|
||||||
*/
|
|
||||||
public Compiler.Hints getHints() {
|
|
||||||
return this.hints == null ? Compiler.Hints.EMPTY : hints;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set compiler hints for this function
|
|
||||||
* @param lc lexical context
|
|
||||||
* @param hints compiler hints
|
|
||||||
* @return new function if hints changed
|
|
||||||
*/
|
|
||||||
public FunctionNode setHints(final LexicalContext lc, final Compiler.Hints hints) {
|
|
||||||
if (this.hints == hints) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -486,10 +439,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toString(final StringBuilder sb) {
|
public void toString(final StringBuilder sb) {
|
||||||
sb.append('[');
|
sb.append('[').
|
||||||
sb.append(returnType);
|
append(returnType).
|
||||||
sb.append(']');
|
append(']').
|
||||||
sb.append(' ');
|
append(' ');
|
||||||
|
|
||||||
sb.append("function");
|
sb.append("function");
|
||||||
|
|
||||||
@ -499,16 +452,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
}
|
}
|
||||||
|
|
||||||
sb.append('(');
|
sb.append('(');
|
||||||
boolean first = true;
|
|
||||||
|
|
||||||
for (final IdentNode parameter : parameters) {
|
for (final Iterator<IdentNode> iter = parameters.iterator(); iter.hasNext(); ) {
|
||||||
if (!first) {
|
final IdentNode parameter = iter.next();
|
||||||
sb.append(", ");
|
if (parameter.getSymbol() != null) {
|
||||||
} else {
|
sb.append('[').append(parameter.getType()).append(']').append(' ');
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter.toString(sb);
|
parameter.toString(sb);
|
||||||
|
if (iter.hasNext()) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(')');
|
sb.append(')');
|
||||||
@ -524,7 +477,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
if (this.flags == flags) {
|
if (this.flags == flags) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -546,11 +499,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should this function node be lazily code generated, i.e. first at link time
|
* Returns true if the function is optimistic
|
||||||
* @return true if lazy
|
* @return True if this function is optimistic
|
||||||
*/
|
*/
|
||||||
public boolean isLazy() {
|
public boolean isOptimistic() {
|
||||||
return getFlag(IS_LAZY);
|
return getFlag(IS_OPTIMISTIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -587,7 +540,15 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
* @return true if the function's generated Java method needs a {@code callee} parameter.
|
* @return true if the function's generated Java method needs a {@code callee} parameter.
|
||||||
*/
|
*/
|
||||||
public boolean needsCallee() {
|
public boolean needsCallee() {
|
||||||
return needsParentScope() || needsSelfSymbol() || isSplit() || (needsArguments() && !isStrict());
|
return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this function uses the return symbol
|
||||||
|
* @return true if uses the return symbol
|
||||||
|
*/
|
||||||
|
public boolean usesReturnSymbol() {
|
||||||
|
return isProgram() || isSplit() || getFlag(USES_RETURN_SYMBOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -634,7 +595,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
if(this.body == body) {
|
if(this.body == body) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -729,7 +690,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
if (this.lastToken == lastToken) {
|
if (this.lastToken == lastToken) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -751,7 +712,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
if (this.name.equals(name)) {
|
if (this.name.equals(name)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -773,15 +734,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
return getFlag(IS_SPLIT);
|
return getFlag(IS_SPLIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if this function has yet-to-be-generated child functions
|
|
||||||
*
|
|
||||||
* @return true if there are lazy child functions
|
|
||||||
*/
|
|
||||||
public boolean hasLazyChildren() {
|
|
||||||
return getFlag(HAS_LAZY_CHILDREN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parameters to this function
|
* Get the parameters to this function
|
||||||
* @return a list of IdentNodes which represent the function parameters, in order
|
* @return a list of IdentNodes which represent the function parameters, in order
|
||||||
@ -801,7 +753,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
if (this.parameters == parameters) {
|
if (this.parameters == parameters) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -821,12 +773,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this function need a self symbol - this is needed only for self
|
* Does this function use its self symbol - this is needed only for self-referencing named function expressions.
|
||||||
* referring functions
|
* Self-referencing declared functions won't have this flag set, as they can access their own symbol through the
|
||||||
* @return true if function needs a symbol for self
|
* scope (since they're bound to the symbol with their name in their enclosing scope).
|
||||||
|
* @return true if this function node is a named function expression that uses the symbol for itself.
|
||||||
*/
|
*/
|
||||||
public boolean needsSelfSymbol() {
|
public boolean usesSelfSymbol() {
|
||||||
return body.getFlag(Block.NEEDS_SELF_SYMBOL);
|
return body.getFlag(Block.USES_SELF_SYMBOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -871,11 +824,8 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
type,
|
type,
|
||||||
compileUnit,
|
compileUnit,
|
||||||
compilationState,
|
compilationState,
|
||||||
body.setReturnType(type),
|
body.setReturnType(type), parameters));
|
||||||
parameters,
|
}
|
||||||
snapshot,
|
|
||||||
hints));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the function is generated in strict mode
|
* Check if the function is generated in strict mode
|
||||||
@ -905,7 +855,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
|||||||
if (this.compileUnit == compileUnit) {
|
if (this.compileUnit == compileUnit) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,29 +28,36 @@ package jdk.nashorn.internal.ir;
|
|||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
|
import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
|
||||||
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||||
|
|
||||||
|
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IR representation for an identifier.
|
* IR representation for an identifier.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public final class IdentNode extends Expression implements PropertyKey, FunctionCall {
|
public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic {
|
||||||
private static final int PROPERTY_NAME = 1 << 0;
|
private static final int PROPERTY_NAME = 1 << 0;
|
||||||
private static final int INITIALIZED_HERE = 1 << 1;
|
private static final int INITIALIZED_HERE = 1 << 1;
|
||||||
private static final int FUNCTION = 1 << 2;
|
private static final int FUNCTION = 1 << 2;
|
||||||
private static final int FUTURESTRICT_NAME = 1 << 3;
|
private static final int FUTURESTRICT_NAME = 1 << 3;
|
||||||
|
private static final int OPTIMISTIC = 1 << 4;
|
||||||
|
|
||||||
/** Identifier. */
|
/** Identifier. */
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
/** Type for a callsite, e.g. X in a get()X or a set(X)V */
|
/** Optimistic type */
|
||||||
private final Type callSiteType;
|
private final Type optimisticType;
|
||||||
|
|
||||||
private final int flags;
|
private final int flags;
|
||||||
|
|
||||||
|
private final int programPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -60,16 +67,18 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
*/
|
*/
|
||||||
public IdentNode(final long token, final int finish, final String name) {
|
public IdentNode(final long token, final int finish, final String name) {
|
||||||
super(token, finish);
|
super(token, finish);
|
||||||
this.name = name.intern();
|
this.name = name.intern();
|
||||||
this.callSiteType = null;
|
this.optimisticType = null;
|
||||||
this.flags = 0;
|
this.flags = 0;
|
||||||
|
this.programPoint = INVALID_PROGRAM_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags) {
|
private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags, final int programPoint) {
|
||||||
super(identNode);
|
super(identNode);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.callSiteType = callSiteType;
|
this.optimisticType = callSiteType;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
|
this.programPoint = programPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,14 +88,15 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
*/
|
*/
|
||||||
public IdentNode(final IdentNode identNode) {
|
public IdentNode(final IdentNode identNode) {
|
||||||
super(identNode);
|
super(identNode);
|
||||||
this.name = identNode.getName();
|
this.name = identNode.getName();
|
||||||
this.callSiteType = null;
|
this.optimisticType = null;
|
||||||
this.flags = identNode.flags;
|
this.flags = identNode.flags;
|
||||||
|
this.programPoint = INVALID_PROGRAM_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return callSiteType == null ? super.getType() : callSiteType;
|
return optimisticType == null ? super.getType() : optimisticType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -94,11 +104,6 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasCallSiteType() {
|
|
||||||
//this is an identity that's part of a getter or setter
|
|
||||||
return callSiteType != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assist in IR navigation.
|
* Assist in IR navigation.
|
||||||
*
|
*
|
||||||
@ -115,13 +120,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toString(final StringBuilder sb) {
|
public void toString(final StringBuilder sb) {
|
||||||
if (hasCallSiteType()) {
|
Node.optimisticType(this, sb);
|
||||||
sb.append('{');
|
|
||||||
final String desc = getType().getDescriptor();
|
|
||||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
|
|
||||||
sb.append('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(name);
|
sb.append(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +158,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
if (isPropertyName()) {
|
if (isPropertyName()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new IdentNode(this, name, callSiteType, flags | PROPERTY_NAME);
|
return new IdentNode(this, name, optimisticType, flags | PROPERTY_NAME, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,7 +177,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
if (isFutureStrictName()) {
|
if (isFutureStrictName()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new IdentNode(this, name, callSiteType, flags | FUTURESTRICT_NAME);
|
return new IdentNode(this, name, optimisticType, flags | FUTURESTRICT_NAME, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,16 +196,16 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
if (isInitializedHere()) {
|
if (isInitializedHere()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new IdentNode(this, name, callSiteType, flags | INITIALIZED_HERE);
|
return new IdentNode(this, name, optimisticType, flags | INITIALIZED_HERE, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this IdentNode is a special identity, currently __DIR__, __FILE__
|
* Check if the name of this IdentNode is same as that of a compile-time property (currently __DIR__, __FILE__, and
|
||||||
* or __LINE__
|
* __LINE__).
|
||||||
*
|
*
|
||||||
* @return true if this IdentNode is special
|
* @return true if this IdentNode's name is same as that of a compile-time property
|
||||||
*/
|
*/
|
||||||
public boolean isSpecialIdentity() {
|
public boolean isCompileTimePropertyName() {
|
||||||
return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
|
return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +214,17 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
return (flags & FUNCTION) != 0;
|
return (flags & FUNCTION) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentNode setType(final TemporarySymbols ts, final Type callSiteType) {
|
||||||
|
if (this.optimisticType == callSiteType) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), callSiteType)) {
|
||||||
|
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", callSiteType, " instead of ", getType());
|
||||||
|
}
|
||||||
|
return new IdentNode(this, name, callSiteType, flags, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark this node as being the callee operand of a {@link CallNode}.
|
* 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.
|
* @return an ident node identical to this one in all aspects except with its function flag set.
|
||||||
@ -223,6 +233,47 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
|||||||
if (isFunction()) {
|
if (isFunction()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new IdentNode(this, name, callSiteType, flags | FUNCTION);
|
return new IdentNode(this, name, optimisticType, flags | FUNCTION, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProgramPoint() {
|
||||||
|
return programPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optimistic setProgramPoint(final int programPoint) {
|
||||||
|
if (this.programPoint == programPoint) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new IdentNode(this, name, optimisticType, flags, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostOptimisticType() {
|
||||||
|
return Type.INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostPessimisticType() {
|
||||||
|
return Type.OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeOptimistic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOptimistic() {
|
||||||
|
return (flags & OPTIMISTIC) == OPTIMISTIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optimistic setIsOptimistic(final boolean isOptimistic) {
|
||||||
|
if (isOptimistic() == isOptimistic) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new IdentNode(this, name, optimisticType, isOptimistic ? (flags | OPTIMISTIC) : (flags & ~OPTIMISTIC), programPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,12 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.ir;
|
package jdk.nashorn.internal.ir;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||||
|
|
||||||
|
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||||
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IR representation of an indexed access (brackets operator.)
|
* IR representation of an indexed access (brackets operator.)
|
||||||
*/
|
*/
|
||||||
@ -49,8 +52,8 @@ public final class IndexNode extends BaseNode {
|
|||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction) {
|
private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final Type optimisticType, final boolean isOptimistic, final int programPoint) {
|
||||||
super(indexNode, base, isFunction);
|
super(indexNode, base, isFunction, optimisticType, isOptimistic, programPoint);
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +75,8 @@ public final class IndexNode extends BaseNode {
|
|||||||
sb.append('(');
|
sb.append('(');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node.optimisticType(this, sb);
|
||||||
|
|
||||||
base.toString(sb);
|
base.toString(sb);
|
||||||
|
|
||||||
if (needsParen) {
|
if (needsParen) {
|
||||||
@ -95,7 +100,7 @@ public final class IndexNode extends BaseNode {
|
|||||||
if (this.base == base) {
|
if (this.base == base) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new IndexNode(this, base, index, isFunction());
|
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,15 +112,41 @@ public final class IndexNode extends BaseNode {
|
|||||||
if(this.index == index) {
|
if(this.index == index) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new IndexNode(this, base, index, isFunction());
|
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseNode setIsFunction() {
|
public IndexNode setType(final TemporarySymbols ts, final Type optimisticType) {
|
||||||
|
if (this.optimisticType == optimisticType) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), optimisticType)) {
|
||||||
|
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", optimisticType, " instead of ", getType());
|
||||||
|
}
|
||||||
|
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IndexNode setIsFunction() {
|
||||||
if (isFunction()) {
|
if (isFunction()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new IndexNode(this, base, index, true);
|
return new IndexNode(this, base, index, true, optimisticType, isOptimistic, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IndexNode setProgramPoint(int programPoint) {
|
||||||
|
if (this.programPoint == programPoint) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IndexNode setIsOptimistic(boolean isOptimistic) {
|
||||||
|
if (this.isOptimistic == isOptimistic) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +148,7 @@ public class LexicalContext {
|
|||||||
* @return the node that was pushed
|
* @return the node that was pushed
|
||||||
*/
|
*/
|
||||||
public <T extends LexicalContextNode> T push(final T node) {
|
public <T extends LexicalContextNode> T push(final T node) {
|
||||||
|
assert !contains(node);
|
||||||
if (sp == stack.length) {
|
if (sp == stack.length) {
|
||||||
final LexicalContextNode[] newStack = new LexicalContextNode[sp * 2];
|
final LexicalContextNode[] newStack = new LexicalContextNode[sp * 2];
|
||||||
System.arraycopy(stack, 0, newStack, 0, sp);
|
System.arraycopy(stack, 0, newStack, 0, sp);
|
||||||
@ -201,7 +202,6 @@ public class LexicalContext {
|
|||||||
return (T)popped;
|
return (T)popped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the top element in the context
|
* Return the top element in the context
|
||||||
* @return the node that was pushed last
|
* @return the node that was pushed last
|
||||||
@ -233,7 +233,6 @@ public class LexicalContext {
|
|||||||
* @return the new node
|
* @return the new node
|
||||||
*/
|
*/
|
||||||
public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode newNode) {
|
public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode newNode) {
|
||||||
//System.err.println("REPLACE old=" + Debug.id(oldNode) + " new=" + Debug.id(newNode));
|
|
||||||
for (int i = sp - 1; i >= 0; i--) {
|
for (int i = sp - 1; i >= 0; i--) {
|
||||||
if (stack[i] == oldNode) {
|
if (stack[i] == oldNode) {
|
||||||
assert i == (sp - 1) : "violation of contract - we always expect to find the replacement node on top of the lexical context stack: " + newNode + " has " + stack[i + 1].getClass() + " above it";
|
assert i == (sp - 1) : "violation of contract - we always expect to find the replacement node on top of the lexical context stack: " + newNode + " has " + stack[i + 1].getClass() + " above it";
|
||||||
@ -410,22 +409,6 @@ public class LexicalContext {
|
|||||||
return getParentBlock() == null;
|
return getParentBlock() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the expression defining the function is a callee of a CallNode that should be the second
|
|
||||||
* element on the stack, e.g. <code>(function(){})()</code>. That is, if the stack ends with
|
|
||||||
* {@code [..., CallNode, FunctionNode]} then {@code callNode.getFunction()} should be equal to
|
|
||||||
* {@code functionNode}, and the top of the stack should itself be a variant of {@code functionNode}.
|
|
||||||
* @param functionNode the function node being tested
|
|
||||||
* @return true if the expression defining the current function is a callee of a call expression.
|
|
||||||
*/
|
|
||||||
public boolean isFunctionDefinedInCurrentCall(FunctionNode functionNode) {
|
|
||||||
final LexicalContextNode parent = stack[sp - 2];
|
|
||||||
if (parent instanceof CallNode && ((CallNode)parent).getFunction() == functionNode) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parent function for a function in the lexical context
|
* Get the parent function for a function in the lexical context
|
||||||
* @param functionNode function for which to get parent
|
* @param functionNode function for which to get parent
|
||||||
@ -444,15 +427,23 @@ public class LexicalContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the number of with scopes until a given node
|
* Count the number of scopes until a given node.
|
||||||
* @param until node to stop counting at, or null if all nodes should be counted
|
* @param until node to stop counting at. Must be within the current function
|
||||||
* @return number of with scopes encountered in the context
|
* @return number of with scopes encountered in the context
|
||||||
*/
|
*/
|
||||||
public int getScopeNestingLevelTo(final LexicalContextNode until) {
|
public int getScopeNestingLevelTo(final LexicalContextNode until) {
|
||||||
|
assert until != null;
|
||||||
//count the number of with nodes until "until" is hit
|
//count the number of with nodes until "until" is hit
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (final Iterator<WithNode> iter = new NodeIterator<>(WithNode.class, until); iter.hasNext(); iter.next()) {
|
for (final Iterator<LexicalContextNode> iter = getAllNodes(); iter.hasNext();) {
|
||||||
n++;
|
final LexicalContextNode node = iter.next();
|
||||||
|
if(node == until) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert !(node instanceof FunctionNode); // Can't go outside current function
|
||||||
|
if(node instanceof WithNode || (node instanceof Block && ((Block)node).needsScope())) {
|
||||||
|
n++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ package jdk.nashorn.internal.ir;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||||
import jdk.nashorn.internal.codegen.types.ArrayType;
|
import jdk.nashorn.internal.codegen.types.ArrayType;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
@ -657,23 +656,12 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
* Compute things like widest element type needed. Internal use from compiler only
|
* Compute things like widest element type needed. Internal use from compiler only
|
||||||
*/
|
*/
|
||||||
public void analyze() {
|
public void analyze() {
|
||||||
elementType = Type.INT;
|
assert elementType.isUnknown();
|
||||||
analyzeElements();
|
elementType = getNarrowestElementType(value);
|
||||||
|
|
||||||
if (elementType.isInteger()) {
|
|
||||||
presetIntArray();
|
|
||||||
} else if (elementType.isLong()) {
|
|
||||||
presetLongArray();
|
|
||||||
} else if (elementType.isNumeric()) {
|
|
||||||
presetNumberArray();
|
|
||||||
} else {
|
|
||||||
presetObjectArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void presetIntArray() {
|
private int[] presetIntArray() {
|
||||||
final int[] array = new int[value.length];
|
final int[] array = new int[value.length];
|
||||||
final int[] computed = new int[value.length];
|
|
||||||
int nComputed = 0;
|
int nComputed = 0;
|
||||||
|
|
||||||
for (int i = 0; i < value.length; i++) {
|
for (int i = 0; i < value.length; i++) {
|
||||||
@ -682,17 +670,16 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
if (element instanceof Number) {
|
if (element instanceof Number) {
|
||||||
array[i] = ((Number)element).intValue();
|
array[i] = ((Number)element).intValue();
|
||||||
} else {
|
} else {
|
||||||
computed[nComputed++] = i;
|
assert getPostsets()[nComputed++] == i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
presets = array;
|
assert getPostsets().length == nComputed;
|
||||||
postsets = Arrays.copyOf(computed, nComputed);
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void presetLongArray() {
|
private long[] presetLongArray() {
|
||||||
final long[] array = new long[value.length];
|
final long[] array = new long[value.length];
|
||||||
final int[] computed = new int[value.length];
|
|
||||||
int nComputed = 0;
|
int nComputed = 0;
|
||||||
|
|
||||||
for (int i = 0; i < value.length; i++) {
|
for (int i = 0; i < value.length; i++) {
|
||||||
@ -701,17 +688,16 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
if (element instanceof Number) {
|
if (element instanceof Number) {
|
||||||
array[i] = ((Number)element).longValue();
|
array[i] = ((Number)element).longValue();
|
||||||
} else {
|
} else {
|
||||||
computed[nComputed++] = i;
|
assert getPostsets()[nComputed++] == i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
presets = array;
|
assert getPostsets().length == nComputed;
|
||||||
postsets = Arrays.copyOf(computed, nComputed);
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void presetNumberArray() {
|
private double[] presetNumberArray() {
|
||||||
final double[] array = new double[value.length];
|
final double[] array = new double[value.length];
|
||||||
final int[] computed = new int[value.length];
|
|
||||||
int nComputed = 0;
|
int nComputed = 0;
|
||||||
|
|
||||||
for (int i = 0; i < value.length; i++) {
|
for (int i = 0; i < value.length; i++) {
|
||||||
@ -720,40 +706,40 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
if (element instanceof Number) {
|
if (element instanceof Number) {
|
||||||
array[i] = ((Number)element).doubleValue();
|
array[i] = ((Number)element).doubleValue();
|
||||||
} else {
|
} else {
|
||||||
computed[nComputed++] = i;
|
assert getPostsets()[nComputed++] == i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
presets = array;
|
assert getPostsets().length == nComputed;
|
||||||
postsets = Arrays.copyOf(computed, nComputed);
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void presetObjectArray() {
|
private Object[] presetObjectArray() {
|
||||||
final Object[] array = new Object[value.length];
|
final Object[] array = new Object[value.length];
|
||||||
final int[] computed = new int[value.length];
|
|
||||||
int nComputed = 0;
|
int nComputed = 0;
|
||||||
|
|
||||||
for (int i = 0; i < value.length; i++) {
|
for (int i = 0; i < value.length; i++) {
|
||||||
final Node node = value[i];
|
final Node node = value[i];
|
||||||
|
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
computed[nComputed++] = i;
|
assert getPostsets()[nComputed++] == i;
|
||||||
} else {
|
} else {
|
||||||
final Object element = objectAsConstant(node);
|
final Object element = objectAsConstant(node);
|
||||||
|
|
||||||
if (element != POSTSET_MARKER) {
|
if (element != POSTSET_MARKER) {
|
||||||
array[i] = element;
|
array[i] = element;
|
||||||
} else {
|
} else {
|
||||||
computed[nComputed++] = i;
|
assert getPostsets()[nComputed++] == i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
presets = array;
|
assert getPostsets().length == nComputed;
|
||||||
postsets = Arrays.copyOf(computed, nComputed);
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void analyzeElements() {
|
private static Type getNarrowestElementType(final Expression[] value) {
|
||||||
|
Type elementType = Type.INT;
|
||||||
for (final Expression node : value) {
|
for (final Expression node : value) {
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
elementType = elementType.widest(Type.OBJECT); //no way to represent undefined as number
|
elementType = elementType.widest(Type.OBJECT); //no way to represent undefined as number
|
||||||
@ -777,6 +763,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return elementType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -789,6 +776,10 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
* @return array element type
|
* @return array element type
|
||||||
*/
|
*/
|
||||||
public ArrayType getArrayType() {
|
public ArrayType getArrayType() {
|
||||||
|
return getArrayType(getElementType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayType getArrayType(final Type elementType) {
|
||||||
if (elementType.isInteger()) {
|
if (elementType.isInteger()) {
|
||||||
return Type.INT_ARRAY;
|
return Type.INT_ARRAY;
|
||||||
} else if (elementType.isLong()) {
|
} else if (elementType.isLong()) {
|
||||||
@ -810,6 +801,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
* @return element type
|
* @return element type
|
||||||
*/
|
*/
|
||||||
public Type getElementType() {
|
public Type getElementType() {
|
||||||
|
assert !elementType.isUnknown();
|
||||||
return elementType;
|
return elementType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -819,6 +811,18 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
* @return post set indices
|
* @return post set indices
|
||||||
*/
|
*/
|
||||||
public int[] getPostsets() {
|
public int[] getPostsets() {
|
||||||
|
if(postsets == null) {
|
||||||
|
final int[] computed = new int[value.length];
|
||||||
|
int nComputed = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < value.length; i++) {
|
||||||
|
final Expression element = value[i];
|
||||||
|
if(element == null || objectAsConstant(element) == POSTSET_MARKER) {
|
||||||
|
computed[nComputed++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
postsets = Arrays.copyOf(computed, nComputed);
|
||||||
|
}
|
||||||
return postsets;
|
return postsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,6 +831,18 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
|||||||
* @return presets array, always returns an array type
|
* @return presets array, always returns an array type
|
||||||
*/
|
*/
|
||||||
public Object getPresets() {
|
public Object getPresets() {
|
||||||
|
if(presets == null) {
|
||||||
|
final Type type = getElementType();
|
||||||
|
if (type.isInteger()) {
|
||||||
|
presets = presetIntArray();
|
||||||
|
} else if (type.isLong()) {
|
||||||
|
presets = presetLongArray();
|
||||||
|
} else if (type.isNumeric()) {
|
||||||
|
presets = presetNumberArray();
|
||||||
|
} else {
|
||||||
|
presets = presetObjectArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
return presets;
|
return presets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +282,21 @@ public abstract class Node implements Cloneable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag an expression as optimistic or not. This is a convenience wrapper
|
||||||
|
* that is a no op of the expression cannot be optimistic
|
||||||
|
* @param expr expression
|
||||||
|
* @param isOptimistic is optimistic flag
|
||||||
|
* @return the new expression, or same if unmodified state
|
||||||
|
*/
|
||||||
|
//SAM method in Java 8
|
||||||
|
public static Expression setIsOptimistic(final Expression expr, final boolean isOptimistic) {
|
||||||
|
if (expr instanceof Optimistic) {
|
||||||
|
return (Expression)((Optimistic)expr).setIsOptimistic(isOptimistic);
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
|
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
|
||||||
static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
|
static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
@ -304,4 +319,21 @@ public abstract class Node implements Cloneable {
|
|||||||
}
|
}
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final String OPT_IDENTIFIER = "%";
|
||||||
|
|
||||||
|
static void optimisticType(final Node node, final StringBuilder sb) {
|
||||||
|
if (node instanceof Optimistic && ((Optimistic)node).isOptimistic()) {
|
||||||
|
sb.append('{');
|
||||||
|
final String desc = (((Expression)node).getType()).getDescriptor();
|
||||||
|
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : desc);
|
||||||
|
if (node instanceof Optimistic && ((Optimistic)node).isOptimistic()) {
|
||||||
|
sb.append(OPT_IDENTIFIER);
|
||||||
|
sb.append('_');
|
||||||
|
sb.append(((Optimistic)node).getProgramPoint());
|
||||||
|
}
|
||||||
|
sb.append('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
112
nashorn/src/jdk/nashorn/internal/ir/Optimistic.java
Normal file
112
nashorn/src/jdk/nashorn/internal/ir/Optimistic.java
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen.types.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a node that can be optimistically typed? This means that it
|
||||||
|
* has a probable type but it's not available through static analysis
|
||||||
|
*
|
||||||
|
* The follow nodes are optimistic, with reasons therefore given within
|
||||||
|
* parenthesis
|
||||||
|
*
|
||||||
|
* @see IndexNode (dynamicGetIndex)
|
||||||
|
* @see BinaryNode (local calculations to strongly typed bytecode)
|
||||||
|
* @see UnaryNode (local calculations to strongly typed bytecode)
|
||||||
|
* @see CallNode (dynamicCall)
|
||||||
|
*
|
||||||
|
* TODO : to be implemented are
|
||||||
|
*
|
||||||
|
* @see AccessNode (dynamicGet)
|
||||||
|
* @see IdentNode (dynamicGet)
|
||||||
|
*/
|
||||||
|
public interface Optimistic {
|
||||||
|
/**
|
||||||
|
* Unique node ID that is associated with an invokedynamic call that mail
|
||||||
|
* fail and its callsite. This is so that nodes can be regenerated less
|
||||||
|
* pessimistically the next generation if an assumption failed
|
||||||
|
*
|
||||||
|
* @return unique node id
|
||||||
|
*/
|
||||||
|
public int getProgramPoint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the node number for this node, associating with a unique per-function
|
||||||
|
* program point
|
||||||
|
* @param programPoint the node number
|
||||||
|
* @return new node, or same if unchanged
|
||||||
|
*/
|
||||||
|
public Optimistic setProgramPoint(final int programPoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is it possible for this particular implementor to actually have any optimism?
|
||||||
|
* SHIFT operators for instance are binary nodes, but never optimistic. Multiply
|
||||||
|
* operators are. We might want to refurbish the type hierarchy to fix this.
|
||||||
|
* @return true if theoretically optimistic
|
||||||
|
*/
|
||||||
|
public boolean canBeOptimistic();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this op optimistic, i.e. narrower than its narrowest statically provable
|
||||||
|
* type
|
||||||
|
* @return true if optimistic
|
||||||
|
*/
|
||||||
|
public boolean isOptimistic();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the most optimistic type for this node. Typically we start out as
|
||||||
|
* an int, and then at runtime we bump this up to number and then Object
|
||||||
|
*
|
||||||
|
* @return optimistic type to be used in code generation
|
||||||
|
*/
|
||||||
|
public Type getMostOptimisticType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most pessimistic type that is guaranteed to be safe. Typically this is
|
||||||
|
* number for arithmetic operations that can overflow, or Object for an add
|
||||||
|
*
|
||||||
|
* @return pessimistic type guaranteed to never overflow
|
||||||
|
*/
|
||||||
|
public Type getMostPessimisticType();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag this type override as optimistic rather than based on statically known
|
||||||
|
* type coercion semantics
|
||||||
|
* @param isOptimistic is this function optimistic
|
||||||
|
* @return new optimistic function
|
||||||
|
*/
|
||||||
|
public Optimistic setIsOptimistic(final boolean isOptimistic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the override type
|
||||||
|
*
|
||||||
|
* @param ts temporary symbols
|
||||||
|
* @param type the type
|
||||||
|
* @return a node equivalent to this one except for the requested change.
|
||||||
|
*/
|
||||||
|
public Optimistic setType(final TemporarySymbols ts, final Type type);
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* 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.IntDeque;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lexical context that keeps track of optimistic assumptions (if any)
|
||||||
|
* made during code generation. Used from Attr and FinalizeTypes
|
||||||
|
*/
|
||||||
|
public class OptimisticLexicalContext extends LexicalContext {
|
||||||
|
|
||||||
|
private final boolean isEnabled;
|
||||||
|
|
||||||
|
class Assumption {
|
||||||
|
Symbol symbol;
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
Assumption(final Symbol symbol, final Type type) {
|
||||||
|
this.symbol = symbol;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return symbol.getName() + "=" + type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Optimistic assumptions that could be made per function */
|
||||||
|
private final Deque<List<Assumption>> optimisticAssumptions = new ArrayDeque<>();
|
||||||
|
private final IntDeque splitNodes = new IntDeque();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param isEnabled are optimistic types enabled?
|
||||||
|
*/
|
||||||
|
public OptimisticLexicalContext(final boolean isEnabled) {
|
||||||
|
super();
|
||||||
|
this.isEnabled = isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are optimistic types enabled
|
||||||
|
* @return true if optimistic types
|
||||||
|
*/
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an optimistic assumption during codegen
|
||||||
|
* TODO : different parameters and more info about the assumption for future profiling
|
||||||
|
* needs
|
||||||
|
* @param symbol symbol
|
||||||
|
* @param type type
|
||||||
|
*/
|
||||||
|
public void logOptimisticAssumption(final Symbol symbol, final Type type) {
|
||||||
|
if (isEnabled) {
|
||||||
|
final List<Assumption> peek = optimisticAssumptions.peek();
|
||||||
|
peek.add(new Assumption(symbol, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of optimistic assumptions made
|
||||||
|
* @return optimistic assumptions
|
||||||
|
*/
|
||||||
|
public List<Assumption> getOptimisticAssumptions() {
|
||||||
|
return Collections.unmodifiableList(optimisticAssumptions.peek());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this method have optimistic assumptions made during codegen?
|
||||||
|
* @return true if optimistic assumptions were made
|
||||||
|
*/
|
||||||
|
public boolean hasOptimisticAssumptions() {
|
||||||
|
return !optimisticAssumptions.isEmpty() && !getOptimisticAssumptions().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends LexicalContextNode> T push(final T node) {
|
||||||
|
if (isEnabled) {
|
||||||
|
if(node instanceof FunctionNode) {
|
||||||
|
optimisticAssumptions.push(new ArrayList<Assumption>());
|
||||||
|
splitNodes.push(0);
|
||||||
|
} else if(node instanceof SplitNode) {
|
||||||
|
splitNodes.getAndIncrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends LexicalContextNode> T pop(final T node) {
|
||||||
|
T popped = super.pop(node);
|
||||||
|
if (isEnabled) {
|
||||||
|
if(node instanceof FunctionNode) {
|
||||||
|
optimisticAssumptions.pop();
|
||||||
|
assert splitNodes.peek() == 0;
|
||||||
|
splitNodes.pop();
|
||||||
|
} else if(node instanceof SplitNode) {
|
||||||
|
splitNodes.decrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return popped;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the lexical context is inside a split node
|
||||||
|
* @return true if we are traversing a SplitNode
|
||||||
|
*/
|
||||||
|
public boolean isInSplitNode() {
|
||||||
|
return !splitNodes.isEmpty() && splitNodes.peek() > 0;
|
||||||
|
}
|
||||||
|
}
|
@ -34,11 +34,13 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
|
|||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
import jdk.nashorn.internal.parser.TokenType;
|
import jdk.nashorn.internal.parser.TokenType;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IR representation for a runtime call.
|
* IR representation for a runtime call.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public class RuntimeNode extends Expression {
|
public class RuntimeNode extends Expression implements Optimistic {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request enum used for meta-information about the runtime request
|
* Request enum used for meta-information about the runtime request
|
||||||
@ -77,7 +79,9 @@ public class RuntimeNode extends Expression {
|
|||||||
/** !== operator with at least one object */
|
/** !== operator with at least one object */
|
||||||
NE_STRICT(TokenType.NE_STRICT, Type.BOOLEAN, 2, true),
|
NE_STRICT(TokenType.NE_STRICT, Type.BOOLEAN, 2, true),
|
||||||
/** != operator with at least one object */
|
/** != operator with at least one object */
|
||||||
NE(TokenType.NE, Type.BOOLEAN, 2, true);
|
NE(TokenType.NE, Type.BOOLEAN, 2, true),
|
||||||
|
/** Verify type */
|
||||||
|
TYPE_GUARD(TokenType.VOID, Type.INT, 2);
|
||||||
|
|
||||||
/** token type */
|
/** token type */
|
||||||
private final TokenType tokenType;
|
private final TokenType tokenType;
|
||||||
@ -210,6 +214,17 @@ public class RuntimeNode extends Expression {
|
|||||||
return request == NE || request == NE_STRICT;
|
return request == NE || request == NE_STRICT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this strict?
|
||||||
|
*
|
||||||
|
* @param request a request
|
||||||
|
*
|
||||||
|
* @return true if script
|
||||||
|
*/
|
||||||
|
public static boolean isStrict(final Request request) {
|
||||||
|
return request == EQ_STRICT || request == NE_STRICT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this request can be reversed, return the reverse request
|
* If this request can be reversed, return the reverse request
|
||||||
* Eq EQ {@literal ->} NE.
|
* Eq EQ {@literal ->} NE.
|
||||||
@ -301,6 +316,8 @@ public class RuntimeNode extends Expression {
|
|||||||
/** is final - i.e. may not be removed again, lower in the code pipeline */
|
/** is final - i.e. may not be removed again, lower in the code pipeline */
|
||||||
private final boolean isFinal;
|
private final boolean isFinal;
|
||||||
|
|
||||||
|
private final int programPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -315,14 +332,16 @@ public class RuntimeNode extends Expression {
|
|||||||
this.request = request;
|
this.request = request;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.isFinal = false;
|
this.isFinal = false;
|
||||||
|
this.programPoint = INVALID_PROGRAM_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List<Expression> args) {
|
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List<Expression> args, final int programPoint) {
|
||||||
super(runtimeNode);
|
super(runtimeNode);
|
||||||
|
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.isFinal = isFinal;
|
this.isFinal = isFinal;
|
||||||
|
this.programPoint = programPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,6 +380,7 @@ public class RuntimeNode extends Expression {
|
|||||||
this.request = request;
|
this.request = request;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.isFinal = false;
|
this.isFinal = false;
|
||||||
|
this.programPoint = parent instanceof Optimistic ? ((Optimistic)parent).getProgramPoint() : INVALID_PROGRAM_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,7 +390,7 @@ public class RuntimeNode extends Expression {
|
|||||||
* @param request the request
|
* @param request the request
|
||||||
*/
|
*/
|
||||||
public RuntimeNode(final UnaryNode parent, final Request request) {
|
public RuntimeNode(final UnaryNode parent, final Request request) {
|
||||||
this(parent, request, parent.rhs());
|
this(parent, request, parent.getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -400,7 +420,7 @@ public class RuntimeNode extends Expression {
|
|||||||
if (this.isFinal == isFinal) {
|
if (this.isFinal == isFinal) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new RuntimeNode(this, request, isFinal, args);
|
return new RuntimeNode(this, request, isFinal, args, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -457,7 +477,7 @@ public class RuntimeNode extends Expression {
|
|||||||
if (this.args == args) {
|
if (this.args == args) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new RuntimeNode(this, request, isFinal, args);
|
return new RuntimeNode(this, request, isFinal, args, programPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -483,4 +503,49 @@ public class RuntimeNode extends Expression {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO these are blank for now:
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProgramPoint() {
|
||||||
|
return programPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RuntimeNode setProgramPoint(final int programPoint) {
|
||||||
|
if(this.programPoint == programPoint) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new RuntimeNode(this, request, isFinal, args, programPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeOptimistic() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostOptimisticType() {
|
||||||
|
return getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostPessimisticType() {
|
||||||
|
return getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOptimistic() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RuntimeNode setIsOptimistic(final boolean isOptimistic) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RuntimeNode setType(final TemporarySymbols ts, final Type type) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
package jdk.nashorn.internal.ir;
|
package jdk.nashorn.internal.ir;
|
||||||
|
|
||||||
|
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -35,8 +37,6 @@ import jdk.nashorn.internal.runtime.Context;
|
|||||||
import jdk.nashorn.internal.runtime.Debug;
|
import jdk.nashorn.internal.runtime.Debug;
|
||||||
import jdk.nashorn.internal.runtime.options.Options;
|
import jdk.nashorn.internal.runtime.options.Options;
|
||||||
|
|
||||||
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a name to specific data.
|
* Maps a name to specific data.
|
||||||
*/
|
*/
|
||||||
@ -56,27 +56,27 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
|
public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
|
||||||
|
|
||||||
/** Is this scope */
|
/** Is this scope */
|
||||||
public static final int IS_SCOPE = 1 << 4;
|
public static final int IS_SCOPE = 1 << 4;
|
||||||
/** Is this a this symbol */
|
/** Is this a this symbol */
|
||||||
public static final int IS_THIS = 1 << 5;
|
public static final int IS_THIS = 1 << 5;
|
||||||
/** Can this symbol ever be undefined */
|
/** Can this symbol ever be undefined */
|
||||||
public static final int CAN_BE_UNDEFINED = 1 << 6;
|
public static final int CAN_BE_UNDEFINED = 1 << 6;
|
||||||
/** Is this symbol always defined? */
|
/** Is this symbol always defined? */
|
||||||
public static final int IS_ALWAYS_DEFINED = 1 << 8;
|
public static final int IS_ALWAYS_DEFINED = 1 << 7;
|
||||||
/** Can this symbol ever have primitive types */
|
|
||||||
public static final int CAN_BE_PRIMITIVE = 1 << 9;
|
|
||||||
/** Is this a let */
|
/** Is this a let */
|
||||||
public static final int IS_LET = 1 << 10;
|
public static final int IS_LET = 1 << 8;
|
||||||
/** Is this an internal symbol, never represented explicitly in source code */
|
/** Is this an internal symbol, never represented explicitly in source code */
|
||||||
public static final int IS_INTERNAL = 1 << 11;
|
public static final int IS_INTERNAL = 1 << 9;
|
||||||
/** Is this a function self-reference symbol */
|
/** Is this a function self-reference symbol */
|
||||||
public static final int IS_FUNCTION_SELF = 1 << 12;
|
public static final int IS_FUNCTION_SELF = 1 << 10;
|
||||||
/** Is this a specialized param? */
|
/** Is this a specialized param, i.e. known type base on runtime callsite? */
|
||||||
public static final int IS_SPECIALIZED_PARAM = 1 << 13;
|
public static final int IS_SPECIALIZED_PARAM = 1 << 11;
|
||||||
/** Is this symbol a shared temporary? */
|
/** Is this symbol a shared temporary? */
|
||||||
public static final int IS_SHARED = 1 << 14;
|
public static final int IS_SHARED = 1 << 12;
|
||||||
/** Is this a function declaration? */
|
/** Is this a function declaration? */
|
||||||
public static final int IS_FUNCTION_DECLARATION = 1 << 15;
|
public static final int IS_FUNCTION_DECLARATION = 1 << 13;
|
||||||
|
/** Is this a program level symbol? */
|
||||||
|
public static final int IS_PROGRAM_LEVEL = 1 << 14;
|
||||||
|
|
||||||
/** Null or name identifying symbol. */
|
/** Null or name identifying symbol. */
|
||||||
private final String name;
|
private final String name;
|
||||||
@ -142,7 +142,7 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
this.slot = slot;
|
this.slot = slot;
|
||||||
this.fieldIndex = -1;
|
this.fieldIndex = -1;
|
||||||
this.range = Range.createUnknownRange();
|
this.range = Range.createUnknownRange();
|
||||||
trace("CREATE SYMBOL");
|
trace("CREATE SYMBOL " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,57 +205,61 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void print(final PrintWriter stream) {
|
void print(final PrintWriter stream) {
|
||||||
final String printName = align(name, 20);
|
final StringBuilder sb = new StringBuilder();
|
||||||
final String printType = align(type.toString(), 10);
|
|
||||||
final String printSlot = align(slot == -1 ? "none" : "" + slot, 10);
|
sb.append(align(name, 20)).
|
||||||
String printFlags = "";
|
append(": ").
|
||||||
|
append(align(type.toString(), 10)).
|
||||||
|
append(", ").
|
||||||
|
append(align(slot == -1 ? "none" : "" + slot, 10));
|
||||||
|
|
||||||
switch (flags & KINDMASK) {
|
switch (flags & KINDMASK) {
|
||||||
case IS_TEMP:
|
case IS_TEMP:
|
||||||
printFlags = "temp " + printFlags;
|
sb.append(" temp");
|
||||||
break;
|
break;
|
||||||
case IS_GLOBAL:
|
case IS_GLOBAL:
|
||||||
printFlags = "global " + printFlags;
|
sb.append(" global");
|
||||||
break;
|
break;
|
||||||
case IS_VAR:
|
case IS_VAR:
|
||||||
printFlags = "var " + printFlags;
|
sb.append(" var");
|
||||||
break;
|
break;
|
||||||
case IS_PARAM:
|
case IS_PARAM:
|
||||||
printFlags = "param " + printFlags;
|
sb.append(" param");
|
||||||
break;
|
break;
|
||||||
case IS_CONSTANT:
|
case IS_CONSTANT:
|
||||||
printFlags = "CONSTANT " + printFlags;
|
sb.append(" const");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isScope()) {
|
if (isScope()) {
|
||||||
printFlags += "scope ";
|
sb.append(" scope");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInternal()) {
|
if (isInternal()) {
|
||||||
printFlags += "internal ";
|
sb.append(" internal");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLet()) {
|
if (isLet()) {
|
||||||
printFlags += "let ";
|
sb.append(" let");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isThis()) {
|
if (isThis()) {
|
||||||
printFlags += "this ";
|
sb.append(" this");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canBeUndefined()) {
|
if (!canBeUndefined()) {
|
||||||
printFlags += "always_def ";
|
sb.append(" def'd");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canBePrimitive()) {
|
if (isProgramLevel()) {
|
||||||
printFlags += "can_be_prim ";
|
sb.append(" program");
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.print(printName + ": " + printType + ", " + printSlot + ", " + printFlags);
|
sb.append('\n');
|
||||||
stream.println();
|
|
||||||
|
stream.print(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,9 +276,11 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
* Allocate a slot for this symbol.
|
* Allocate a slot for this symbol.
|
||||||
*
|
*
|
||||||
* @param needsSlot True if symbol needs a slot.
|
* @param needsSlot True if symbol needs a slot.
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setNeedsSlot(final boolean needsSlot) {
|
public Symbol setNeedsSlot(final boolean needsSlot) {
|
||||||
setSlot(needsSlot ? 0 : -1);
|
setSlot(needsSlot ? 0 : -1);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -312,10 +318,6 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canBePrimitive()) {
|
|
||||||
sb.append(" P?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,27 +383,28 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag this symbol as scope as described in {@link Symbol#isScope()}
|
* Flag this symbol as scope as described in {@link Symbol#isScope()}
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
/**
|
public Symbol setIsScope() {
|
||||||
* Flag this symbol as scope as described in {@link Symbol#isScope()}
|
|
||||||
*/
|
|
||||||
public void setIsScope() {
|
|
||||||
if (!isScope()) {
|
if (!isScope()) {
|
||||||
trace("SET IS SCOPE");
|
trace("SET IS SCOPE");
|
||||||
assert !isShared();
|
assert !isShared();
|
||||||
flags |= IS_SCOPE;
|
flags |= IS_SCOPE;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary.
|
* Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary.
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setIsShared() {
|
public Symbol setIsShared() {
|
||||||
if (!isShared()) {
|
if (!isShared()) {
|
||||||
assert isTemp();
|
assert isTemp();
|
||||||
trace("SET IS SHARED");
|
trace("SET IS SHARED");
|
||||||
flags |= IS_SHARED;
|
flags |= IS_SHARED;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -447,6 +450,14 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
return isParam() || (flags & IS_ALWAYS_DEFINED) == IS_ALWAYS_DEFINED;
|
return isParam() || (flags & IS_ALWAYS_DEFINED) == IS_ALWAYS_DEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this is a program (script) level definition
|
||||||
|
* @return true if program level
|
||||||
|
*/
|
||||||
|
public boolean isProgramLevel() {
|
||||||
|
return (flags & IS_PROGRAM_LEVEL) == IS_PROGRAM_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the range for this symbol
|
* Get the range for this symbol
|
||||||
* @return range for symbol
|
* @return range for symbol
|
||||||
@ -458,9 +469,11 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
/**
|
/**
|
||||||
* Set the range for this symbol
|
* Set the range for this symbol
|
||||||
* @param range range
|
* @param range range
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setRange(final Range range) {
|
public Symbol setRange(final Range range) {
|
||||||
this.range = range;
|
this.range = range;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -480,14 +493,6 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
return (flags & IS_SPECIALIZED_PARAM) == IS_SPECIALIZED_PARAM;
|
return (flags & IS_SPECIALIZED_PARAM) == IS_SPECIALIZED_PARAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether this symbol ever has primitive assignments. Conservative
|
|
||||||
* @return true if primitive assignments exist
|
|
||||||
*/
|
|
||||||
public boolean canBePrimitive() {
|
|
||||||
return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this symbol can ever be undefined
|
* Check if this symbol can ever be undefined
|
||||||
* @return true if can be undefined
|
* @return true if can be undefined
|
||||||
@ -498,26 +503,16 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag this symbol as potentially undefined in parts of the program
|
* Flag this symbol as potentially undefined in parts of the program
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setCanBeUndefined() {
|
public Symbol setCanBeUndefined() {
|
||||||
assert type.isObject() : type;
|
|
||||||
if (isAlwaysDefined()) {
|
if (isAlwaysDefined()) {
|
||||||
return;
|
return this;
|
||||||
} else if (!canBeUndefined()) {
|
} else if (!canBeUndefined()) {
|
||||||
assert !isShared();
|
assert !isShared();
|
||||||
flags |= CAN_BE_UNDEFINED;
|
flags |= CAN_BE_UNDEFINED;
|
||||||
}
|
}
|
||||||
}
|
return this;
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag this symbol as potentially primitive
|
|
||||||
* @param type the primitive type it occurs with, currently unused but can be used for width guesses
|
|
||||||
*/
|
|
||||||
public void setCanBePrimitive(final Type type) {
|
|
||||||
if(!canBePrimitive()) {
|
|
||||||
assert !isShared();
|
|
||||||
flags |= CAN_BE_PRIMITIVE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -557,7 +552,7 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
* Flag this symbol as a let
|
* Flag this symbol as a let
|
||||||
*/
|
*/
|
||||||
public void setIsLet() {
|
public void setIsLet() {
|
||||||
if(!isLet()) {
|
if (!isLet()) {
|
||||||
assert !isShared();
|
assert !isShared();
|
||||||
flags |= IS_LET;
|
flags |= IS_LET;
|
||||||
}
|
}
|
||||||
@ -587,12 +582,14 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
* and get allocated in a JO-prefixed ScriptObject subclass.
|
* and get allocated in a JO-prefixed ScriptObject subclass.
|
||||||
*
|
*
|
||||||
* @param fieldIndex field index - a positive integer
|
* @param fieldIndex field index - a positive integer
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setFieldIndex(final int fieldIndex) {
|
public Symbol setFieldIndex(final int fieldIndex) {
|
||||||
if(this.fieldIndex != fieldIndex) {
|
if (this.fieldIndex != fieldIndex) {
|
||||||
assert !isShared();
|
assert !isShared();
|
||||||
this.fieldIndex = fieldIndex;
|
this.fieldIndex = fieldIndex;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -606,12 +603,40 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
/**
|
/**
|
||||||
* Set the symbol flags
|
* Set the symbol flags
|
||||||
* @param flags flags
|
* @param flags flags
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setFlags(final int flags) {
|
public Symbol setFlags(final int flags) {
|
||||||
if(this.flags != flags) {
|
if (this.flags != flags) {
|
||||||
assert !isShared();
|
assert !isShared() : this;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a single symbol flag
|
||||||
|
* @param flag flag to set
|
||||||
|
* @return the symbol
|
||||||
|
*/
|
||||||
|
public Symbol setFlag(final int flag) {
|
||||||
|
if ((this.flags & flag) == 0) {
|
||||||
|
assert !isShared() : this;
|
||||||
|
this.flags |= flag;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears a single symbol flag
|
||||||
|
* @param flag flag to set
|
||||||
|
* @return the symbol
|
||||||
|
*/
|
||||||
|
public Symbol clearFlag(final int flag) {
|
||||||
|
if ((this.flags & flag) != 0) {
|
||||||
|
assert !isShared() : this;
|
||||||
|
this.flags &= ~flag;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -632,9 +657,11 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Increase the symbol's use count by one.
|
* Increase the symbol's use count by one.
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void increaseUseCount() {
|
public Symbol increaseUseCount() {
|
||||||
useCount++;
|
useCount++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -647,33 +674,39 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the bytecode slot for this symbol
|
* Set the bytecode slot for this symbol
|
||||||
* @param slot valid bytecode slot, or -1 if not available
|
* @param slot valid bytecode slot, or -1 if not available
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setSlot(final int slot) {
|
public Symbol setSlot(final int slot) {
|
||||||
if (slot != this.slot) {
|
if (slot != this.slot) {
|
||||||
assert !isShared();
|
assert !isShared();
|
||||||
trace("SET SLOT " + slot);
|
trace("SET SLOT " + slot);
|
||||||
this.slot = slot;
|
this.slot = slot;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign a specific subclass of Object to the symbol
|
* Assign a specific subclass of Object to the symbol
|
||||||
*
|
*
|
||||||
* @param type the type
|
* @param type the type
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setType(final Class<?> type) {
|
public Symbol setType(final Class<?> type) {
|
||||||
assert !type.isPrimitive() && !Number.class.isAssignableFrom(type) : "Class<?> types can only be subclasses of object";
|
assert !type.isPrimitive() && !Number.class.isAssignableFrom(type) : "Class<?> types can only be subclasses of object";
|
||||||
setType(Type.typeFor(type));
|
setType(Type.typeFor(type));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign a type to the symbol
|
* Assign a type to the symbol
|
||||||
*
|
*
|
||||||
* @param type the type
|
* @param type the type
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setType(final Type type) {
|
public Symbol setType(final Type type) {
|
||||||
setTypeOverride(Type.widest(this.type, type));
|
setTypeOverride(Type.widest(this.type, type));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -690,15 +723,17 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
* constraint - otherwise a type can only be
|
* constraint - otherwise a type can only be
|
||||||
* widened
|
* widened
|
||||||
*
|
*
|
||||||
* @param type the type
|
* @param type the type
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public void setTypeOverride(final Type type) {
|
public Symbol setTypeOverride(final Type type) {
|
||||||
final Type old = this.type;
|
final Type old = this.type;
|
||||||
if (old != type) {
|
if (old != type) {
|
||||||
assert !isShared();
|
assert !isShared() : this + " is a shared symbol and cannot have its type overridden to " + type;
|
||||||
trace("TYPE CHANGE: " + old + "=>" + type + " == " + type);
|
trace("TYPE CHANGE: " + old + "=>" + type + " == " + type);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -711,8 +746,8 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
* be changed.
|
* be changed.
|
||||||
*/
|
*/
|
||||||
public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) {
|
public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) {
|
||||||
if(getSymbolType() != type) {
|
if (getSymbolType() != type) {
|
||||||
if(isShared()) {
|
if (isShared()) {
|
||||||
assert !hasSlot();
|
assert !hasSlot();
|
||||||
return ts.getTypedTemporarySymbol(type);
|
return ts.getTypedTemporarySymbol(type);
|
||||||
}
|
}
|
||||||
@ -728,14 +763,16 @@ public final class Symbol implements Comparable<Symbol> {
|
|||||||
* when flags need to be tagged, but block is in the
|
* when flags need to be tagged, but block is in the
|
||||||
* middle of evaluation and cannot be modified.
|
* middle of evaluation and cannot be modified.
|
||||||
*
|
*
|
||||||
* @param lc lexical context
|
* @param lc lexical context
|
||||||
* @param symbol symbol
|
* @param symbol symbol
|
||||||
|
* @return the symbol
|
||||||
*/
|
*/
|
||||||
public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
|
public static Symbol setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
|
||||||
symbol.setIsScope();
|
symbol.setIsScope();
|
||||||
if (!symbol.isGlobal()) {
|
if (!symbol.isGlobal()) {
|
||||||
lc.setBlockNeedsScope(lc.getDefiningBlock(symbol));
|
lc.setBlockNeedsScope(lc.getDefiningBlock(symbol));
|
||||||
}
|
}
|
||||||
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trace(final String desc) {
|
private void trace(final String desc) {
|
||||||
|
@ -28,8 +28,13 @@ package jdk.nashorn.internal.ir;
|
|||||||
import static jdk.nashorn.internal.parser.TokenType.BIT_NOT;
|
import static jdk.nashorn.internal.parser.TokenType.BIT_NOT;
|
||||||
import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
|
import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
|
||||||
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
|
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
|
||||||
|
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import jdk.nashorn.internal.codegen.types.Type;
|
import jdk.nashorn.internal.codegen.types.Type;
|
||||||
|
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||||
import jdk.nashorn.internal.parser.Token;
|
import jdk.nashorn.internal.parser.Token;
|
||||||
@ -39,9 +44,27 @@ import jdk.nashorn.internal.parser.TokenType;
|
|||||||
* UnaryNode nodes represent single operand operations.
|
* UnaryNode nodes represent single operand operations.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public final class UnaryNode extends Expression implements Assignment<Expression> {
|
public final class UnaryNode extends Expression implements Assignment<Expression>, Optimistic {
|
||||||
/** Right hand side argument. */
|
/** Right hand side argument. */
|
||||||
private final Expression rhs;
|
private final Expression expression;
|
||||||
|
|
||||||
|
private final int programPoint;
|
||||||
|
|
||||||
|
private final boolean isOptimistic;
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
private static final List<TokenType> CAN_OVERFLOW =
|
||||||
|
Collections.unmodifiableList(
|
||||||
|
Arrays.asList(new TokenType[] {
|
||||||
|
TokenType.ADD,
|
||||||
|
TokenType.SUB, //negate
|
||||||
|
TokenType.DECPREFIX,
|
||||||
|
TokenType.DECPOSTFIX,
|
||||||
|
TokenType.INCPREFIX,
|
||||||
|
TokenType.INCPOSTFIX,
|
||||||
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -56,20 +79,26 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param token token
|
* @param token token
|
||||||
* @param start start
|
* @param start start
|
||||||
* @param finish finish
|
* @param finish finish
|
||||||
* @param rhs expression
|
* @param expression expression
|
||||||
*/
|
*/
|
||||||
public UnaryNode(final long token, final int start, final int finish, final Expression rhs) {
|
public UnaryNode(final long token, final int start, final int finish, final Expression expression) {
|
||||||
super(token, start, finish);
|
super(token, start, finish);
|
||||||
this.rhs = rhs;
|
this.expression = expression;
|
||||||
|
this.programPoint = INVALID_PROGRAM_POINT;
|
||||||
|
this.isOptimistic = false;
|
||||||
|
this.type = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private UnaryNode(final UnaryNode unaryNode, final Expression rhs) {
|
private UnaryNode(final UnaryNode unaryNode, final Expression expression, final Type type, final int programPoint, final boolean isOptimistic) {
|
||||||
super(unaryNode);
|
super(unaryNode);
|
||||||
this.rhs = rhs;
|
this.expression = expression;
|
||||||
|
this.programPoint = programPoint;
|
||||||
|
this.isOptimistic = isOptimistic;
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,17 +126,23 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getWidestOperationType() {
|
public Type getWidestOperationType() {
|
||||||
return isAssignment() ? Type.NUMBER : Type.OBJECT;
|
switch (tokenType()) {
|
||||||
|
case ADD:
|
||||||
|
case SUB:
|
||||||
|
return Type.NUMBER;
|
||||||
|
default:
|
||||||
|
return isAssignment() ? Type.NUMBER : Type.OBJECT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Expression getAssignmentDest() {
|
public Expression getAssignmentDest() {
|
||||||
return isAssignment() ? rhs() : null;
|
return isAssignment() ? getExpression() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UnaryNode setAssignmentDest(Expression n) {
|
public UnaryNode setAssignmentDest(Expression n) {
|
||||||
return setRHS(n);
|
return setExpression(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -122,7 +157,7 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
@Override
|
@Override
|
||||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||||
if (visitor.enterUnaryNode(this)) {
|
if (visitor.enterUnaryNode(this)) {
|
||||||
return visitor.leaveUnaryNode(setRHS((Expression)rhs.accept(visitor)));
|
return visitor.leaveUnaryNode(setExpression((Expression)expression.accept(visitor)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -131,20 +166,20 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
@Override
|
@Override
|
||||||
public boolean isLocal() {
|
public boolean isLocal() {
|
||||||
switch (tokenType()) {
|
switch (tokenType()) {
|
||||||
case NEW:
|
case NEW:
|
||||||
return false;
|
return false;
|
||||||
case ADD:
|
case ADD:
|
||||||
case SUB:
|
case SUB:
|
||||||
case NOT:
|
case NOT:
|
||||||
case BIT_NOT:
|
case BIT_NOT:
|
||||||
return rhs.isLocal() && rhs.getType().isJSPrimitive();
|
return expression.isLocal() && expression.getType().isJSPrimitive();
|
||||||
case DECPOSTFIX:
|
case DECPOSTFIX:
|
||||||
case DECPREFIX:
|
case DECPREFIX:
|
||||||
case INCPOSTFIX:
|
case INCPOSTFIX:
|
||||||
case INCPREFIX:
|
case INCPREFIX:
|
||||||
return rhs instanceof IdentNode && rhs.isLocal() && rhs.getType().isJSPrimitive();
|
return expression instanceof IdentNode && expression.isLocal() && expression.getType().isJSPrimitive();
|
||||||
default:
|
default:
|
||||||
return rhs.isLocal();
|
return expression.isLocal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +188,7 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
toString(sb, new Runnable() {
|
toString(sb, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
sb.append(rhs().toString());
|
sb.append(getExpression().toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -166,20 +201,23 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
* when invoked.
|
* when invoked.
|
||||||
*/
|
*/
|
||||||
public void toString(final StringBuilder sb, final Runnable rhsStringBuilder) {
|
public void toString(final StringBuilder sb, final Runnable rhsStringBuilder) {
|
||||||
final TokenType type = tokenType();
|
final TokenType tokenType = tokenType();
|
||||||
final String name = type.getName();
|
final String name = tokenType.getName();
|
||||||
final boolean isPostfix = type == DECPOSTFIX || type == INCPOSTFIX;
|
final boolean isPostfix = tokenType == DECPOSTFIX || tokenType == INCPOSTFIX;
|
||||||
|
|
||||||
boolean rhsParen = type.needsParens(rhs().tokenType(), false);
|
if (isOptimistic()) {
|
||||||
|
sb.append(Node.OPT_IDENTIFIER);
|
||||||
|
}
|
||||||
|
boolean rhsParen = tokenType.needsParens(getExpression().tokenType(), false);
|
||||||
|
|
||||||
if (!isPostfix) {
|
if (!isPostfix) {
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
sb.append(type.name());
|
sb.append(tokenType.name());
|
||||||
rhsParen = true;
|
rhsParen = true;
|
||||||
} else {
|
} else {
|
||||||
sb.append(name);
|
sb.append(name);
|
||||||
|
|
||||||
if (type.ordinal() > BIT_NOT.ordinal()) {
|
if (tokenType.ordinal() > BIT_NOT.ordinal()) {
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,7 +232,7 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isPostfix) {
|
if (isPostfix) {
|
||||||
sb.append(type == DECPOSTFIX ? "--" : "++");
|
sb.append(tokenType == DECPOSTFIX ? "--" : "++");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,8 +244,8 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
*
|
*
|
||||||
* @return right hand side or expression node
|
* @return right hand side or expression node
|
||||||
*/
|
*/
|
||||||
public Expression rhs() {
|
public Expression getExpression() {
|
||||||
return rhs;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,13 +254,72 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
|||||||
*
|
*
|
||||||
* @see BinaryNode
|
* @see BinaryNode
|
||||||
*
|
*
|
||||||
* @param rhs right hand side or expression node
|
* @param expression right hand side or expression node
|
||||||
* @return a node equivalent to this one except for the requested change.
|
* @return a node equivalent to this one except for the requested change.
|
||||||
*/
|
*/
|
||||||
public UnaryNode setRHS(final Expression rhs) {
|
public UnaryNode setExpression(final Expression expression) {
|
||||||
if (this.rhs == rhs) {
|
if (this.expression == expression) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new UnaryNode(this, rhs);
|
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProgramPoint() {
|
||||||
|
return programPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnaryNode setProgramPoint(final int programPoint) {
|
||||||
|
if (this.programPoint == programPoint) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnaryNode setIsOptimistic(final boolean isOptimistic) {
|
||||||
|
if (this.isOptimistic == isOptimistic) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeOptimistic() {
|
||||||
|
return getMostOptimisticType() != getMostPessimisticType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostOptimisticType() {
|
||||||
|
if (CAN_OVERFLOW.contains(tokenType())) {
|
||||||
|
return Type.INT;
|
||||||
|
}
|
||||||
|
return getMostPessimisticType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getMostPessimisticType() {
|
||||||
|
return getWidestOperationType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOptimistic() {
|
||||||
|
//return hasType() && canBeOptimistic() && getType().narrowerThan(getMostPessimisticType());
|
||||||
|
return isOptimistic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return type == null ? super.getType() : type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnaryNode setType(TemporarySymbols ts, Type type) {
|
||||||
|
if (this.type == type) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new UnaryNode(this, expression, type, programPoint, isOptimistic);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VarNode setAssignmentDest(IdentNode n) {
|
public VarNode setAssignmentDest(final IdentNode n) {
|
||||||
return setName(n);
|
return setName(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,19 +108,27 @@ public final class ASTWriter {
|
|||||||
String type = clazz.getName();
|
String type = clazz.getName();
|
||||||
|
|
||||||
type = type.substring(type.lastIndexOf('.') + 1, type.length());
|
type = type.substring(type.lastIndexOf('.') + 1, type.length());
|
||||||
|
int truncate = type.indexOf("Node");
|
||||||
|
if (truncate == -1) {
|
||||||
|
truncate = type.indexOf("Statement");
|
||||||
|
}
|
||||||
|
if (truncate != -1) {
|
||||||
|
type = type.substring(0, truncate);
|
||||||
|
}
|
||||||
|
type = type.toLowerCase();
|
||||||
|
|
||||||
if (isReference) {
|
if (isReference) {
|
||||||
type = "ref: " + type;
|
type = "ref: " + type;
|
||||||
}
|
}
|
||||||
type += "@" + Debug.id(node);
|
|
||||||
final Symbol symbol;
|
final Symbol symbol;
|
||||||
if(node instanceof Expression) {
|
if (node instanceof Expression) {
|
||||||
symbol = ((Expression)node).getSymbol();
|
symbol = ((Expression)node).getSymbol();
|
||||||
} else {
|
} else {
|
||||||
symbol = null;
|
symbol = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol != null) {
|
if (symbol != null) {
|
||||||
type += "#" + symbol;
|
type += ">" + symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node instanceof Block && ((Block)node).needsScope()) {
|
if (node instanceof Block && ((Block)node).needsScope()) {
|
||||||
@ -160,6 +168,8 @@ public final class ASTWriter {
|
|||||||
status += " (" + tname + ")";
|
status += " (" + tname + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status += " @" + Debug.id(node);
|
||||||
|
|
||||||
if (children.isEmpty()) {
|
if (children.isEmpty()) {
|
||||||
sb.append("[").
|
sb.append("[").
|
||||||
append(type).
|
append(type).
|
||||||
@ -200,7 +210,7 @@ public final class ASTWriter {
|
|||||||
} else if (value instanceof Collection) {
|
} else if (value instanceof Collection) {
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
ASTWriter.indent(sb, indent + 1);
|
ASTWriter.indent(sb, indent + 1);
|
||||||
sb.append("[Collection ").
|
sb.append('[').
|
||||||
append(child.getName()).
|
append(child.getName()).
|
||||||
append("[0..").
|
append("[0..").
|
||||||
append(((Collection<Node>)value).size()).
|
append(((Collection<Node>)value).size()).
|
||||||
|
@ -91,7 +91,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
|
|||||||
final Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
|
final Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
|
||||||
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
|
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
|
||||||
try {
|
try {
|
||||||
final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
|
final FunctionNode functionNode = parser.parse(CompilerConstants.PROGRAM.symbolName());
|
||||||
functionNode.accept(jsonWriter);
|
functionNode.accept(jsonWriter);
|
||||||
return jsonWriter.getString();
|
return jsonWriter.getString();
|
||||||
} catch (final ParserException e) {
|
} catch (final ParserException e) {
|
||||||
@ -802,7 +802,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
|
|||||||
type("NewExpression");
|
type("NewExpression");
|
||||||
comma();
|
comma();
|
||||||
|
|
||||||
final CallNode callNode = (CallNode)unaryNode.rhs();
|
final CallNode callNode = (CallNode)unaryNode.getExpression();
|
||||||
property("callee");
|
property("callee");
|
||||||
callNode.getFunction().accept(this);
|
callNode.getFunction().accept(this);
|
||||||
comma();
|
comma();
|
||||||
@ -844,7 +844,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
|
|||||||
comma();
|
comma();
|
||||||
|
|
||||||
property("argument");
|
property("argument");
|
||||||
unaryNode.rhs().accept(this);
|
unaryNode.getExpression().accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return leave();
|
return leave();
|
||||||
|
@ -0,0 +1,552 @@
|
|||||||
|
/*
|
||||||
|
* 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.debug;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jdk.internal.org.objectweb.asm.Attribute;
|
||||||
|
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||||
|
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||||
|
import jdk.internal.org.objectweb.asm.Label;
|
||||||
|
import jdk.nashorn.internal.ir.debug.NashornTextifier.NashornLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass of the ASM classs reader that retains more info, such
|
||||||
|
* as bytecode offsets
|
||||||
|
*/
|
||||||
|
public class NashornClassReader extends ClassReader {
|
||||||
|
|
||||||
|
private final Map<String, List<Label>> labelMap = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param bytecode bytecode for class
|
||||||
|
*/
|
||||||
|
public NashornClassReader(final byte[] bytecode) {
|
||||||
|
super(bytecode);
|
||||||
|
parse(bytecode);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Label> getExtraLabels(final String className, final String methodName, final String methodDesc) {
|
||||||
|
final String key = fullyQualifiedName(className, methodName, methodDesc);
|
||||||
|
return labelMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readByte(final byte[] bytecode, int index) {
|
||||||
|
return (byte)(bytecode[index] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readShort(final byte[] bytecode, int index) {
|
||||||
|
return (short)((bytecode[index] & 0xff) << 8) | (bytecode[index + 1] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readInt(final byte[] bytecode, int index) {
|
||||||
|
return ((bytecode[index] & 0xff) << 24) | ((bytecode[index + 1] & 0xff) << 16) | ((bytecode[index + 2] & 0xff) << 8) | (bytecode[index + 3] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long readLong(final byte[] bytecode, int index) {
|
||||||
|
int hi = readInt(bytecode, index);
|
||||||
|
int lo = readInt(bytecode, index + 4);
|
||||||
|
return ((long)hi << 32) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readUTF(int index, final int utfLen, final byte[] bytecode) {
|
||||||
|
int endIndex = index + utfLen;
|
||||||
|
char buf[] = new char[utfLen * 2];
|
||||||
|
int strLen = 0;
|
||||||
|
int c;
|
||||||
|
int st = 0;
|
||||||
|
char cc = 0;
|
||||||
|
int i = index;
|
||||||
|
|
||||||
|
while (i < endIndex) {
|
||||||
|
c = bytecode[i++];
|
||||||
|
switch (st) {
|
||||||
|
case 0:
|
||||||
|
c = c & 0xFF;
|
||||||
|
if (c < 0x80) { // 0xxxxxxx
|
||||||
|
buf[strLen++] = (char) c;
|
||||||
|
} else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
|
||||||
|
cc = (char) (c & 0x1F);
|
||||||
|
st = 1;
|
||||||
|
} else { // 1110 xxxx 10xx xxxx 10xx xxxx
|
||||||
|
cc = (char) (c & 0x0F);
|
||||||
|
st = 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char
|
||||||
|
buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
|
||||||
|
st = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // byte 2 of 3-byte char
|
||||||
|
cc = (char) ((cc << 6) | (c & 0x3F));
|
||||||
|
st = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new String(buf, 0, strLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parse(final byte[] bytecode) {
|
||||||
|
String thisClassName;
|
||||||
|
|
||||||
|
int u = 0;
|
||||||
|
|
||||||
|
int magic = readInt(bytecode, u);
|
||||||
|
u += 4; //magic
|
||||||
|
assert magic == 0xcafebabe : Integer.toHexString(magic);
|
||||||
|
readShort(bytecode, u); //minor
|
||||||
|
u += 2;
|
||||||
|
readShort(bytecode, u); //major
|
||||||
|
u += 2; //minor
|
||||||
|
|
||||||
|
int cpc = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
ArrayList<Constant> cp = new ArrayList<>(cpc);
|
||||||
|
cp.add(null);
|
||||||
|
|
||||||
|
for (int i = 1; i < cpc; i++) {
|
||||||
|
//constant pool entries
|
||||||
|
final int tag = readByte(bytecode, u);
|
||||||
|
u += 1;
|
||||||
|
switch (tag) {
|
||||||
|
case 7: //class
|
||||||
|
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
|
||||||
|
u += 2;
|
||||||
|
break;
|
||||||
|
case 9: //fieldref
|
||||||
|
case 10: //methodref
|
||||||
|
case 11: //interfacemethodref
|
||||||
|
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
|
||||||
|
u += 4;
|
||||||
|
break;
|
||||||
|
case 8: //string
|
||||||
|
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u))); //string index
|
||||||
|
u += 2;
|
||||||
|
break;
|
||||||
|
case 3: //int
|
||||||
|
cp.add(new DirectInfo<>(cp, tag, readInt(bytecode, u)));
|
||||||
|
u += 4;
|
||||||
|
break;
|
||||||
|
case 4: //float
|
||||||
|
cp.add(new DirectInfo<>(cp, tag, Float.intBitsToFloat(readInt(bytecode, u))));
|
||||||
|
u += 4;
|
||||||
|
break;
|
||||||
|
case 5: //long
|
||||||
|
cp.add(new DirectInfo<>(cp, tag, readLong(bytecode, u)));
|
||||||
|
cp.add(null);
|
||||||
|
i++;
|
||||||
|
u += 8;
|
||||||
|
break;
|
||||||
|
case 6: //double
|
||||||
|
cp.add(new DirectInfo<>(cp, tag, Double.longBitsToDouble(readLong(bytecode, u))));
|
||||||
|
cp.add(null);
|
||||||
|
i++;
|
||||||
|
u += 8;
|
||||||
|
break;
|
||||||
|
case 12: //name and type
|
||||||
|
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
|
||||||
|
u += 4;
|
||||||
|
break;
|
||||||
|
case 1: //utf8
|
||||||
|
int len = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
cp.add(new DirectInfo<>(cp, tag, readUTF(u, len, bytecode)));
|
||||||
|
u += len;
|
||||||
|
break;
|
||||||
|
case 16: //methodtype
|
||||||
|
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
|
||||||
|
u += 2;
|
||||||
|
break;
|
||||||
|
case 18: //indy
|
||||||
|
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)) {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "#" + index + ' ' + cp.get(index2).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
u += 4;
|
||||||
|
break;
|
||||||
|
case 15: //methodhandle
|
||||||
|
int kind = readByte(bytecode, u);
|
||||||
|
assert kind >= 1 && kind <= 9 : kind;
|
||||||
|
cp.add(new IndexInfo2(cp, tag, kind, readShort(bytecode, u + 1)) {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "#" + index + ' ' + cp.get(index2).toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
u += 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert false : tag;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readShort(bytecode, u); //access flags
|
||||||
|
u += 2; //access
|
||||||
|
int cls = readShort(bytecode, u);
|
||||||
|
u += 2; //this_class
|
||||||
|
thisClassName = cp.get(cls).toString();
|
||||||
|
u += 2; //super
|
||||||
|
|
||||||
|
int ifc = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
u += ifc * 2;
|
||||||
|
|
||||||
|
int fc = readShort(bytecode, u);
|
||||||
|
u += 2; //fields
|
||||||
|
|
||||||
|
for (int i = 0 ; i < fc ; i++) {
|
||||||
|
u += 2; //access
|
||||||
|
readShort(bytecode, u); //fieldname
|
||||||
|
u += 2; //name
|
||||||
|
u += 2; //descriptor
|
||||||
|
int ac = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
//field attributes
|
||||||
|
for (int j = 0; j < ac; j++) {
|
||||||
|
u += 2; //attribute name
|
||||||
|
int len = readInt(bytecode, u);
|
||||||
|
u += 4;
|
||||||
|
u += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mc = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
for (int i = 0 ; i < mc ; i++) {
|
||||||
|
readShort(bytecode, u);
|
||||||
|
u += 2; //access
|
||||||
|
|
||||||
|
int methodNameIndex = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
final String methodName = cp.get(methodNameIndex).toString();
|
||||||
|
|
||||||
|
int methodDescIndex = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
final String methodDesc = cp.get(methodDescIndex).toString();
|
||||||
|
|
||||||
|
int ac = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
|
||||||
|
//method attributes
|
||||||
|
for (int j = 0; j < ac; j++) {
|
||||||
|
int nameIndex = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
String attrName = cp.get(nameIndex).toString();
|
||||||
|
|
||||||
|
int attrLen = readInt(bytecode, u);
|
||||||
|
u += 4;
|
||||||
|
|
||||||
|
if ("Code".equals(attrName)) {
|
||||||
|
readShort(bytecode, u);
|
||||||
|
u += 2; //max stack
|
||||||
|
readShort(bytecode, u);
|
||||||
|
u += 2; //max locals
|
||||||
|
int len = readInt(bytecode, u);
|
||||||
|
u += 4;
|
||||||
|
parseCode(bytecode, u, len, fullyQualifiedName(thisClassName, methodName, methodDesc));
|
||||||
|
u += len;
|
||||||
|
int elen = readShort(bytecode, u); //exception table length
|
||||||
|
u += 2;
|
||||||
|
u += elen * 8;
|
||||||
|
|
||||||
|
//method attributes
|
||||||
|
int ac2 = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
for (int k = 0; k < ac2; k++) {
|
||||||
|
u += 2; //name;
|
||||||
|
int aclen = readInt(bytecode, u);
|
||||||
|
u += 4; //length
|
||||||
|
u += aclen; //bytes;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
u += attrLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ac = readShort(bytecode, u);
|
||||||
|
u += 2;
|
||||||
|
//other attributes
|
||||||
|
for (int i = 0 ; i < ac ; i++) {
|
||||||
|
readShort(bytecode, u); //name index
|
||||||
|
u += 2;
|
||||||
|
int len = readInt(bytecode, u);
|
||||||
|
u += 4;
|
||||||
|
u += len;
|
||||||
|
//attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
return thisClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fullyQualifiedName(final String className, final String methodName, final String methodDesc) {
|
||||||
|
return className + '.' + methodName + methodDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseCode(final byte[] bytecode, final int index, final int len, final String desc) {
|
||||||
|
final List<Label> labels = new ArrayList<>();
|
||||||
|
labelMap.put(desc, labels);
|
||||||
|
|
||||||
|
boolean wide = false;
|
||||||
|
|
||||||
|
for (int i = index; i < index + len;) {
|
||||||
|
int opcode = bytecode[i];
|
||||||
|
labels.add(new NashornLabel(opcode, i - index));
|
||||||
|
|
||||||
|
switch (opcode & 0xff) {
|
||||||
|
case 0xc4: //wide
|
||||||
|
wide = true;
|
||||||
|
i += 1;
|
||||||
|
break;
|
||||||
|
case 0xa9: //ret
|
||||||
|
i += wide ? 4 : 2;
|
||||||
|
break;
|
||||||
|
case 0xab: //lookupswitch
|
||||||
|
i += 1;
|
||||||
|
while (((i - index) & 3) != 0) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
readInt(bytecode, i);
|
||||||
|
i += 4; //defaultbyte
|
||||||
|
int npairs = readInt(bytecode, i);
|
||||||
|
i += 4;
|
||||||
|
i += 8 * npairs;
|
||||||
|
break;
|
||||||
|
case 0xaa: //tableswitch
|
||||||
|
i += 1;
|
||||||
|
while (((i - index) & 3) != 0) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
readInt(bytecode, i); //default
|
||||||
|
i += 4;
|
||||||
|
int lo = readInt(bytecode, i);
|
||||||
|
i += 4;
|
||||||
|
int hi = readInt(bytecode, i);
|
||||||
|
i += 4;
|
||||||
|
i += 4 * (hi - lo + 1);
|
||||||
|
break;
|
||||||
|
case 0xc5: //multianewarray
|
||||||
|
i += 4;
|
||||||
|
break;
|
||||||
|
case 0x19: //aload (wide)
|
||||||
|
case 0x18: //dload
|
||||||
|
case 0x17: //fload
|
||||||
|
case 0x15: //iload
|
||||||
|
case 0x16: //lload
|
||||||
|
case 0x3a: //astore wide
|
||||||
|
case 0x39: //dstore
|
||||||
|
case 0x38: //fstore
|
||||||
|
case 0x36: //istore
|
||||||
|
case 0x37: //lstore
|
||||||
|
i += wide ? 3 : 2;
|
||||||
|
break;
|
||||||
|
case 0x10: //bipush
|
||||||
|
case 0x12: //ldc
|
||||||
|
case 0xbc: //anewarrayu
|
||||||
|
i += 2;
|
||||||
|
break;
|
||||||
|
case 0xb4: //getfield
|
||||||
|
case 0xb2: //getstatic
|
||||||
|
case 0xbd: //anewarray
|
||||||
|
case 0xc0: //checkcast
|
||||||
|
case 0xa5: //ifacmp_eq
|
||||||
|
case 0xa6: //ifacmp_ne
|
||||||
|
case 0x9f: //all ifs and ifcmps
|
||||||
|
case 0xa0:
|
||||||
|
case 0xa1:
|
||||||
|
case 0xa2:
|
||||||
|
case 0xa3:
|
||||||
|
case 0xa4:
|
||||||
|
case 0x99:
|
||||||
|
case 0x9a:
|
||||||
|
case 0x9b:
|
||||||
|
case 0x9c:
|
||||||
|
case 0x9d:
|
||||||
|
case 0x9e:
|
||||||
|
case 0xc7:
|
||||||
|
case 0xc6:
|
||||||
|
case 0xc1: //instanceof
|
||||||
|
case 0xa7: //goto
|
||||||
|
case 0xb7: //special
|
||||||
|
case 0xb8: //static
|
||||||
|
case 0xb6: //virtual
|
||||||
|
case 0xa8: //jsr
|
||||||
|
case 0x13: //ldc_w
|
||||||
|
case 0x14: //ldc2_w
|
||||||
|
case 0xbb: //new
|
||||||
|
case 0xb5: //putfield
|
||||||
|
case 0xb3: //putstatic
|
||||||
|
case 0x11: //sipush
|
||||||
|
i += 3;
|
||||||
|
break;
|
||||||
|
case 0x84: //iinc (wide)
|
||||||
|
i += wide ? 5 : 3;
|
||||||
|
break;
|
||||||
|
case 0xba: //indy
|
||||||
|
case 0xb9: //interface
|
||||||
|
case 0xc8:
|
||||||
|
case 0xc9: //jsr_w
|
||||||
|
i += 5; //goto_w
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wide) {
|
||||||
|
wide = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(final ClassVisitor classVisitor, Attribute[] attrs, final int flags) {
|
||||||
|
super.accept(classVisitor, attrs, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Label readLabel(final int offset, final Label[] labels) {
|
||||||
|
Label label = super.readLabel(offset, labels);
|
||||||
|
label.info = (int)offset;
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static class Constant {
|
||||||
|
protected ArrayList<Constant> cp;
|
||||||
|
protected int tag;
|
||||||
|
protected Constant(final ArrayList<Constant> cp, int tag) {
|
||||||
|
this.cp = cp;
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
final String getType() {
|
||||||
|
String str = type[tag];
|
||||||
|
while (str.length() < 16) {
|
||||||
|
str += " ";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class IndexInfo extends Constant {
|
||||||
|
protected final int index;
|
||||||
|
|
||||||
|
IndexInfo(final ArrayList<Constant> cp, int tag, int index) {
|
||||||
|
super(cp, tag);
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return cp.get(index).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class IndexInfo2 extends IndexInfo {
|
||||||
|
protected final int index2;
|
||||||
|
|
||||||
|
IndexInfo2(final ArrayList<Constant> cp, int tag, int index, int index2) {
|
||||||
|
super(cp, tag, index);
|
||||||
|
this.index2 = index2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + ' ' + cp.get(index2).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DirectInfo<T> extends Constant {
|
||||||
|
protected final T info;
|
||||||
|
|
||||||
|
DirectInfo(final ArrayList<Constant> cp, int tag, T info) {
|
||||||
|
super(cp, tag);
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return info.toString();// + " [class=" + info.getClass().getSimpleName() + ']';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String type[] = {
|
||||||
|
//0
|
||||||
|
"<error>",
|
||||||
|
//1
|
||||||
|
"UTF8",
|
||||||
|
//2
|
||||||
|
"<error>",
|
||||||
|
//3
|
||||||
|
"Integer",
|
||||||
|
//4
|
||||||
|
"Float",
|
||||||
|
//5
|
||||||
|
"Long",
|
||||||
|
//6
|
||||||
|
"Double",
|
||||||
|
//7
|
||||||
|
"Class",
|
||||||
|
//8
|
||||||
|
"String",
|
||||||
|
//9
|
||||||
|
"Fieldref",
|
||||||
|
//10
|
||||||
|
"Methodref",
|
||||||
|
//11
|
||||||
|
"InterfaceMethodRef",
|
||||||
|
//12
|
||||||
|
"NameAndType",
|
||||||
|
//13
|
||||||
|
"<error>",
|
||||||
|
//14
|
||||||
|
"<error>",
|
||||||
|
//15
|
||||||
|
"MethodHandle",
|
||||||
|
//16
|
||||||
|
"MethodType",
|
||||||
|
//17
|
||||||
|
"<error>",
|
||||||
|
//18
|
||||||
|
"Invokedynamic"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
1224
nashorn/src/jdk/nashorn/internal/ir/debug/NashornTextifier.java
Normal file
1224
nashorn/src/jdk/nashorn/internal/ir/debug/NashornTextifier.java
Normal file
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…
Reference in New Issue
Block a user