diff --git a/nashorn/.hgignore b/nashorn/.hgignore
index 6d68c1d476d..6cfabad507b 100644
--- a/nashorn/.hgignore
+++ b/nashorn/.hgignore
@@ -13,6 +13,8 @@ webrev.zip
*.clazz
*.log
*.orig
+*.rej
+*~
genfiles.properties
hotspot.log
.DS_Store*
diff --git a/nashorn/bin/rundiff.sh b/nashorn/bin/rundiff.sh
new file mode 100644
index 00000000000..a672104ae7c
--- /dev/null
+++ b/nashorn/bin/rundiff.sh
@@ -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
diff --git a/nashorn/bin/runnormal.sh b/nashorn/bin/runnormal.sh
new file mode 100644
index 00000000000..4949c6d98c5
--- /dev/null
+++ b/nashorn/bin/runnormal.sh
@@ -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 ${@}
diff --git a/nashorn/bin/runnormaldual.sh b/nashorn/bin/runnormaldual.sh
new file mode 100644
index 00000000000..dc2b970acd0
--- /dev/null
+++ b/nashorn/bin/runnormaldual.sh
@@ -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 ${@}
diff --git a/nashorn/bin/runopt.sh b/nashorn/bin/runopt.sh
new file mode 100644
index 00000000000..7c5f6ced6a2
--- /dev/null
+++ b/nashorn/bin/runopt.sh
@@ -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 ${@}
diff --git a/nashorn/bin/runoptdual.sh b/nashorn/bin/runoptdual.sh
new file mode 100644
index 00000000000..825b9410e11
--- /dev/null
+++ b/nashorn/bin/runoptdual.sh
@@ -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 ${@}
diff --git a/nashorn/bin/runoptdualcatch.sh b/nashorn/bin/runoptdualcatch.sh
new file mode 100644
index 00000000000..2553820312b
--- /dev/null
+++ b/nashorn/bin/runoptdualcatch.sh
@@ -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 ${@}
+
diff --git a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
index 479d1d31f21..a8d6ae2b981 100644
--- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
+++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
@@ -413,7 +413,8 @@ public class MethodGenerator extends MethodVisitor {
super.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
- "(Ljava/lang/String;)V", false);
+ "(Ljava/lang/String;)V",
+ false);
}
// print the object on the top of the stack
@@ -426,6 +427,7 @@ public class MethodGenerator extends MethodVisitor {
super.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
- "(Ljava/lang/Object;)V", false);
+ "(Ljava/lang/Object;)V",
+ false);
}
}
diff --git a/nashorn/make/build.xml b/nashorn/make/build.xml
index 78a713bfc46..883f7451e92 100644
--- a/nashorn/make/build.xml
+++ b/nashorn/make/build.xml
@@ -365,18 +365,6 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
-
-
-
-
-
-
-
-
-
-
-
-
@@ -467,6 +455,28 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nashorn/make/nbproject/ide-targets.xml b/nashorn/make/nbproject/ide-targets.xml
index 70b3e68fcb7..a592cff6a07 100644
--- a/nashorn/make/nbproject/ide-targets.xml
+++ b/nashorn/make/nbproject/ide-targets.xml
@@ -31,9 +31,10 @@
+
-
+
diff --git a/nashorn/make/project.properties b/nashorn/make/project.properties
index d51e72e4180..23e2cc1918d 100644
--- a/nashorn/make/project.properties
+++ b/nashorn/make/project.properties
@@ -175,7 +175,7 @@ octane-test-sys-prop.test.js.exclude.list=\
mandreel.js
# 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
sunspider-test-sys-prop.test.js.framework=${test.basic.dir}/runsunspider.js
@@ -258,20 +258,29 @@ run.test.classpath=\
src.dir=src
test.src.dir=test/src
-# -Xmx is used for all tests, -Xms only for octane benchmark
run.test.xmx=3G
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.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:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
# 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
run.test.jvmargs.octane.main=${run.test.jvmargs.common}
diff --git a/nashorn/src/jdk/internal/dynalink/DynamicLinker.java b/nashorn/src/jdk/internal/dynalink/DynamicLinker.java
index 155ff309d01..545bb1c46b5 100644
--- a/nashorn/src/jdk/internal/dynalink/DynamicLinker.java
+++ b/nashorn/src/jdk/internal/dynalink/DynamicLinker.java
@@ -140,7 +140,6 @@ import jdk.internal.dynalink.support.RuntimeContextLinkRequestImpl;
* @author Attila Szegedi
*/
public class DynamicLinker {
-
private static final String CLASS_NAME = DynamicLinker.class.getName();
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 final LinkerServices linkerServices;
+ private final GuardedInvocationFilter prelinkFilter;
private final int runtimeContextArgCount;
private final boolean syncOnRelink;
private final int unstableRelinkThreshold;
@@ -156,18 +156,20 @@ public class DynamicLinker {
* Creates a new dynamic linker.
*
* @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)}
*/
- DynamicLinker(LinkerServices linkerServices, int runtimeContextArgCount, boolean syncOnRelink,
- int unstableRelinkThreshold) {
+ DynamicLinker(LinkerServices linkerServices, GuardedInvocationFilter prelinkFilter, int runtimeContextArgCount,
+ boolean syncOnRelink, int unstableRelinkThreshold) {
if(runtimeContextArgCount < 0) {
throw new IllegalArgumentException("runtimeContextArgCount < 0");
}
if(unstableRelinkThreshold < 0) {
throw new IllegalArgumentException("unstableRelinkThreshold < 0");
}
- this.runtimeContextArgCount = runtimeContextArgCount;
this.linkerServices = linkerServices;
+ this.prelinkFilter = prelinkFilter;
+ this.runtimeContextArgCount = runtimeContextArgCount;
this.syncOnRelink = syncOnRelink;
this.unstableRelinkThreshold = unstableRelinkThreshold;
}
@@ -224,11 +226,10 @@ public class DynamicLinker {
final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0;
final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
final LinkRequest linkRequest =
- runtimeContextArgCount == 0 ? new LinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments)
- : new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments,
- runtimeContextArgCount);
+ runtimeContextArgCount == 0 ?
+ new LinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments) :
+ new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments, runtimeContextArgCount);
- // Find a suitable method handle with a guard
GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);
// 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;
// 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
diff --git a/nashorn/src/jdk/internal/dynalink/DynamicLinkerFactory.java b/nashorn/src/jdk/internal/dynalink/DynamicLinkerFactory.java
index 72fbebe33be..04efac8be47 100644
--- a/nashorn/src/jdk/internal/dynalink/DynamicLinkerFactory.java
+++ b/nashorn/src/jdk/internal/dynalink/DynamicLinkerFactory.java
@@ -102,14 +102,15 @@ import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
import jdk.internal.dynalink.support.CompositeGuardingDynamicLinker;
import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
+import jdk.internal.dynalink.support.DefaultPrelinkFilter;
import jdk.internal.dynalink.support.LinkerServicesImpl;
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
* 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 DynamicLinker} documentation for tips on how to use this class.
+ * {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker} and a
+ * {@link DefaultPrelinkFilter}. See {@link DynamicLinker} documentation for tips on how to use this class.
*
* @author Attila Szegedi
*/
@@ -128,6 +129,7 @@ public class DynamicLinkerFactory {
private int runtimeContextArgCount = 0;
private boolean syncOnRelink = false;
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
@@ -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
*/
@@ -306,8 +320,12 @@ public class DynamicLinkerFactory {
}
}
+ if(prelinkFilter == null) {
+ prelinkFilter = new DefaultPrelinkFilter();
+ }
+
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
- runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
+ prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
}
private static ClassLoader getThreadContextClassLoader() {
diff --git a/nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java b/nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java
new file mode 100644
index 00000000000..8560d82f68d
--- /dev/null
+++ b/nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java
@@ -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);
+}
diff --git a/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java b/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
index a6ef8c11b90..d798221c82b 100644
--- a/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
+++ b/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
@@ -97,7 +97,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
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.Guards;
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
@@ -459,12 +459,16 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
LinkerServices linkerServices, List operations) throws Exception {
- final MethodType type = callSiteDescriptor.getMethodType();
switch(callSiteDescriptor.getNameTokenCount()) {
case 2: {
// Must have three arguments: target object, property name, and property value.
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:
// foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
// get_setter_handle(type, linkerServices))
@@ -473,8 +477,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// component's invocation.
// 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.
- // We want setters that conform to "R(O, V)"
+ // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
+ // Object return type).
final MethodType setterType = type.dropParameterTypes(1, 2);
// Bind property setter handle to the expected setter type and linker services. Type is
// MethodHandle(Object, String, Object)
@@ -495,11 +499,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
final MethodHandle fallbackFolded;
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,
type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
} 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
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
0, MethodHandle.class);
@@ -545,9 +549,12 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
LinkerServices linkerServices, List ops) throws Exception {
- final MethodType type = callSiteDescriptor.getMethodType();
switch(callSiteDescriptor.getNameTokenCount()) {
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
assertParameterCount(callSiteDescriptor, 2);
@@ -563,11 +570,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
callSiteBoundMethodGetter);
- // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
+ // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
// 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,
type.parameterType(1));
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
@@ -575,17 +582,19 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
final MethodHandle fallbackFolded;
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,
type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
} else {
- // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
- // extra argument resulting from fold
- fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
- 0, AnnotatedDynamicMethod.class);
+ // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
+ // drop the extra argument resulting from fold and to change its return type to Object.
+ final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
+ 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(
IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
if(nextComponent == null) {
@@ -612,8 +621,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// value is null.
final ValidationType validationType = annGetter.validationType;
// TODO: we aren't using the type that declares the most generic getter here!
- return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
- type), clazz, validationType);
+ return new GuardedInvocationComponent(getter, getGuard(validationType,
+ callSiteDescriptor.getMethodType()), clazz, validationType);
}
default: {
// 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(),
- MethodType.methodType(boolean.class, DynamicMethod.class));
- private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
+ private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
+ MethodType.methodType(boolean.class, Object.class));
+ private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
LinkerServices linkerServices, List 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()) {
case 2: {
// Must have exactly two arguments: receiver and name
assertParameterCount(callSiteDescriptor, 2);
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
linkerServices, ops);
- if(nextComponent == null) {
- // No next component operation; just return a component for this operation.
+ if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
+ 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);
}
@@ -665,21 +678,20 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// 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.
- final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
- DynamicMethod.class));
+ final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
// 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(
- DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
- DynamicMethod.class));
+ OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
- // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
- assert nextComponentInvocation.type().equals(type);
+ // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
+ // 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.
final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
- DynamicMethod.class);
+ Object.class);
// Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
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);
}
@@ -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,
// we'll always return it at this point.
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: {
// 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) {
if(descriptor.getMethodType().parameterCount() != paramCount) {
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(
- "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);
@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);
}
diff --git a/nashorn/src/jdk/internal/dynalink/beans/BeanLinker.java b/nashorn/src/jdk/internal/dynalink/beans/BeanLinker.java
index 519036b37cc..6e7b22a45df 100644
--- a/nashorn/src/jdk/internal/dynalink/beans/BeanLinker.java
+++ b/nashorn/src/jdk/internal/dynalink/beans/BeanLinker.java
@@ -235,8 +235,9 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
} else {
checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
}
- return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
- binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
+ final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
+ nextComponent.getGuardedInvocation().getInvocation());
+ return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
gic.getValidatorClass(), gic.getValidationType());
}
@@ -306,7 +307,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
}
/*private*/ MethodHandle bind(MethodHandle handle) {
- return bindToFixedKey(linkerServices.asType(handle, methodType));
+ return bindToFixedKey(linkerServices.asTypeLosslessReturn(handle, methodType));
}
/*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 :
RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
- return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
- binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
+ final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
+ nextComponent.getGuardedInvocation().getInvocation());
+ return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
gic.getValidatorClass(), gic.getValidationType());
}
diff --git a/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java b/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
index 8ce41bc658c..de74d81c378 100644
--- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
+++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
@@ -148,7 +148,6 @@ class OverloadedDynamicMethod extends DynamicMethod {
}
}
- @SuppressWarnings("fallthrough")
@Override
public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
final MethodType callSiteType = callSiteDescriptor.getMethodType();
@@ -207,7 +206,7 @@ class OverloadedDynamicMethod extends DynamicMethod {
case 1: {
// 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.
- invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
+ return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
}
default: {
// We have more than one candidate. We have no choice but to link to a method that resolves overloads on
diff --git a/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java b/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java
index f711489b037..a477a280d5a 100644
--- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java
+++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java
@@ -93,6 +93,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.dynalink.linker.LinkerServices;
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
@@ -114,13 +115,15 @@ class OverloadedMethod {
OverloadedMethod(List methodHandles, OverloadedDynamicMethod parent, MethodType callSiteType,
LinkerServices linkerServices) {
this.parent = parent;
- this.callSiteType = callSiteType;
+ final Class> commonRetType = getCommonReturnType(methodHandles);
+ this.callSiteType = callSiteType.changeReturnType(commonRetType);
this.linkerServices = linkerServices;
fixArgMethods = new ArrayList<>(methodHandles.size());
varArgMethods = new ArrayList<>(methodHandles.size());
final int argNum = callSiteType.parameterCount();
for(MethodHandle mh: methodHandles) {
+ mh = mh.asType(mh.type().changeReturnType(commonRetType));
if(mh.isVarargsCollector()) {
final MethodHandle asFixed = mh.asFixedArity();
if(argNum == asFixed.type().parameterCount()) {
@@ -137,7 +140,7 @@ class OverloadedMethod {
final MethodHandle bound = SELECT_METHOD.bindTo(this);
final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
callSiteType.changeReturnType(MethodHandle.class));
- invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(callSiteType), collecting);
+ invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(this.callSiteType), collecting);
}
MethodHandle getInvoker() {
@@ -262,4 +265,13 @@ class OverloadedMethod {
b.append(classes[l - 1].getComponentType().getCanonicalName()).append("...");
}
}
+
+ private static Class> getCommonReturnType(List methodHandles) {
+ final Iterator it = methodHandles.iterator();
+ Class> retType = it.next().type().returnType();
+ while(it.hasNext()) {
+ retType = TypeUtilities.getCommonLosslessConversionType(retType, it.next().type().returnType());
+ }
+ return retType;
+ }
}
diff --git a/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java b/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
index 6b55d81f15c..88e64a80bf5 100644
--- a/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
+++ b/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
@@ -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
* 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 callSiteType the type of the call site
* @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,
final LinkerServices linkerServices, final MethodType callSiteType) {
- return linkerServices.asType(sizedMethod, callSiteType);
+ return linkerServices.asTypeLosslessReturn(sizedMethod, callSiteType);
}
private static boolean typeMatchesDescription(String paramTypes, MethodType type) {
diff --git a/nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java b/nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java
index 26a741e8288..bdbf555953d 100644
--- a/nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java
+++ b/nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java
@@ -90,7 +90,9 @@ import java.lang.invoke.SwitchPoint;
import java.lang.invoke.WrongMethodTypeException;
import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.support.CatchExceptionCombinator;
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
@@ -102,10 +104,23 @@ import jdk.internal.dynalink.support.Guards;
* @author Attila Szegedi
*/
public class GuardedInvocation {
+ private static final boolean USE_FAST_REWRITE = Options.getBooleanProperty("nashorn.fastrewrite");
+
private final MethodHandle invocation;
private final MethodHandle guard;
+ private final Class extends Throwable> exception;
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.
*
@@ -116,7 +131,18 @@ public class GuardedInvocation {
* @throws NullPointerException if invocation is null.
*/
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.
*/
public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
- invocation.getClass(); // NPE check
- this.invocation = invocation;
- this.guard = guard;
- this.switchPoint = switchPoint;
+ this(invocation, guard, switchPoint, 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.
* @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
* 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.
*/
- public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint, MethodHandle guard) {
- this(invocation, guard, switchPoint);
+ public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint, Class extends Throwable> exception) {
+ invocation.getClass(); // NPE check
+ this.invocation = invocation;
+ this.guard = guard;
+ this.switchPoint = switchPoint;
+ this.exception = exception;
}
+
/**
* Returns the invocation method handle.
*
@@ -176,6 +206,15 @@ public class GuardedInvocation {
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.
* @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.
*/
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) {
@@ -240,6 +279,20 @@ public class GuardedInvocation {
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
* 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) {
final MethodHandle guarded =
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) {
if(!mh.type().equals(type)) {
throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
diff --git a/nashorn/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java b/nashorn/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java
index 2cd0d0f04d1..82a361970ed 100644
--- a/nashorn/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java
+++ b/nashorn/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java
@@ -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
* 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 can also have a switch point for asynchronous invalidation of the linkage. 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.
+ * invocation can also have a switch point for asynchronous invalidation of the linkage, as well as a
+ * {@link Throwable} subclass that describes an expected exception condition that also triggers relinking (often it
+ * is faster to rely on an infrequent but expected {@link ClassCastException} than on an always evaluated
+ * {@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
*/
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices)
diff --git a/nashorn/src/jdk/internal/dynalink/linker/LinkRequest.java b/nashorn/src/jdk/internal/dynalink/linker/LinkRequest.java
index 1a82a4fab7b..83a128ee71c 100644
--- a/nashorn/src/jdk/internal/dynalink/linker/LinkRequest.java
+++ b/nashorn/src/jdk/internal/dynalink/linker/LinkRequest.java
@@ -100,6 +100,17 @@ public interface LinkRequest {
*/
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
* affect the arguments in this request.
diff --git a/nashorn/src/jdk/internal/dynalink/linker/LinkerServices.java b/nashorn/src/jdk/internal/dynalink/linker/LinkerServices.java
index deaf820f11b..1f8a678b48c 100644
--- a/nashorn/src/jdk/internal/dynalink/linker/LinkerServices.java
+++ b/nashorn/src/jdk/internal/dynalink/linker/LinkerServices.java
@@ -87,7 +87,9 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.DynamicLinker;
+import jdk.internal.dynalink.DynamicLinkerFactory;
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
@@ -103,17 +105,33 @@ public interface LinkerServices {
* 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,
* 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
- * the return type.
+ * provided by {@link GuardingTypeConverterFactory} implementations.
*
* @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.
+ * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
+ * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}, and
+ * {@link MethodHandles#filterReturnValue(MethodHandle, MethodHandle)} with
+ * {@link GuardingTypeConverterFactory}-produced type converters as filters.
*/
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
* 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.
*/
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));
+ }
+ }
}
diff --git a/nashorn/src/jdk/internal/dynalink/support/CatchExceptionCombinator.java b/nashorn/src/jdk/internal/dynalink/support/CatchExceptionCombinator.java
new file mode 100644
index 00000000000..0aa5ac9f16d
--- /dev/null
+++ b/nashorn/src/jdk/internal/dynalink/support/CatchExceptionCombinator.java
@@ -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 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() {
+ @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);
+ }
+}
diff --git a/nashorn/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java b/nashorn/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java
new file mode 100644
index 00000000000..9185064b0c0
--- /dev/null
+++ b/nashorn/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java
@@ -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());
+ }
+}
diff --git a/nashorn/src/jdk/internal/dynalink/support/LinkRequestImpl.java b/nashorn/src/jdk/internal/dynalink/support/LinkRequestImpl.java
index c2b0092bf99..6eafbdbb899 100644
--- a/nashorn/src/jdk/internal/dynalink/support/LinkRequestImpl.java
+++ b/nashorn/src/jdk/internal/dynalink/support/LinkRequestImpl.java
@@ -95,6 +95,7 @@ import jdk.internal.dynalink.linker.LinkRequest;
public class LinkRequestImpl implements LinkRequest {
private final CallSiteDescriptor callSiteDescriptor;
+ private final Object callSiteToken;
private final Object[] arguments;
private final boolean callSiteUnstable;
@@ -102,11 +103,13 @@ public class LinkRequestImpl implements LinkRequest {
* Creates a new link request.
*
* @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 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.callSiteToken = callSiteToken;
this.callSiteUnstable = callSiteUnstable;
this.arguments = arguments;
}
@@ -126,6 +129,11 @@ public class LinkRequestImpl implements LinkRequest {
return callSiteDescriptor;
}
+ @Override
+ public Object getCallSiteToken() {
+ return callSiteToken;
+ }
+
@Override
public boolean isCallSiteUnstable() {
return callSiteUnstable;
@@ -138,6 +146,6 @@ public class LinkRequestImpl implements LinkRequest {
@Override
public LinkRequest replaceArguments(CallSiteDescriptor newCallSiteDescriptor, Object[] newArguments) {
- return new LinkRequestImpl(newCallSiteDescriptor, callSiteUnstable, newArguments);
+ return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, callSiteUnstable, newArguments);
}
}
diff --git a/nashorn/src/jdk/internal/dynalink/support/LinkerServicesImpl.java b/nashorn/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
index 3b8e7b46abe..90791e4bdd1 100644
--- a/nashorn/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
+++ b/nashorn/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
@@ -126,6 +126,11 @@ public class LinkerServicesImpl implements LinkerServices {
return typeConverterFactory.asType(handle, fromType);
}
+ @Override
+ public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType) {
+ return Implementation.asTypeLosslessReturn(this, handle, fromType);
+ }
+
@Override
public MethodHandle getTypeConverter(Class> sourceType, Class> targetType) {
return typeConverterFactory.getTypeConverter(sourceType, targetType);
diff --git a/nashorn/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java b/nashorn/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java
index 03309a7c26a..ff77a98eb8a 100644
--- a/nashorn/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java
+++ b/nashorn/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java
@@ -101,15 +101,16 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
* Creates a new link request.
*
* @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 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
* runtime specific context arguments.
* @throws IllegalArgumentException if runtimeContextArgCount is less than 1.
*/
- public RuntimeContextLinkRequestImpl(CallSiteDescriptor callSiteDescriptor, boolean callSiteUnstable,
- Object[] arguments, int runtimeContextArgCount) {
- super(callSiteDescriptor, callSiteUnstable, arguments);
+ public RuntimeContextLinkRequestImpl(CallSiteDescriptor callSiteDescriptor, Object callSiteToken,
+ boolean callSiteUnstable, Object[] arguments, int runtimeContextArgCount) {
+ super(callSiteDescriptor, callSiteToken, callSiteUnstable, arguments);
if(runtimeContextArgCount < 1) {
throw new IllegalArgumentException("runtimeContextArgCount < 1");
}
@@ -121,14 +122,14 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
if(contextStrippedRequest == null) {
contextStrippedRequest =
new LinkRequestImpl(CallSiteDescriptorFactory.dropParameterTypes(getCallSiteDescriptor(), 1,
- runtimeContextArgCount + 1), isCallSiteUnstable(), getTruncatedArguments());
+ runtimeContextArgCount + 1), getCallSiteToken(), isCallSiteUnstable(), getTruncatedArguments());
}
return contextStrippedRequest;
}
@Override
public LinkRequest replaceArguments(CallSiteDescriptor callSiteDescriptor, Object[] arguments) {
- return new RuntimeContextLinkRequestImpl(callSiteDescriptor, isCallSiteUnstable(), arguments,
+ return new RuntimeContextLinkRequestImpl(callSiteDescriptor, getCallSiteToken(), isCallSiteUnstable(), arguments,
runtimeContextArgCount);
}
diff --git a/nashorn/src/jdk/internal/dynalink/support/TypeUtilities.java b/nashorn/src/jdk/internal/dynalink/support/TypeUtilities.java
index 57fea990efa..3b121127e70 100644
--- a/nashorn/src/jdk/internal/dynalink/support/TypeUtilities.java
+++ b/nashorn/src/jdk/internal/dynalink/support/TypeUtilities.java
@@ -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
- * superinterface.
+ * Given two types represented by c1 and c2, returns a type that is their most specific common supertype for
+ * purposes of lossless conversions.
*
* @param c1 one type
* @param c2 another type
- * @return their most common superclass or superinterface. If they have several unrelated superinterfaces as their
- * most specific common type, or the types themselves are completely unrelated interfaces, {@link java.lang.Object}
- * is returned.
+ * @return their most common superclass or superinterface for purposes of lossless conversions. If they have several
+ * unrelated superinterfaces as their most specific common type, or the types themselves are completely
+ * 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) {
return c1;
+ } else if(isConvertibleWithoutLoss(c2, c1)) {
+ return c1;
+ } else if(isConvertibleWithoutLoss(c1, c2)) {
+ return c2;
}
- Class> c3 = c2;
- if(c3.isPrimitive()) {
- if(c3 == Byte.TYPE)
- c3 = Byte.class;
- 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;
+ if(c1 == void.class) {
+ return c2;
+ } else if(c2 == void.class) {
+ return c1;
}
- Set> a1 = getAssignables(c1, c3);
- Set> a2 = getAssignables(c3, c1);
+ if(c1.isPrimitive() && c2.isPrimitive()) {
+ 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> a1 = getAssignables(npc1, npc2);
+ Set> a2 = getAssignables(npc2, npc1);
a1.retainAll(a2);
if(a1.isEmpty()) {
// Can happen when at least one of the arguments is an interface,
@@ -168,7 +179,7 @@ public class TypeUtilities {
max.add(clazz);
}
if(max.size() > 1) {
- return OBJECT_CLASS;
+ return Object.class;
}
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
* 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 methodType the parameter type in the method declaration
- * @return true if callSiteType is method invocation convertible to the methodType.
+ * @param sourceType the type being converted from (call site type for parameter types, method type for return types)
+ * @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
+ * @return true if source type is method invocation convertible to target type.
*/
- public static boolean isMethodInvocationConvertible(Class> callSiteType, Class> methodType) {
- if(methodType.isAssignableFrom(callSiteType)) {
+ public static boolean isMethodInvocationConvertible(Class> sourceType, Class> targetType) {
+ if(targetType.isAssignableFrom(sourceType)) {
return true;
}
- if(callSiteType.isPrimitive()) {
- if(methodType.isPrimitive()) {
- return isProperPrimitiveSubtype(callSiteType, methodType);
+ if(sourceType.isPrimitive()) {
+ if(targetType.isPrimitive()) {
+ return isProperPrimitiveSubtype(sourceType, targetType);
}
// 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()) {
- final Class> unboxedCallSiteType = PRIMITIVE_TYPES.get(callSiteType);
+ if(targetType.isPrimitive()) {
+ final Class> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
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;
}
@@ -266,7 +312,7 @@ public class TypeUtilities {
*/
public static boolean isPotentiallyConvertible(Class> callSiteType, Class> methodType) {
// Widening or narrowing reference conversion
- if(methodType.isAssignableFrom(callSiteType) || callSiteType.isAssignableFrom(methodType)) {
+ if(areAssignable(callSiteType, methodType)) {
return true;
}
if(callSiteType.isPrimitive()) {
@@ -286,6 +332,16 @@ public class TypeUtilities {
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
* 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;
}
+ /**
+ * 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>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
private static Map, Class>> createWrapperToPrimitiveTypes() {
diff --git a/nashorn/src/jdk/nashorn/api/scripting/JSObject.java b/nashorn/src/jdk/nashorn/api/scripting/JSObject.java
index bd6e820be3d..8d818642ef5 100644
--- a/nashorn/src/jdk/nashorn/api/scripting/JSObject.java
+++ b/nashorn/src/jdk/nashorn/api/scripting/JSObject.java
@@ -26,7 +26,6 @@
package jdk.nashorn.api.scripting;
import java.util.Collection;
-import java.util.Collections;
import java.util.Set;
/**
diff --git a/nashorn/src/jdk/nashorn/api/scripting/NashornException.java b/nashorn/src/jdk/nashorn/api/scripting/NashornException.java
index a5f8c24a2e6..c48bf4b26aa 100644
--- a/nashorn/src/jdk/nashorn/api/scripting/NashornException.java
+++ b/nashorn/src/jdk/nashorn/api/scripting/NashornException.java
@@ -182,7 +182,7 @@ public abstract class NashornException extends RuntimeException {
if (ECMAErrors.isScriptFrame(st)) {
final String className = "<" + st.getFileName() + ">";
String methodName = st.getMethodName();
- if (methodName.equals(CompilerConstants.RUN_SCRIPT.symbolName())) {
+ if (methodName.equals(CompilerConstants.PROGRAM.symbolName())) {
methodName = "";
}
@@ -224,10 +224,22 @@ public abstract class NashornException extends RuntimeException {
return buf.toString();
}
+ /**
+ * Get the thrown object. Subclass responsibility
+ * @return thrown object
+ */
protected Object getThrown() {
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) {
if (ecmaError != null) {
return this; // initialized already!
diff --git a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
index 3c4d29fc025..bbbaae9c99e 100644
--- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
+++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
@@ -61,6 +61,7 @@ import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.GlobalObject;
import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
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) {
// set "context" global variable via contextProperty - because this
// property is non-writable
- contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
+ contextProperty.setValue(ctxtGlobal, ctxtGlobal, ctxt, false);
Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
if (args == null || args == UNDEFINED) {
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 {
return compileImpl(source, getNashornGlobalFrom(ctxt));
}
diff --git a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
index 7a9792df19c..29dcf22d211 100644
--- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
+++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
@@ -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) {
functionName.getClass(); // null check
final ScriptObject oldGlobal = Context.getGlobal();
diff --git a/nashorn/src/jdk/nashorn/internal/IntDeque.java b/nashorn/src/jdk/nashorn/internal/IntDeque.java
new file mode 100644
index 00000000000..477afcf94ae
--- /dev/null
+++ b/nashorn/src/jdk/nashorn/internal/IntDeque.java
@@ -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;
+ }
+}
diff --git a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java
index 177cfef4e4b..d2fed14df29 100644
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java
@@ -43,6 +43,7 @@ import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.ir.Symbol.IS_LET;
import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
+import static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL;
import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
@@ -63,10 +64,13 @@ import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
+import jdk.nashorn.internal.ir.FunctionCall;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
@@ -74,9 +78,12 @@ import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
+import jdk.nashorn.internal.ir.Optimistic;
+import jdk.nashorn.internal.ir.OptimisticLexicalContext;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
+import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
@@ -85,6 +92,7 @@ import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
+import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -111,7 +119,9 @@ import jdk.nashorn.internal.runtime.PropertyMap;
* computed.
*/
-final class Attr extends NodeOperatorVisitor {
+final class Attr extends NodeOperatorVisitor {
+
+ private final CompilationEnvironment env;
/**
* Local definitions in current block (to discriminate from function
@@ -127,7 +137,8 @@ final class Attr extends NodeOperatorVisitor {
*/
private final Deque> localUses;
- private final Deque returnTypes;
+ private final Set optimistic = new HashSet<>();
+ private final Set neverOptimistic = new HashSet<>();
private int catchNestingLevel;
@@ -139,12 +150,12 @@ final class Attr extends NodeOperatorVisitor {
/**
* Constructor.
*/
- Attr(final TemporarySymbols temporarySymbols) {
- super(new LexicalContext());
+ Attr(final CompilationEnvironment env, final TemporarySymbols temporarySymbols) {
+ super(new OptimisticLexicalContext(env.useOptimisticTypes()));
+ this.env = env;
this.temporarySymbols = temporarySymbols;
- this.localDefs = new ArrayDeque<>();
- this.localUses = new ArrayDeque<>();
- this.returnTypes = new ArrayDeque<>();
+ this.localDefs = new ArrayDeque<>();
+ this.localUses = new ArrayDeque<>();
}
@Override
@@ -159,10 +170,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAccessNode(final AccessNode accessNode) {
- //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this, that
- //is why we can't set the access node base to be an object here, that will ruin access specialization
- //for example for a.x | 17.
- return end(ensureSymbol(Type.OBJECT, accessNode));
+ return end(ensureSymbolTypeOverride(accessNode, Type.OBJECT));
}
private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
@@ -235,6 +243,11 @@ final class Attr extends NodeOperatorVisitor {
@Override
public boolean enterVarNode(final VarNode varNode) {
+ final Expression init = varNode.getInit();
+ if (init != null) {
+ tagOptimistic(init);
+ }
+
final String name = varNode.getName().getName();
//if this is used before the var node, the var node symbol needs to be tagged as can be undefined
if (uses.contains(name)) {
@@ -264,7 +277,7 @@ final class Attr extends NodeOperatorVisitor {
if (varNode.isStatement()) {
final IdentNode ident = varNode.getName();
final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR);
- if (canBeUndefined.contains(ident.getName())) {
+ if (canBeUndefined.contains(ident.getName()) && varNode.getInit() == null) {
symbol.setType(Type.OBJECT);
symbol.setCanBeUndefined();
}
@@ -299,6 +312,9 @@ final class Attr extends NodeOperatorVisitor {
if (!(anonymous || body.getExistingSymbol(name) != null)) {
assert !anonymous && name != null;
newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT);
+ if(functionNode.allVarsInScope()) { // basically, has deep eval
+ lc.setFlag(body, Block.USES_SELF_SYMBOL);
+ }
}
}
@@ -326,14 +342,24 @@ final class Attr extends NodeOperatorVisitor {
return end(block);
}
+ private boolean useOptimisticTypes() {
+ return env.useOptimisticTypes() && !lc.isInSplitNode();
+ }
+
@Override
public boolean enterCallNode(final CallNode callNode) {
- return start(callNode);
+ for (final Expression arg : callNode.getArgs()) {
+ tagOptimistic(arg);
+ }
+ return true;
}
@Override
public Node leaveCallNode(final CallNode callNode) {
- return end(ensureSymbol(callNode.getType(), callNode));
+ for (final Expression arg : callNode.getArgs()) {
+ inferParameter(arg, arg.getType());
+ }
+ return end(ensureSymbolTypeOverride(callNode, Type.OBJECT));
}
@Override
@@ -346,8 +372,13 @@ final class Attr extends NodeOperatorVisitor {
// define block-local exception variable
final String exname = exception.getName();
- final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED);
- newType(def, Type.OBJECT); //we can catch anything, not just ecma exceptions
+ // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
+ // symbol is naturally internal, and should be treated as such.
+ final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
+ final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED | (isInternal ? IS_INTERNAL : 0));
+ // Normally, we can catch anything, not just ECMAExceptions, hence Type.OBJECT. However, for catches with
+ // internal symbol, we can be sure the caught type is a Throwable.
+ newType(def, isInternal ? Type.typeFor(EXCEPTION_PREFIX.type()) : Type.OBJECT);
addLocalDef(exname);
@@ -382,6 +413,10 @@ final class Attr extends NodeOperatorVisitor {
flags |= IS_SCOPE;
}
+ if (lc.getCurrentFunction().isProgram()) {
+ flags |= IS_PROGRAM_LEVEL;
+ }
+
final FunctionNode function = lc.getFunction(block);
if (symbol != null) {
// Symbol was already defined. Check if it needs to be redefined.
@@ -432,15 +467,20 @@ final class Attr extends NodeOperatorVisitor {
return symbol;
}
+ @Override
+ public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
+ final Expression expr = expressionStatement.getExpression();
+ if (!expr.isSelfModifying()) { //self modifying ops like i++ need the optimistic type for their own operation, not just the return value, as there is no difference. gah.
+ tagNeverOptimistic(expr);
+ }
+ return true;
+ }
+
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
start(functionNode, false);
- if (functionNode.isLazy()) {
- return false;
- }
-
- //an outermost function in our lexical context that is not a program (runScript)
+ //an outermost function in our lexical context that is not a program
//is possible - it is a function being compiled lazily
if (functionNode.isDeclared()) {
final Iterator blocks = lc.getBlocks();
@@ -449,7 +489,6 @@ final class Attr extends NodeOperatorVisitor {
}
}
- returnTypes.push(functionNode.getReturnType());
pushLocalsFunction();
return true;
@@ -471,7 +510,7 @@ final class Attr extends NodeOperatorVisitor {
final boolean anonymous = functionNode.isAnonymous();
final String name = anonymous ? null : functionNode.getIdent().getName();
if (anonymous || body.getExistingSymbol(name) != null) {
- newFunctionNode = (FunctionNode)ensureSymbol(FunctionNode.FUNCTION_TYPE, newFunctionNode);
+ newFunctionNode = (FunctionNode)ensureSymbol(newFunctionNode, FunctionNode.FUNCTION_TYPE);
} else {
assert name != null;
final Symbol self = body.getExistingSymbol(name);
@@ -480,11 +519,6 @@ final class Attr extends NodeOperatorVisitor {
}
}
- //unknown parameters are promoted to object type.
- if (newFunctionNode.hasLazyChildren()) {
- //the final body has already been assigned as we have left the function node block body by now
- objectifySymbols(body);
- }
newFunctionNode = finalizeParameters(newFunctionNode);
newFunctionNode = finalizeTypes(newFunctionNode);
for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
@@ -496,7 +530,7 @@ final class Attr extends NodeOperatorVisitor {
List syntheticInitializers = null;
- if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
+ if (newFunctionNode.usesSelfSymbol()) {
syntheticInitializers = new ArrayList<>(2);
LOG.info("Accepting self symbol init for ", newFunctionNode.getName());
// "var fn = :callee"
@@ -520,19 +554,12 @@ final class Attr extends NodeOperatorVisitor {
newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements));
}
- if (returnTypes.peek().isUnknown()) {
- LOG.info("Unknown return type promoted to object");
- newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
- }
- final Type returnType = returnTypes.pop();
- newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
- newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
+ final int optimisticFlag = lc.hasOptimisticAssumptions() ? FunctionNode.IS_OPTIMISTIC : 0;
+ newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR).setFlag(lc, optimisticFlag);
popLocals();
- end(newFunctionNode, false);
-
- return newFunctionNode;
+ return end(newFunctionNode, false);
}
/**
@@ -580,9 +607,9 @@ final class Attr extends NodeOperatorVisitor {
final FunctionNode functionNode = lc.getDefiningFunction(symbol);
assert functionNode != null;
assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
- lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
+ lc.setFlag(functionNode.getBody(), Block.USES_SELF_SYMBOL);
newType(symbol, FunctionNode.FUNCTION_TYPE);
- } else if (!identNode.isInitializedHere()) {
+ } else if (!(identNode.isInitializedHere() || symbol.isAlwaysDefined())) {
/*
* See NASHORN-448, JDK-8016235
*
@@ -617,8 +644,12 @@ final class Attr extends NodeOperatorVisitor {
symbol.increaseUseCount();
}
addLocalUse(identNode.getName());
+ IdentNode node = (IdentNode)identNode.setSymbol(lc, symbol);
+ if (isTaggedOptimistic(identNode) && symbol.isScope()) {
+ node = ensureSymbolTypeOverride(node, symbol.getSymbolType());
+ }
- return end(identNode.setSymbol(lc, symbol));
+ return end(node);
}
private boolean inCatch() {
@@ -636,16 +667,21 @@ final class Attr extends NodeOperatorVisitor {
}
}
- private boolean symbolNeedsToBeScope(Symbol symbol) {
+ private boolean symbolNeedsToBeScope(final Symbol symbol) {
if (symbol.isThis() || symbol.isInternal()) {
return false;
}
+
+ if (lc.getCurrentFunction().allVarsInScope()) {
+ return true;
+ }
+
boolean previousWasBlock = false;
for (final Iterator it = lc.getAllNodes(); it.hasNext();) {
final LexicalContextNode node = it.next();
- if (node instanceof FunctionNode) {
- // We reached the function boundary without seeing a definition for the symbol - it needs to be in
- // scope.
+ if (node instanceof FunctionNode || node instanceof SplitNode) {
+ // We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
+ // It needs to be in scope.
return true;
} else if (node instanceof WithNode) {
if (previousWasBlock) {
@@ -729,7 +765,8 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
- return end(ensureSymbol(Type.OBJECT, indexNode));
+ // return end(ensureSymbolOptimistic(Type.OBJECT, indexNode));
+ return end(ensureSymbolTypeOverride(indexNode, Type.OBJECT));
}
@SuppressWarnings("rawtypes")
@@ -751,31 +788,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveObjectNode(final ObjectNode objectNode) {
- return end(ensureSymbol(Type.OBJECT, objectNode));
- }
-
- @Override
- public Node leaveReturnNode(final ReturnNode returnNode) {
- final Expression expr = returnNode.getExpression();
- final Type returnType;
-
- if (expr != null) {
- //we can't do parameter specialization if we return something that hasn't been typed yet
- final Symbol symbol = expr.getSymbol();
- if (expr.getType().isUnknown() && symbol.isParam()) {
- symbol.setType(Type.OBJECT);
- }
-
- returnType = widestReturnType(returnTypes.pop(), symbol.getSymbolType());
- } else {
- returnType = Type.OBJECT; //undefined
- }
- LOG.info("Returntype is now ", returnType);
- returnTypes.push(returnType);
-
- end(returnNode);
-
- return returnNode;
+ return end(ensureSymbol(objectNode, Type.OBJECT));
}
@Override
@@ -819,9 +832,7 @@ final class Attr extends NodeOperatorVisitor {
switchNode.setTag(newInternal(lc.getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
- end(switchNode);
-
- return switchNode.setCases(lc, newCases);
+ return end(switchNode.setCases(lc, newCases));
}
@Override
@@ -848,7 +859,10 @@ final class Attr extends NodeOperatorVisitor {
assert symbol != null;
// NASHORN-467 - use before definition of vars - conservative
- if (isLocalUse(ident.getName())) {
+ //function each(iterator, context) {
+ // for (var i = 0, length = this.length >>> 0; i < length; i++) { if (i in this) iterator.call(context, this[i], i, this); }
+ //
+ if (isLocalUse(ident.getName()) && varNode.getInit() == null) {
newType(symbol, Type.OBJECT);
symbol.setCanBeUndefined();
}
@@ -893,30 +907,56 @@ final class Attr extends NodeOperatorVisitor {
return end(newVarNode);
}
+ @Override
+ public boolean enterNOT(UnaryNode unaryNode) {
+ tagNeverOptimistic(unaryNode.getExpression());
+ return true;
+ }
+
+ public boolean enterUnaryArithmetic(final UnaryNode unaryNode) {
+ tagOptimistic(unaryNode.getExpression());
+ return true;
+ }
+
+ private UnaryNode leaveUnaryArithmetic(final UnaryNode unaryNode) {
+ return end(coerce(unaryNode, unaryNode.getMostPessimisticType(), unaryNode.getExpression().getType()));
+ }
+
+ @Override
+ public boolean enterADD(final UnaryNode unaryNode) {
+ return enterUnaryArithmetic(unaryNode);
+ }
+
@Override
public Node leaveADD(final UnaryNode unaryNode) {
- return end(ensureSymbol(arithType(), unaryNode));
+ return leaveUnaryArithmetic(unaryNode);
}
@Override
public Node leaveBIT_NOT(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.INT, unaryNode));
+ return end(coerce(unaryNode, Type.INT));
+ }
+
+ @Override
+ public boolean enterDECINC(final UnaryNode unaryNode) {
+ return enterUnaryArithmetic(unaryNode);
}
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
// @see assignOffset
- final Type type = arithType();
- newType(unaryNode.rhs().getSymbol(), type);
- return end(ensureSymbol(type, unaryNode));
+ final Type pessimisticType = unaryNode.getMostPessimisticType();
+ final UnaryNode newUnaryNode = ensureSymbolTypeOverride(unaryNode, pessimisticType, unaryNode.getExpression().getType());
+ newType(newUnaryNode.getExpression().getSymbol(), newUnaryNode.getType());
+ return end(newUnaryNode);
}
@Override
public Node leaveDELETE(final UnaryNode unaryNode) {
- final FunctionNode currentFunctionNode = lc.getCurrentFunction();
- final boolean strictMode = currentFunctionNode.isStrict();
- final Expression rhs = unaryNode.rhs();
- final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
+ final FunctionNode currentFunctionNode = lc.getCurrentFunction();
+ final boolean strictMode = currentFunctionNode.isStrict();
+ final Expression rhs = unaryNode.getExpression();
+ final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
Request request = Request.DELETE;
final List args = new ArrayList<>();
@@ -925,7 +965,10 @@ final class Attr extends NodeOperatorVisitor {
// If this is a declared variable or a function parameter, delete always fails (except for globals).
final String name = ((IdentNode)rhs).getName();
- final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
+ final boolean isParam = rhs.getSymbol().isParam();
+ final boolean isVar = rhs.getSymbol().isVar();
+ final boolean isNonProgramVar = isVar && !rhs.getSymbol().isProgramLevel();
+ final boolean failDelete = strictMode || isParam || isNonProgramVar;
if (failDelete && rhs.getSymbol().isThis()) {
return LiteralNode.newInstance(unaryNode, true).accept(this);
@@ -968,29 +1011,14 @@ final class Attr extends NodeOperatorVisitor {
return leaveRuntimeNode(runtimeNode);
}
- /**
- * Is the symbol denoted by the specified name in the current lexical context defined in the program level
- * @param name the name of the symbol
- * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
- */
- private boolean isProgramLevelSymbol(final String name) {
- for(final Iterator it = lc.getBlocks(); it.hasNext();) {
- final Block next = it.next();
- if(next.getExistingSymbol(name) != null) {
- return next == lc.getFunctionBody(lc.getOutermostFunction());
- }
- }
- throw new AssertionError("Couldn't find symbol " + name + " in the context");
- }
-
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew())));
+ return end(coerce(unaryNode.setExpression(((CallNode)unaryNode.getExpression()).setIsNew()), Type.OBJECT));
}
@Override
public Node leaveNOT(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.BOOLEAN, unaryNode));
+ return end(coerce(unaryNode, Type.BOOLEAN));
}
private IdentNode compilerConstant(CompilerConstants cc) {
@@ -1010,7 +1038,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveTYPEOF(final UnaryNode unaryNode) {
- final Expression rhs = unaryNode.rhs();
+ final Expression rhs = unaryNode.getExpression();
List args = new ArrayList<>();
if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
@@ -1033,17 +1061,29 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
- return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode));
+ return end(ensureSymbol(runtimeNode, runtimeNode.getRequest().getReturnType()));
+ }
+
+ @Override
+ public boolean enterSUB(final UnaryNode unaryNode) {
+ return enterUnaryArithmetic(unaryNode);
}
@Override
public Node leaveSUB(final UnaryNode unaryNode) {
- return end(ensureSymbol(arithType(), unaryNode));
+ return leaveUnaryArithmetic(unaryNode);
}
@Override
public Node leaveVOID(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.OBJECT, unaryNode));
+ return end(ensureSymbol(unaryNode, Type.OBJECT));
+ }
+
+ @Override
+ public boolean enterADD(final BinaryNode binaryNode) {
+ tagOptimistic(binaryNode.lhs());
+ tagOptimistic(binaryNode.rhs());
+ return true;
}
/**
@@ -1055,18 +1095,22 @@ final class Attr extends NodeOperatorVisitor {
final Expression lhs = binaryNode.lhs();
final Expression rhs = binaryNode.rhs();
- ensureTypeNotUnknown(lhs);
- ensureTypeNotUnknown(rhs);
- //even if we are adding two known types, this can overflow. i.e.
- //int and number -> number.
- //int and int are also number though.
- //something and object is object
- return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode));
+ //an add is at least as wide as the current arithmetic type, possibly wider in the case of objects
+ //which will be corrected in the post pass if unknown at this stage
+
+ Type argumentsType = Type.widest(lhs.getType(), rhs.getType());
+ if(argumentsType.getTypeClass() == String.class) {
+ assert binaryNode.isTokenType(TokenType.ADD);
+ argumentsType = Type.OBJECT;
+ }
+ final Type pessimisticType = Type.widest(Type.NUMBER, argumentsType);
+
+ return end(ensureSymbolTypeOverride(binaryNode, pessimisticType, argumentsType));
}
@Override
public Node leaveAND(final BinaryNode binaryNode) {
- return end(ensureSymbol(Type.OBJECT, binaryNode));
+ return end(ensureSymbol(binaryNode, Type.OBJECT));
}
/**
@@ -1075,11 +1119,18 @@ final class Attr extends NodeOperatorVisitor {
*/
private boolean enterAssignmentNode(final BinaryNode binaryNode) {
start(binaryNode);
+ final Expression lhs = binaryNode.lhs();
+ if (lhs instanceof IdentNode) {
+ if (CompilerConstants.isCompilerConstant(((IdentNode)lhs).getName())) {
+ tagNeverOptimistic(binaryNode.rhs());
+ }
+ }
+ //tagOptimistic(binaryNode.lhs());
+ tagOptimistic(binaryNode.rhs());
return true;
}
-
/**
* This assign helper is called after an assignment, when all children of
* the assign has been processed. It fixes the types and recursively makes
@@ -1096,6 +1147,7 @@ final class Attr extends NodeOperatorVisitor {
final Block block = lc.getCurrentBlock();
final IdentNode ident = (IdentNode)lhs;
final String name = ident.getName();
+
final Symbol symbol = findSymbol(block, name);
if (symbol == null) {
@@ -1114,7 +1166,7 @@ final class Attr extends NodeOperatorVisitor {
}
newType(lhs.getSymbol(), type);
- return end(ensureSymbol(type, binaryNode));
+ return end(ensureSymbol(binaryNode, type));
}
private boolean isLocal(FunctionNode function, Symbol symbol) {
@@ -1140,11 +1192,11 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
- final Expression lhs = binaryNode.lhs();
- final Expression rhs = binaryNode.rhs();
-
- final Type widest = Type.widest(lhs.getType(), rhs.getType());
+ final Expression lhs = binaryNode.lhs();
+ final Expression rhs = binaryNode.rhs();
+ final Type widest = Type.widest(lhs.getType(), rhs.getType());
//Type.NUMBER if we can't prove that the add doesn't overflow. todo
+
return leaveSelfModifyingAssignmentNode(binaryNode, widest.isNumeric() ? Type.NUMBER : Type.OBJECT);
}
@@ -1248,23 +1300,50 @@ final class Attr extends NodeOperatorVisitor {
return leaveSelfModifyingAssignmentNode(binaryNode);
}
+ @Override
+ public boolean enterBIT_AND(final BinaryNode binaryNode) {
+ return enterBitwiseOperator(binaryNode);
+ }
+
@Override
public Node leaveBIT_AND(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
+ return leaveBitwiseOperator(binaryNode);
+ }
+
+ @Override
+ public boolean enterBIT_OR(final BinaryNode binaryNode) {
+ return enterBitwiseOperator(binaryNode);
}
@Override
public Node leaveBIT_OR(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
+ return leaveBitwiseOperator(binaryNode);
+ }
+
+ @Override
+ public boolean enterBIT_XOR(final BinaryNode binaryNode) {
+ return enterBitwiseOperator(binaryNode);
}
@Override
public Node leaveBIT_XOR(final BinaryNode binaryNode) {
+ return leaveBitwiseOperator(binaryNode);
+ }
+
+ public boolean enterBitwiseOperator(final BinaryNode binaryNode) {
+ start(binaryNode);
+ tagOptimistic(binaryNode.lhs());
+ tagOptimistic(binaryNode.rhs());
+ return true;
+ }
+
+ private Node leaveBitwiseOperator(final BinaryNode binaryNode) {
return end(coerce(binaryNode, Type.INT));
}
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
+// return end(ensureSymbol(binaryNode, binaryNode.rhs().getType()));
return leaveComma(binaryNode, binaryNode.rhs());
}
@@ -1274,8 +1353,11 @@ final class Attr extends NodeOperatorVisitor {
}
private Node leaveComma(final BinaryNode commaNode, final Expression effectiveExpr) {
- ensureTypeNotUnknown(effectiveExpr);
- return end(ensureSymbol(effectiveExpr.getType(), commaNode));
+ Type type = effectiveExpr.getType();
+ if (type.isUnknown()) { //TODO more optimistic
+ type = Type.OBJECT;
+ }
+ return end(ensureSymbol(commaNode, type));
}
@Override
@@ -1284,34 +1366,32 @@ final class Attr extends NodeOperatorVisitor {
}
private Node leaveCmp(final BinaryNode binaryNode) {
- ensureTypeNotUnknown(binaryNode.lhs());
- ensureTypeNotUnknown(binaryNode.rhs());
- Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
- ensureSymbol(widest, binaryNode.lhs());
- ensureSymbol(widest, binaryNode.rhs());
- return end(ensureSymbol(Type.BOOLEAN, binaryNode));
+ //infect untyped comp with opportunistic type from other
+ final Expression lhs = binaryNode.lhs();
+ final Expression rhs = binaryNode.rhs();
+ final Type type = Type.narrowest(lhs.getType(), rhs.getType(), Type.INT);
+ inferParameter(lhs, type);
+ inferParameter(rhs, type);
+ Type widest = Type.widest(lhs.getType(), rhs.getType());
+ ensureSymbol(lhs, widest);
+ ensureSymbol(rhs, widest);
+ return end(ensureSymbol(binaryNode, Type.BOOLEAN));
}
- private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
- // TODO we currently don't support changing inferred type based on uses, only on
- // definitions. we would need some additional logic. We probably want to do that
- // in the future, if e.g. a specialized method gets parameter that is only used
- // as, say, an int : function(x) { return x & 4711 }, and x is not defined in
- // the function. to make this work, uncomment the following two type inferences
- // and debug.
- //newType(binaryNode.lhs().getSymbol(), operandType);
- //newType(binaryNode.rhs().getSymbol(), operandType);
- return ensureSymbol(destType, binaryNode);
- }
-
- private Node coerce(final BinaryNode binaryNode, final Type type) {
- return coerce(binaryNode, type, type);
+ private boolean enterBinaryArithmetic(final BinaryNode binaryNode) {
+ tagOptimistic(binaryNode.lhs());
+ tagOptimistic(binaryNode.rhs());
+ return true;
}
//leave a binary node and inherit the widest type of lhs , rhs
private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
- assert !Compiler.shouldUseIntegerArithmetic();
- return end(coerce(binaryNode, Type.NUMBER));
+ return end(coerce(binaryNode, binaryNode.getMostPessimisticType(), Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType())));
+ }
+
+ @Override
+ public boolean enterEQ(BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
}
@Override
@@ -1319,16 +1399,31 @@ final class Attr extends NodeOperatorVisitor {
return leaveCmp(binaryNode);
}
+ @Override
+ public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode);
}
+ @Override
+ public boolean enterGE(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveGE(final BinaryNode binaryNode) {
return leaveCmp(binaryNode);
}
+ @Override
+ public boolean enterGT(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveGT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode);
@@ -1353,11 +1448,21 @@ final class Attr extends NodeOperatorVisitor {
}
}
+ @Override
+ public boolean enterLE(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveLE(final BinaryNode binaryNode) {
return leaveCmp(binaryNode);
}
+ @Override
+ public boolean enterLT(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveLT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode);
@@ -1368,16 +1473,31 @@ final class Attr extends NodeOperatorVisitor {
return leaveBinaryArithmetic(binaryNode);
}
+ @Override
+ public boolean enterMUL(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveMUL(final BinaryNode binaryNode) {
return leaveBinaryArithmetic(binaryNode);
}
+ @Override
+ public boolean enterNE(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveNE(final BinaryNode binaryNode) {
return leaveCmp(binaryNode);
}
+ @Override
+ public boolean enterNE_STRICT(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveNE_STRICT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode);
@@ -1385,17 +1505,32 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveOR(final BinaryNode binaryNode) {
- return end(ensureSymbol(Type.OBJECT, binaryNode));
+ return end(ensureSymbol(binaryNode, Type.OBJECT));
+ }
+
+ @Override
+ public boolean enterSAR(final BinaryNode binaryNode) {
+ return enterBitwiseOperator(binaryNode);
}
@Override
public Node leaveSAR(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
+ return leaveBitwiseOperator(binaryNode);
+ }
+
+ @Override
+ public boolean enterSHL(final BinaryNode binaryNode) {
+ return enterBitwiseOperator(binaryNode);
}
@Override
public Node leaveSHL(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
+ return leaveBitwiseOperator(binaryNode);
+ }
+
+ @Override
+ public boolean enterSHR(final BinaryNode binaryNode) {
+ return enterBitwiseOperator(binaryNode);
}
@Override
@@ -1403,11 +1538,22 @@ final class Attr extends NodeOperatorVisitor {
return end(coerce(binaryNode, Type.LONG));
}
+ @Override
+ public boolean enterSUB(final BinaryNode binaryNode) {
+ return enterBinaryArithmetic(binaryNode);
+ }
+
@Override
public Node leaveSUB(final BinaryNode binaryNode) {
return leaveBinaryArithmetic(binaryNode);
}
+ @Override
+ public boolean enterForNode(ForNode forNode) {
+ tagNeverOptimistic(forNode.getTest());
+ return true;
+ }
+
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForIn()) {
@@ -1420,54 +1566,59 @@ final class Attr extends NodeOperatorVisitor {
newType(forNode.getInit().getSymbol(), Type.OBJECT);
}
- end(forNode);
+ return end(forNode);
+ }
- return forNode;
+ @Override
+ public boolean enterTernaryNode(TernaryNode ternaryNode) {
+ tagNeverOptimistic(ternaryNode.getTest());
+ return true;
}
@Override
public Node leaveTernaryNode(final TernaryNode ternaryNode) {
- final Expression trueExpr = ternaryNode.getTrueExpression();
- final Expression falseExpr = ternaryNode.getFalseExpression();
-
- ensureTypeNotUnknown(trueExpr);
- ensureTypeNotUnknown(falseExpr);
-
- final Type type = widestReturnType(trueExpr.getType(), falseExpr.getType());
- return end(ensureSymbol(type, ternaryNode));
+ final Type trueType = ternaryNode.getTrueExpression().getType();
+ final Type falseType = ternaryNode.getFalseExpression().getType();
+ final Type type;
+ if (trueType.isUnknown() || falseType.isUnknown()) {
+ type = Type.UNKNOWN;
+ } else {
+ type = widestReturnType(trueType, falseType);
+ }
+ return end(ensureSymbol(ternaryNode, type));
}
/**
* When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to
- * anything other than Object. Also, widening a numeric type to an object type must widen to Object proper and not
- * any more specific subclass (e.g. widest of int/long/double and String is Object).
+ * anything other than object. Note that this wouldn't be necessary if {@code Type.widest} did not allow
+ * boolean-to-number widening. Eventually, we should address it there, but it affects too many other parts of the
+ * system and is sometimes legitimate (e.g. whenever a boolean value would undergo ToNumber conversion anyway).
* @param t1 type 1
* @param t2 type 2
- * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, or if one is
- * numeric and the other is neither numeric nor unknown in which case {@code Type.OBJECT} is returned.
+ * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, in which case
+ * {@code Type.OBJECT} is returned.
*/
private static Type widestReturnType(final Type t1, final Type t2) {
if (t1.isUnknown()) {
return t2;
} else if (t2.isUnknown()) {
return t1;
- } else if (t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
+ } else if(t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
return Type.OBJECT;
}
return Type.widest(t1, t2);
}
private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
- final Class> type = cc.type();
// Must not call this method for constants with no explicit types; use the one with (..., Type) signature instead.
- assert type != null;
- initCompileConstant(cc, block, flags, Type.typeFor(type));
+ assert cc.type() != null;
+ initCompileConstant(cc, block, flags, Type.typeFor(cc.type()));
}
private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) {
- final Symbol symbol = defineSymbol(block, cc.symbolName(), flags);
- symbol.setTypeOverride(type);
- symbol.setNeedsSlot(true);
+ defineSymbol(block, cc.symbolName(), flags).
+ setTypeOverride(type).
+ setNeedsSlot(true);
}
/**
@@ -1477,22 +1628,29 @@ final class Attr extends NodeOperatorVisitor {
* @param functionNode the function node
*/
private void initParameters(final FunctionNode functionNode, final Block body) {
+ final boolean isOptimistic = env.useOptimisticTypes();
int pos = 0;
for (final IdentNode param : functionNode.getParameters()) {
addLocalDef(param.getName());
- final Type callSiteParamType = functionNode.getHints().getParameterType(pos);
- int flags = IS_PARAM;
- if (callSiteParamType != null) {
- LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that.");
- flags |= Symbol.IS_SPECIALIZED_PARAM;
- }
-
- final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
+ final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM);
assert paramSymbol != null;
- newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
-
+ final Type callSiteParamType = env.getParamType(functionNode, pos);
+ if (callSiteParamType != null) {
+ LOG.info("Callsite type override for parameter " + pos + " " + paramSymbol + " => " + callSiteParamType);
+ newType(paramSymbol, callSiteParamType);
+ } else {
+ // When we're using optimistic compilation, we'll generate specialized versions of the functions anyway
+ // based on their input type, so if we're doing a compilation without parameter types explicitly
+ // specified in the compilation environment, just pre-initialize them all to Object. Note that this is
+ // not merely an optimization; it has correctness implications as Type.UNKNOWN is narrower than all
+ // other types, which when combined with optimistic typing can cause invalid coercions to be introduced
+ // in the generated code. E.g. "var b = { x: 0 }; (function (i) { this.x += i }).apply(b, [1.1])" would
+ // erroneously allow coercion of "i" to int when "this.x" is an optimistic-int and "i" starts out
+ // with Type.UNKNOWN.
+ newType(paramSymbol, isOptimistic ? Type.OBJECT : Type.UNKNOWN);
+ }
LOG.info("Initialized param ", pos, "=", paramSymbol);
pos++;
}
@@ -1508,49 +1666,47 @@ final class Attr extends NodeOperatorVisitor {
private FunctionNode finalizeParameters(final FunctionNode functionNode) {
final List newParams = new ArrayList<>();
final boolean isVarArg = functionNode.isVarArg();
- final int nparams = functionNode.getParameters().size();
+ final boolean pessimistic = !useOptimisticTypes();
- int specialize = 0;
- int pos = 0;
for (final IdentNode param : functionNode.getParameters()) {
final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName());
assert paramSymbol != null;
- assert paramSymbol.isParam();
+ assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
newParams.add((IdentNode)param.setSymbol(lc, paramSymbol));
assert paramSymbol != null;
- Type type = functionNode.getHints().getParameterType(pos);
- if (type == null) {
- type = Type.OBJECT;
+ Type type = paramSymbol.getSymbolType();
+
+ // all param types are initialized to unknown
+ // first we check if we do have a type (inferred during generation)
+ // and it's not an object. In that case we make an optimistic
+ // assumption
+ if (!type.isUnknown() && !type.isObject()) {
+ //optimistically inferred
+ lc.logOptimisticAssumption(paramSymbol, type);
}
- // if we know that a parameter is only used as a certain type throughout
- // this function, we can tell the runtime system that no matter what the
- // call site is, use this information:
- // we also need more than half of the parameters to be specializable
- // for the heuristic to be worth it, and we need more than one use of
- // the parameter to consider it, i.e. function(x) { call(x); } doens't count
- if (paramSymbol.getUseCount() > 1 && !paramSymbol.getSymbolType().isObject()) {
- LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType());
- specialize++;
+ //known runtime types are hardcoded already in initParameters so avoid any
+ //overly optimistic assumptions, e.g. a double parameter known from
+ //RecompilableScriptFunctionData is with us all the way
+ if (type.isUnknown()) {
+ newType(paramSymbol, Type.OBJECT);
}
- newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
+ // if we are pessimistic, we are always an object
+ if (pessimistic) {
+ newType(paramSymbol, Type.OBJECT);
+ }
// parameters should not be slots for a function that uses variable arity signature
if (isVarArg) {
paramSymbol.setNeedsSlot(false);
+ newType(paramSymbol, Type.OBJECT);
}
-
- pos++;
}
FunctionNode newFunctionNode = functionNode;
- if (nparams == 0 || (specialize * 2) < nparams) {
- newFunctionNode = newFunctionNode.clearSnapshot(lc);
- }
-
return newFunctionNode.setParameters(lc, newParams);
}
@@ -1571,45 +1727,6 @@ final class Attr extends NodeOperatorVisitor {
}
}
- private static void ensureTypeNotUnknown(final Expression node) {
-
- final Symbol symbol = node.getSymbol();
-
- LOG.info("Ensure type not unknown for: ", symbol);
-
- /*
- * Note that not just unknowns, but params need to be blown
- * up to objects, because we can have something like
- *
- * function f(a) {
- * var b = ~a; //b and a are inferred to be int
- * return b;
- * }
- *
- * In this case, it would be correct to say that "if you have
- * an int at the callsite, just pass it".
- *
- * However
- *
- * function f(a) {
- * var b = ~a; //b and a are inferred to be int
- * return b == 17; //b is still inferred to be int.
- * }
- *
- * can be called with f("17") and if we assume that b is an
- * int and don't blow it up to an object in the comparison, we
- * are screwed. I hate JavaScript.
- *
- * This check has to be done for any operation that might take
- * objects as parameters, for example +, but not *, which is known
- * to coerce types into doubles
- */
- if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) {
- newType(symbol, Type.OBJECT);
- symbol.setCanBeUndefined();
- }
- }
-
private static Symbol pseudoSymbol(final String name) {
return new Symbol(name, 0, Type.OBJECT);
}
@@ -1618,15 +1735,6 @@ final class Attr extends NodeOperatorVisitor {
return newInternal(lc.getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(EXCEPTION_PREFIX.type()));
}
- /**
- * Return the type that arithmetic ops should use. Until we have implemented better type
- * analysis (range based) or overflow checks that are fast enough for int arithmetic,
- * this is the number type
- * @return the arithetic type
- */
- private static Type arithType() {
- return Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER;
- }
/**
* If types have changed, we can have failed to update vars. For example
@@ -1638,9 +1746,15 @@ final class Attr extends NodeOperatorVisitor {
*/
private FunctionNode finalizeTypes(final FunctionNode functionNode) {
final Set changed = new HashSet<>();
+ final Deque returnTypes = new ArrayDeque<>();
+
FunctionNode currentFunctionNode = functionNode;
+ int fixedPointIterations = 0;
do {
+ fixedPointIterations++;
+ assert fixedPointIterations < 0x100 : "too many fixed point iterations for " + functionNode.getName() + " -> most likely infinite loop";
changed.clear();
+
final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor(new LexicalContext()) {
private Expression widen(final Expression node, final Type to) {
@@ -1656,7 +1770,9 @@ final class Attr extends NodeOperatorVisitor {
}
newType(symbol, to);
final Expression newNode = node.setSymbol(lc, symbol);
- changed.add(newNode);
+ if (node != newNode) {
+ changed.add(newNode);
+ }
return newNode;
}
return node;
@@ -1664,7 +1780,31 @@ final class Attr extends NodeOperatorVisitor {
@Override
public boolean enterFunctionNode(final FunctionNode node) {
- return !node.isLazy();
+ returnTypes.push(Type.UNKNOWN);
+ return true;
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ Type returnType = returnTypes.pop();
+ if (returnType.isUnknown()) {
+ returnType = Type.OBJECT;
+ }
+ return node.setReturnType(lc, returnType);
+ }
+
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ Type returnType = returnTypes.pop();
+ if (returnNode.hasExpression()) {
+ returnType = widestReturnType(returnType, returnNode.getExpression().getType()); //getSymbol().getSymbolType());
+ } else {
+ returnType = Type.OBJECT; //undefined
+ }
+
+ returnTypes.push(returnType);
+
+ return returnNode;
}
//
@@ -1683,10 +1823,16 @@ final class Attr extends NodeOperatorVisitor {
@SuppressWarnings("fallthrough")
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
- final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+ Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
BinaryNode newBinaryNode = binaryNode;
if (isAdd(binaryNode)) {
+ if(widest.getTypeClass() == String.class) {
+ // Erase "String" to "Object" as we have trouble with optimistically typed operands that
+ // would be typed "String" in the code generator as they are always loaded using the type
+ // of the operation.
+ widest = Type.OBJECT;
+ }
newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) {
return new RuntimeNode(newBinaryNode, Request.ADD);
@@ -1728,6 +1874,11 @@ final class Attr extends NodeOperatorVisitor {
}
+ @Override
+ public Node leaveTernaryNode(TernaryNode ternaryNode) {
+ return widen(ternaryNode, Type.widest(ternaryNode.getTrueExpression().getType(), ternaryNode.getFalseExpression().getType()));
+ }
+
private boolean isAdd(final Node node) {
return node.isTokenType(TokenType.ADD);
}
@@ -1773,24 +1924,145 @@ final class Attr extends NodeOperatorVisitor {
return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType());
}
- private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) {
+ private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type pessimisticType) {
//e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType()) is the coerce type
final Expression lhs = binaryNode.lhs();
-
- newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
-
- return end(ensureSymbol(destType, binaryNode));
+ final BinaryNode newBinaryNode = ensureSymbolTypeOverride(binaryNode, pessimisticType, Type.widest(lhs.getType(), binaryNode.rhs().getType()));
+ newType(lhs.getSymbol(), newBinaryNode.getType()); //may not narrow if dest is already wider than destType
+ return end(newBinaryNode);
}
- private Expression ensureSymbol(final Type type, final Expression expr) {
+ private Expression ensureSymbol(final Expression expr, final Type type) {
LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type);
return temporarySymbols.ensureSymbol(lc, type, expr);
}
+ @Override
+ public boolean enterReturnNode(ReturnNode returnNode) {
+ tagOptimistic(returnNode.getExpression());
+ return true;
+ }
+
+ @Override
+ public boolean enterIfNode(IfNode ifNode) {
+ tagNeverOptimistic(ifNode.getTest());
+ return true;
+ }
+
+ @Override
+ public boolean enterWhileNode(WhileNode whileNode) {
+ tagNeverOptimistic(whileNode.getTest());
+ return true;
+ }
+
+ /**
+ * Used to signal that children should be optimistic. Otherwise every identnode
+ * in the entire program would basically start out as being guessed as an int
+ * and warmup would take an ENORMOUS time. This is also used while we get all
+ * the logic up and running, as we currently can't afford to debug every potential
+ * situtation that has to do with unwarranted optimism. We currently only tag
+ * type overrides, all other nodes are nops in this function
+ *
+ * @param expr an expression that is to be tagged as optimistic.
+ */
+ private long tag(final Optimistic expr) {
+ return ((long)lc.getCurrentFunction().getId() << 32) | expr.getProgramPoint();
+ }
+
+ /**
+ * This is used to guarantee that there are no optimistic setters, something that
+ * doesn't make sense in our current model, where only optimistic getters can exist.
+ * If we set something, we use the callSiteType. We might want to use dual fields
+ * though and incorporate this later for the option of putting something wider than
+ * is currently in the field causing an UnwarrantedOptimismException.
+ *
+ * @param expr expression to be tagged as never optimistic
+ */
+ private void tagNeverOptimistic(final Expression expr) {
+ if (expr instanceof Optimistic) {
+ LOG.info("Tagging TypeOverride node '" + expr + "' never optimistic");
+ neverOptimistic.add(tag((Optimistic)expr));
+ }
+ }
+
+ private void tagOptimistic(final Expression expr) {
+ if (expr instanceof Optimistic) {
+ LOG.info("Tagging TypeOverride node '" + expr + "' as optimistic");
+ optimistic.add(tag((Optimistic)expr));
+ }
+ }
+
+ private boolean isTaggedNeverOptimistic(final Optimistic expr) {
+ return neverOptimistic.contains(tag(expr));
+ }
+
+ private boolean isTaggedOptimistic(final Optimistic expr) {
+ return optimistic.contains(tag(expr));
+ }
+
+ private Type getOptimisticType(Optimistic expr) {
+ return useOptimisticTypes() ? env.getOptimisticType(expr) : expr.getMostPessimisticType();
+ }
+
+ /**
+ * This is the base function for typing a TypeOverride as optimistic. For any expression that
+ * can also be a type override (call, ident node (scope load), access node, index node) we use
+ * the override type to communicate optimism.
+ *
+ * @param pessimisticType conservative always guaranteed to work for this operation
+ * @param to node to set type for
+ */
+ private T ensureSymbolTypeOverride(final T node, final Type pessimisticType) {
+ return ensureSymbolTypeOverride(node, pessimisticType, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ private T ensureSymbolTypeOverride(final T node, final Type pessimisticType, final Type argumentsType) {
+ // check what the most optimistic type for this node should be
+ // if we are running with optimistic types, this starts out as e.g. int, and based on previous
+ // failed assumptions it can be wider, for example double if we have failed this assumption
+ // in a previous run
+ Type optimisticType = getOptimisticType(node);
+
+ if (argumentsType != null) {
+ optimisticType = Type.widest(optimisticType, argumentsType);
+ }
+
+ // the symbol of the expression is the pessimistic one, i.e. IndexNodes are always Object for consistency
+ // with the type system.
+ T expr = (T)ensureSymbol(node, pessimisticType);
+
+ if (optimisticType.isObject()) {
+ return expr;
+ }
+
+ if (isTaggedNeverOptimistic(expr)) {
+ return expr;
+ }
+
+ if(!(node instanceof FunctionCall && ((FunctionCall)node).isFunction())) {
+ // in the case that we have an optimistic type, set the type override (setType is inherited from TypeOverride)
+ // but maintain the symbol type set above. Also flag the function as optimistic. Don't do this for any
+ // expressions that are used as the callee of a function call.
+ if (optimisticType.narrowerThan(pessimisticType)) {
+ expr = (T)expr.setType(temporarySymbols, optimisticType);
+ expr = (T)Node.setIsOptimistic(expr, true);
+ if (optimisticType.isPrimitive()) {
+ final Symbol symbol = expr.getSymbol();
+ if (symbol.isShared()) {
+ expr = (T)expr.setSymbol(lc, symbol.createUnshared(symbol.getName()));
+ }
+ }
+ LOG.fine(expr, " turned optimistic with type=", optimisticType);
+ assert ((Optimistic)expr).isOptimistic();
+ }
+ }
+ return expr;
+ }
+
+
private Symbol newInternal(final String name, final Type type) {
- final Symbol iter = defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
- iter.setType(type); // NASHORN-73
- return iter;
+ return defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL).setType(type); //NASHORN-73
}
private static void newType(final Symbol symbol, final Type type) {
@@ -1845,34 +2117,46 @@ final class Attr extends NodeOperatorVisitor {
localUses.peek().add(name);
}
- /**
- * Pessimistically promote all symbols in current function node to Object types
- * This is done when the function contains unevaluated black boxes such as
- * lazy sub-function nodes that have not been compiled.
- *
- * @param body body for the function node we are leaving
- */
- private static void objectifySymbols(final Block body) {
- body.accept(new NodeVisitor(new LexicalContext()) {
- private void toObject(final Block block) {
- for (final Symbol symbol : block.getSymbols()) {
- if (!symbol.isTemp()) {
- newType(symbol, Type.OBJECT);
- }
- }
+ private void inferParameter(final Expression node, final Type type) {
+ final Symbol symbol = node.getSymbol();
+ if (useOptimisticTypes() && symbol.isParam()) {
+ final Type symbolType = symbol.getSymbolType();
+ if(symbolType.isBoolean() && !(type.isBoolean() || type == Type.OBJECT)) {
+ // boolean parameters can only legally be widened to Object
+ return;
}
+ if (symbolType != type) {
+ LOG.info("Infer parameter type " + symbol + " ==> " + type + " " + lc.getCurrentFunction().getSource().getName() + " " + lc.getCurrentFunction().getName());
+ }
+ symbol.setType(type); //will be overwritten by object later if pessimistic anyway
+ lc.logOptimisticAssumption(symbol, type);
+ }
+ }
- @Override
- public boolean enterBlock(final Block block) {
- toObject(block);
- return true;
- }
+ private BinaryNode coerce(final BinaryNode binaryNode, final Type pessimisticType) {
+ return coerce(binaryNode, pessimisticType, null);
+ }
- @Override
- public boolean enterFunctionNode(final FunctionNode node) {
- return false;
+ private BinaryNode coerce(final BinaryNode binaryNode, final Type pessimisticType, final Type argumentsType) {
+ BinaryNode newNode = ensureSymbolTypeOverride(binaryNode, pessimisticType, argumentsType);
+ inferParameter(binaryNode.lhs(), newNode.getType());
+ inferParameter(binaryNode.rhs(), newNode.getType());
+ return newNode;
+ }
+
+ private UnaryNode coerce(final UnaryNode unaryNode, final Type pessimisticType) {
+ return coerce(unaryNode, pessimisticType, null);
+ }
+
+ private UnaryNode coerce(final UnaryNode unaryNode, final Type pessimisticType, final Type argumentType) {
+ UnaryNode newNode = ensureSymbolTypeOverride(unaryNode, pessimisticType, argumentType);
+ if (newNode.isOptimistic()) {
+ if (unaryNode.getExpression() instanceof Optimistic) {
+ newNode = newNode.setExpression(Node.setIsOptimistic(unaryNode.getExpression(), true));
}
- });
+ }
+ inferParameter(unaryNode.getExpression(), newNode.getType());
+ return newNode;
}
private static String name(final Node node) {
diff --git a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
index ad9bdb07436..27e226c11ac 100644
--- a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
+++ b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
@@ -57,7 +57,7 @@ final class BranchOptimizer {
}
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()) {
case NOT:
diff --git a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
index 09c6a0651cb..42720b544b6 100644
--- a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -54,19 +54,23 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
-
-import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
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.RewriteException;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
@@ -106,6 +110,8 @@ import jdk.nashorn.internal.runtime.Source;
* @see Compiler
*/
public class ClassEmitter implements Emitter {
+ /** Default flags for class generation - public class */
+ private static final EnumSet DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
/** Sanity check flag - have we started on a class? */
private boolean classStarted;
@@ -125,9 +131,6 @@ public class ClassEmitter implements Emitter {
/** The script environment */
protected final ScriptEnvironment env;
- /** Default flags for class generation - oublic class */
- private static final EnumSet DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
-
/** Compile unit class name. */
private String unitClassName;
@@ -376,9 +379,19 @@ public class ClassEmitter implements Emitter {
static String disassemble(final byte[] bytecode) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
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() {
+ @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
*/
MethodEmitter method(final FunctionNode functionNode) {
+ final FunctionSignature signature = new FunctionSignature(functionNode);
final MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
functionNode.getName(),
- new FunctionSignature(functionNode).toString(),
+ signature.toString(),
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 method in the class
*
diff --git a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index 6ad92829bfe..da87fa7f43d 100644
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
@@ -45,20 +46,34 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getClassName;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_SCOPE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+import java.util.RandomAccess;
import java.util.Set;
import java.util.TreeMap;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
@@ -94,6 +109,7 @@ import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
+import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
@@ -120,17 +136,20 @@ import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.RewriteException;
import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.Undefined;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
+import jdk.nashorn.internal.runtime.options.Options;
/**
* This is the lowest tier of the code generator. It takes lowered ASTs emitted
@@ -153,9 +172,24 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
*/
final class CodeGenerator extends NodeOperatorVisitor {
+ private static final Type SCOPE_TYPE = Type.typeFor(ScriptObject.class);
+
private static final String GLOBAL_OBJECT = Type.getInternalName(Global.class);
- private static final String SCRIPTFUNCTION_IMPL_OBJECT = Type.getInternalName(ScriptFunctionImpl.class);
+ private static final String SCRIPTFUNCTION_IMPL_NAME = Type.getInternalName(ScriptFunctionImpl.class);
+ private static final Type SCRIPTFUNCTION_IMPL_TYPE = Type.typeFor(ScriptFunction.class);
+
+ private static final Call INIT_REWRITE_EXCEPTION = CompilerConstants.specialCallNoLookup(RewriteException.class,
+ "", void.class, UnwarrantedOptimismException.class, Object[].class);
+ private static final Call INIT_REWRITE_EXCEPTION_REST_OF = CompilerConstants.specialCallNoLookup(RewriteException.class,
+ "", void.class, UnwarrantedOptimismException.class, Object[].class, int[].class);
+
+ private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+ "ensureInt", int.class, Object.class, int.class);
+ private static final Call ENSURE_LONG = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+ "ensureLong", long.class, Object.class, int.class);
+ private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+ "ensureNumber", double.class, Object.class, int.class);
/** Constant data & installation. The only reason the compiler keeps this is because it is assigned
* by reflection in class installation */
@@ -183,10 +217,18 @@ final class CodeGenerator extends NodeOperatorVisitor emittedMethods = new HashSet<>();
+ // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
+ private final Map fnIdToContinuationInfo = new HashMap<>();
+
+ // Function Id -> (Function Id -> Function Data)). Used by compilation of most-optimistic function only.
+ private final Map> fnIdToNestedFunctions = new HashMap<>();
+
+ private final Deque