diff options
author | Edward Nevill edward.nevill@linaro.org <Edward Nevill edward.nevill@linaro.org> | 2013-12-23 13:00:33 +0000 |
---|---|---|
committer | Edward Nevill edward.nevill@linaro.org <Edward Nevill edward.nevill@linaro.org> | 2013-12-23 13:00:33 +0000 |
commit | d9c7bed615253d4d273f81faf9859f8f10f7cac9 (patch) | |
tree | 6917c0df28cda99c2bc4d23239045fa29a3f4858 | |
parent | db1032c1f2e2b6e679849ae4c8b0e7dd7605fc53 (diff) | |
parent | 7f54e85e6d7f3767c4ad17e5e336f0562657435d (diff) |
Remerge to jdk8-b117
217 files changed, 6257 insertions, 2167 deletions
@@ -222,3 +222,10 @@ f35e1255024b66f7cf82517798f45f6e194e5567 jdk8-b107 6ec2f9e5ed5bd60c2900976e6a54fdcac2f37e9e jdk8-b109 4d291109480a3f99d18ef98398479a245f68ebf2 preview_rc1 b35e0d2e2a246475b6ca01820e20fda98c72fde6 preview_rc2 +d49a8c2173f5f90c9a39cc4af8e03cfa8f35ee4c jdk8-b110 +75fd3486e584f20475c064a2cd4d01ac6406a511 jdk8-b111 +6a4fdb3bb4e34af4c5bb8db467bb01e13b1a7e31 jdk8-b112 +676cd7bf5e092356f7ee2116c8cf88cdc12377c7 jdk8-b113 +79f7b79bf97b71c9b5c9b103dbdef5f269eeb86d jdk8-b114 +f0d3ac2474ee755b1180ec71bcdfa190845b17eb jdk8-b115 +0fb1a427fbf6e04c77cebbbf99b6631c664ed793 jdk8-b116 diff --git a/make/build.xml b/make/build.xml index 4bc1a398..7d1f42ae 100644 --- a/make/build.xml +++ b/make/build.xml @@ -46,6 +46,16 @@ <available property="asm.available" classname="jdk.internal.org.objectweb.asm.Type"/> <!-- check if testng.jar is avaiable --> <available property="testng.available" file="${file.reference.testng.jar}"/> + <!-- check if Jemmy ang testng.jar are avaiable --> + <condition property="jemmy.jfx.testng.available" value="true"> + <and> + <available file="${file.reference.jemmyfx.jar}"/> + <available file="${file.reference.jemmycore.jar}"/> + <available file="${file.reference.jemmyawtinput.jar}"/> + <available file="${file.reference.jfxrt.jar}"/> + <isset property="testng.available"/> + </and> + </condition> <!-- enable/disable make code coverage --> <condition property="cc.enabled"> @@ -236,6 +246,10 @@ <fileset dir="${test.src.dir}/META-INF/services/"/> </copy> + <copy todir="${build.test.classes.dir}/jdk/nashorn/internal/runtime/resources"> + <fileset dir="${test.src.dir}/jdk/nashorn/internal/runtime/resources"/> + </copy> + <!-- tests that check nashorn internals and internal API --> <jar jarfile="${nashorn.internal.tests.jar}"> <fileset dir="${build.test.classes.dir}" excludes="**/api/**"/> @@ -245,6 +259,7 @@ <jar jarfile="${nashorn.api.tests.jar}"> <fileset dir="${build.test.classes.dir}" includes="**/api/**"/> <fileset dir="${build.test.classes.dir}" includes="**/META-INF/**"/> + <fileset dir="${build.test.classes.dir}" includes="**/resources/*.js"/> </jar> </target> @@ -346,6 +361,38 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" { </java> </target> + <target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available"> + <echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/> + </target> + + <target name="testjfx" depends="jar, check-jemmy.jfx.testng, compile-test" if="jemmy.jfx.testng.available"> + <fileset id="test.classes" dir="${build.test.classes.dir}"> + <include name="**/framework/*Test.class"/> + </fileset> + + <copy file="${file.reference.jfxrt.jar}" todir="dist"/> + + <condition property="jfx.prism.order" value="-Dprism.order=j2d" else=" "> + <not> + <os family="mac"/> + </not> + </condition> + + <testng outputdir="${build.test.results.dir}" classfilesetref="test.classes" + verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}"> + <jvmarg line="${ext.class.path}"/> + <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx}"/> + <propertyset> + <propertyref prefix="testjfx-test-sys-prop."/> + <mapper from="testjfx-test-sys-prop.*" to="*" type="glob"/> + </propertyset> + <sysproperty key="test.fork.jvm.options" value="${testjfx-test-sys-prop.test.fork.jvm.options} ${jfx.prism.order}"/> + <classpath> + <pathelement path="${testjfx.run.test.classpath}"/> + </classpath> + </testng> + </target> + <target name="test262" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available"> <fileset id="test.classes" dir="${build.test.classes.dir}"> <include name="**/framework/*Test.class"/> diff --git a/make/java.security.override b/make/java.security.override deleted file mode 100644 index a7edf33b..00000000 --- a/make/java.security.override +++ /dev/null @@ -1,14 +0,0 @@ -# We would like to avoid references from anywhere outside nashorn -# to codegen, IR and parser packages, in particular script generated classes. -# We ensure that by overriding "package.access" security property. - -# The following "package.access" value was copied from default java.security -# of jre/lib/security and appended with nashorn sensitive packages. - -# -# List of comma-separated packages that start with or equal this string -# will cause a security exception to be thrown when -# passed to checkPackageAccess unless the -# corresponding RuntimePermission ("accessClassInPackage."+package) has -# been granted. -package.access=sun.,com.sun.xml.internal.ws.,com.sun.xml.internal.bind.,com.sun.imageio.,com.sun.org.apache.xerces.internal.utils.,com.sun.org.apache.xalan.internal.utils.,com.sun.org.glassfish.external.,com.sun.org.glassfish.gmbal.,jdk.internal.,jdk.nashorn.internal.,jdk.nashorn.tools. diff --git a/make/project.properties b/make/project.properties index e6eea02e..c3ffca4e 100644 --- a/make/project.properties +++ b/make/project.properties @@ -118,6 +118,7 @@ test.trusted.dir=test/script/trusted test.external.dir=test/script/external test262.dir=${test.external.dir}/test262 test262.suite.dir=${test262.dir}/test/suite +testjfx.dir=${test.script.dir}/jfx test-sys-prop.test.dir=${test.dir} test-sys-prop.test.js.roots=${test.basic.dir} ${test.error.dir} ${test.sandbox.dir} ${test.trusted.dir} @@ -208,6 +209,32 @@ test262-test-sys-prop.test.js.framework=\ ${test262.dir}/test/harness/framework.js \ ${test262.dir}/test/harness/sta.js +# testjfx test root +testjfx-test-sys-prop.test.js.roots=${testjfx.dir} + +# execute testjfx tests in shared nashorn context or not? +testjfx-test-sys-prop.test.js.shared.context=false + +# framework root for our script tests +testjfx-test-sys-prop.test.js.framework=\ + -fx \ + ${test.script.dir}${file.separator}jfx.js + +file.reference.jemmyfx.jar=test${file.separator}lib${file.separator}JemmyFX.jar +file.reference.jemmycore.jar=test${file.separator}lib${file.separator}JemmyCore.jar +file.reference.jemmyawtinput.jar=test${file.separator}lib${file.separator}JemmyAWTInput.jar +file.reference.jfxrt.jar=${java.home}${file.separator}lib${file.separator}ext${file.separator}jfxrt.jar +testjfx.run.test.classpath=\ + ${file.reference.jemmyfx.jar}${path.separator}\ + ${file.reference.jemmycore.jar}${path.separator}\ + ${file.reference.jemmyawtinput.jar}${path.separator}\ + ${file.reference.testng.jar}${path.separator}\ + ${nashorn.internal.tests.jar}${path.separator}\ + ${nashorn.api.tests.jar} + +# testjfx VM options for script tests with @fork option +testjfx-test-sys-prop.test.fork.jvm.options=${run.test.jvmargs.main} -Xmx${run.test.xmx} -cp ${testjfx.run.test.classpath} + run.test.classpath=\ ${file.reference.testng.jar}:\ ${nashorn.internal.tests.jar}:\ @@ -234,7 +261,7 @@ run.test.jvmargs.main=${run.test.jvmargs.common} -ea #-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M run.test.jvmargs.octane.main=${run.test.jvmargs.common} -run.test.jvmsecurityargs=-Xverify:all -Djava.security.properties=${basedir}/make/java.security.override -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy +run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy # VM options for script tests with @fork option test-sys-prop.test.fork.jvm.options=${run.test.jvmargs.main} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} diff --git a/makefiles/BuildNashorn.gmk b/makefiles/BuildNashorn.gmk index 96bedf44..fc870442 100644 --- a/makefiles/BuildNashorn.gmk +++ b/makefiles/BuildNashorn.gmk @@ -37,35 +37,35 @@ NASHORN_VERSION := $(JDK_VERSION) NASHORN_FULL_VERSION := $(FULL_VERSION) ifdef MILESTONE - ifeq ($(MILESTONE),internal) + ifeq ($(MILESTONE), internal) NASHORN_VERSION = $(FULL_VERSION) endif endif # Need to use source and target 7 for nasgen to work. -$(eval $(call SetupJavaCompiler,GENERATE_NEWBYTECODE_DEBUG,\ - JVM:=$(JAVA),\ - JAVAC:=$(NEW_JAVAC),\ - FLAGS:=-g -source 7 -target 7 -bootclasspath $(JDK_CLASSES),\ - SERVER_DIR:=$(SJAVAC_SERVER_DIR),\ - SERVER_JVM:=$(SJAVAC_SERVER_JAVA))) +$(eval $(call SetupJavaCompiler,GENERATE_NEWBYTECODE_DEBUG, \ + JVM := $(JAVA), \ + JAVAC := $(NEW_JAVAC), \ + FLAGS := -g -source 7 -target 7 -bootclasspath $(JDK_CLASSES), \ + SERVER_DIR := $(SJAVAC_SERVER_DIR), \ + SERVER_JVM := $(SJAVAC_SERVER_JAVA))) # Build nashorn into intermediate directory -$(eval $(call SetupJavaCompilation,BUILD_NASHORN,\ - SETUP:=GENERATE_NEWBYTECODE_DEBUG,\ - SRC:=$(NASHORN_TOPDIR)/src,\ - COPY:=.properties .js,\ - BIN:=$(NASHORN_OUTPUTDIR)/nashorn_classes)) +$(eval $(call SetupJavaCompilation,BUILD_NASHORN, \ + SETUP := GENERATE_NEWBYTECODE_DEBUG, \ + SRC := $(NASHORN_TOPDIR)/src, \ + COPY := .properties .js, \ + BIN := $(NASHORN_OUTPUTDIR)/nashorn_classes)) NASGEN_SRC := $(NASHORN_TOPDIR)/buildtools/nasgen/src ASM_SRC := $(JDK_TOPDIR)/src/share/classes/jdk/internal/org/objectweb/asm # Build nasgen -$(eval $(call SetupJavaCompilation,BUILD_NASGEN,\ - SETUP:=GENERATE_NEWBYTECODE_DEBUG,\ - SRC:=$(NASGEN_SRC) $(ASM_SRC), \ - BIN:=$(NASHORN_OUTPUTDIR)/nasgen_classes,\ - ADD_JAVAC_FLAGS:=-cp $(NASHORN_OUTPUTDIR)/nashorn_classes)) +$(eval $(call SetupJavaCompilation,BUILD_NASGEN, \ + SETUP := GENERATE_NEWBYTECODE_DEBUG, \ + SRC := $(NASGEN_SRC) $(ASM_SRC), \ + BIN := $(NASHORN_OUTPUTDIR)/nasgen_classes, \ + ADD_JAVAC_FLAGS := -cp $(NASHORN_OUTPUTDIR)/nashorn_classes)) # Nasgen needs nashorn classes $(BUILD_NASGEN): $(BUILD_NASHORN) @@ -77,35 +77,36 @@ $(NASHORN_OUTPUTDIR)/classes/_the.nasgen.run: $(BUILD_NASGEN) $(RM) -rf $(@D)/jdk $(@D)/netscape $(CP) -R -p $(NASHORN_OUTPUTDIR)/nashorn_classes/* $(@D)/ $(FIXPATH) $(JAVA) \ - -cp "$(NASHORN_OUTPUTDIR)/nasgen_classes$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes" \ - jdk.nashorn.internal.tools.nasgen.Main $(@D) jdk.nashorn.internal.objects $(@D) + -cp "$(NASHORN_OUTPUTDIR)/nasgen_classes$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes" \ + jdk.nashorn.internal.tools.nasgen.Main $(@D) jdk.nashorn.internal.objects $(@D) $(TOUCH) $@ # Version file needs to be processed with version numbers VERSION_FILE := $(NASHORN_OUTPUTDIR)/classes/jdk/nashorn/internal/runtime/resources/version.properties + # Needs to happen after nasgen run since nasgen run deletes it $(VERSION_FILE): $(NASHORN_OUTPUTDIR)/classes/_the.nasgen.run $(VERSION_FILE): $(NASHORN_TOPDIR)/src/jdk/nashorn/internal/runtime/resources/version.properties-template $(ECHO) Creating version.properties $(MKDIR) -p $(@D) $(CAT) $< | $(SED) -e 's/$$(FULL_VERSION)/$(NASHORN_FULL_VERSION)/g' \ - -e 's/$$(RELEASE)/$(NASHORN_VERSION)/g' \ - -e '/^#.*$$/d' -e '/^$$/d' > $@ + -e 's/$$(RELEASE)/$(NASHORN_VERSION)/g' \ + -e '/^#.*$$/d' -e '/^$$/d' > $@ -MANIFEST_ATTRIBUTES:=Name: jdk/nashorn/\nImplementation-Title: Oracle Nashorn\nImplementation-Version: $(NASHORN_FULL_VERSION) +MANIFEST_ATTRIBUTES := Name: jdk/nashorn/\nImplementation-Title: Oracle Nashorn\nImplementation-Version: $(NASHORN_FULL_VERSION) # Create nashorn.jar from the final classes dir -$(eval $(call SetupArchive,BUILD_NASHORN_JAR,\ +$(eval $(call SetupArchive,BUILD_NASHORN_JAR, \ $(NASHORN_OUTPUTDIR)/classes/_the.nasgen.run \ - $(VERSION_FILE),\ - SRCS:=$(NASHORN_OUTPUTDIR)/classes,\ - SUFFIXES:=.class .js .properties Factory,\ - MANIFEST:=$(NASHORN_TOPDIR)/src/META-INF/MANIFEST.MF,\ - EXTRA_MANIFEST_ATTR:=$(MANIFEST_ATTRIBUTES),\ - SKIP_METAINF:=true,\ - JAR:=$(NASHORN_JAR))) + $(VERSION_FILE), \ + SRCS := $(NASHORN_OUTPUTDIR)/classes, \ + SUFFIXES := .class .js .properties Factory, \ + MANIFEST := $(NASHORN_TOPDIR)/src/META-INF/MANIFEST.MF, \ + EXTRA_MANIFEST_ATTR := $(MANIFEST_ATTRIBUTES), \ + SKIP_METAINF := true, \ + JAR := $(NASHORN_JAR))) all: $(NASHORN_JAR) - + .PHONY: all diff --git a/makefiles/Makefile b/makefiles/Makefile index 9539fe0e..c7d0dd00 100644 --- a/makefiles/Makefile +++ b/makefiles/Makefile @@ -24,19 +24,19 @@ # # Locate this Makefile -ifeq ($(filter /%,$(lastword $(MAKEFILE_LIST))),) - makefile_path:=$(CURDIR)/$(lastword $(MAKEFILE_LIST)) +ifeq ($(filter /%, $(lastword $(MAKEFILE_LIST))), ) + makefile_path := $(CURDIR)/$(lastword $(MAKEFILE_LIST)) else - makefile_path:=$(lastword $(MAKEFILE_LIST)) + makefile_path := $(lastword $(MAKEFILE_LIST)) endif -repo_dir:=$(patsubst %/makefiles/Makefile,%,$(makefile_path)) +repo_dir := $(patsubst %/makefiles/Makefile, %, $(makefile_path)) # What is the name of this subsystem (langtools, corba, etc)? -subsystem_name:=$(notdir $(repo_dir)) +subsystem_name := $(notdir $(repo_dir)) # Try to locate top-level makefile -top_level_makefile:=$(repo_dir)/../common/makefiles/Makefile -ifneq ($(wildcard $(top_level_makefile)),) +top_level_makefile := $(repo_dir)/../common/makefiles/Makefile +ifneq ($(wildcard $(top_level_makefile)), ) $(info Will run $(subsystem_name) target on top-level Makefile) $(info WARNING: This is a non-recommended way of building!) $(info ===================================================) diff --git a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java index d15fab99..6b55d81f 100644 --- a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java +++ b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java @@ -91,6 +91,7 @@ import java.util.StringTokenizer; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.support.Guards; +import jdk.internal.dynalink.support.Lookup; /** * Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the @@ -100,6 +101,9 @@ import jdk.internal.dynalink.support.Guards; * @version $Id: $ */ abstract class SingleDynamicMethod extends DynamicMethod { + + private static final MethodHandle CAN_CONVERT_TO = Lookup.findOwnStatic(MethodHandles.lookup(), "canConvertTo", boolean.class, LinkerServices.class, Class.class, Object.class); + SingleDynamicMethod(String name) { super(name); } @@ -201,23 +205,69 @@ abstract class SingleDynamicMethod extends DynamicMethod { return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector( callSiteLastArgType); } - if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) { - // Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive); - // link immediately to a vararg-packing method handle. - return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType); + + // This method handle takes the single argument and packs it into a newly allocated single-element array. It + // will be used when the incoming argument can't be converted to the vararg array type (the "vararg packer" + // method). + final MethodHandle varArgCollectingInvocation = createConvertingInvocation(collectArguments(fixTarget, + argsLen), linkerServices, callSiteType); + + // Is call site type assignable from an array type (e.g. Object:int[], or Object[]:String[]) + final boolean isAssignableFromArray = callSiteLastArgType.isAssignableFrom(varArgType); + // Do we have a custom conversion that can potentially convert the call site type to an array? + final boolean isCustomConvertible = linkerServices.canConvert(callSiteLastArgType, varArgType); + if(!isAssignableFromArray && !isCustomConvertible) { + // Call site signature guarantees the argument can definitely not be converted to an array (i.e. it is + // primitive), and no conversion can help with it either. Link immediately to a vararg-packing method + // handle. + return varArgCollectingInvocation; } - // Call site signature makes no guarantees that the single argument in the vararg position will be - // compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg - // method when it is not. - return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType), - createConvertingInvocation(fixTarget, linkerServices, callSiteType), - createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType)); + + // This method handle employs language-specific conversions to convert the last argument into an array of + // vararg type. + final MethodHandle arrayConvertingInvocation = createConvertingInvocation(MethodHandles.filterArguments( + fixTarget, fixParamsLen, linkerServices.getTypeConverter(callSiteLastArgType, varArgType)), + linkerServices, callSiteType); + + // This method handle determines whether the value can be converted to the array of vararg type using a + // language-specific conversion. + final MethodHandle canConvertArgToArray = MethodHandles.insertArguments(CAN_CONVERT_TO, 0, linkerServices, + varArgType); + + // This one adjusts the previous one for the location of the argument and the call site type. + final MethodHandle canConvertLastArgToArray = MethodHandles.dropArguments(canConvertArgToArray, 0, + MethodType.genericMethodType(fixParamsLen).parameterList()).asType(callSiteType.changeReturnType(boolean.class)); + + // This one takes the previous ones and combines them into a method handle that converts the argument into + // a vararg array when it can, otherwise falls back to the vararg packer. + final MethodHandle convertToArrayWhenPossible = MethodHandles.guardWithTest(canConvertLastArgToArray, + arrayConvertingInvocation, varArgCollectingInvocation); + + if(isAssignableFromArray) { + return MethodHandles.guardWithTest( + // Is incoming parameter already a compatible array? + Guards.isInstance(varArgType, fixParamsLen, callSiteType), + // Yes: just pass it to the method + createConvertingInvocation(fixTarget, linkerServices, callSiteType), + // No: either go through a custom conversion, or if it is not possible, go directly to the + // vararg packer. + isCustomConvertible ? convertToArrayWhenPossible : varArgCollectingInvocation); + } + + // Just do the custom conversion with fallback to the vararg packer logic. + assert isCustomConvertible; + return convertToArrayWhenPossible; } // Remaining case: more than one vararg. return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType); } + @SuppressWarnings("unused") + private static boolean canConvertTo(final LinkerServices linkerServices, Class<?> to, Object obj) { + return obj == null ? false : linkerServices.canConvert(obj.getClass(), to); + } + /** * Creates a method handle out of the original target that will collect the varargs for the exact component type of * the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs diff --git a/src/jdk/internal/dynalink/support/Guards.java b/src/jdk/internal/dynalink/support/Guards.java index 661cf334..42aa6758 100644 --- a/src/jdk/internal/dynalink/support/Guards.java +++ b/src/jdk/internal/dynalink/support/Guards.java @@ -88,6 +88,7 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.logging.Level; import java.util.logging.Logger; +import jdk.internal.dynalink.DynamicLinker; import jdk.internal.dynalink.linker.LinkerServices; /** @@ -115,11 +116,11 @@ public class Guards { public static MethodHandle isOfClass(Class<?> clazz, MethodType type) { final Class<?> declaredType = type.parameterType(0); if(clazz == declaredType) { - LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type }); + LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() }); return constantTrue(type); } if(!declaredType.isAssignableFrom(clazz)) { - LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type }); + LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() }); return constantFalse(type); } return getClassBoundArgumentTest(IS_OF_CLASS, clazz, 0, type); @@ -152,11 +153,11 @@ public class Guards { public static MethodHandle isInstance(Class<?> clazz, int pos, MethodType type) { final Class<?> declaredType = type.parameterType(pos); if(clazz.isAssignableFrom(declaredType)) { - LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type }); + LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() }); return constantTrue(type); } if(!declaredType.isAssignableFrom(clazz)) { - LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type }); + LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() }); return constantFalse(type); } return getClassBoundArgumentTest(IS_INSTANCE, clazz, pos, type); @@ -174,11 +175,11 @@ public class Guards { public static MethodHandle isArray(int pos, MethodType type) { final Class<?> declaredType = type.parameterType(pos); if(declaredType.isArray()) { - LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type }); + LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() }); return constantTrue(type); } if(!declaredType.isAssignableFrom(Object[].class)) { - LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type }); + LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() }); return constantFalse(type); } return asType(IS_ARRAY, pos, type); diff --git a/src/jdk/internal/dynalink/support/messages.properties b/src/jdk/internal/dynalink/support/messages.properties index eaf1f630..88d59908 100644 --- a/src/jdk/internal/dynalink/support/messages.properties +++ b/src/jdk/internal/dynalink/support/messages.properties @@ -76,11 +76,11 @@ # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -isInstanceGuardAlwaysTrue=isInstance guard for {0} in position {1} in method type {2} will always return true -isInstanceGuardAlwaysFalse=isInstance guard for {0} in position {1} in method type {2} will always return false +isInstanceGuardAlwaysTrue=isInstance guard for {0} in position {1} in method type {2} at {3} will always return true +isInstanceGuardAlwaysFalse=isInstance guard for {0} in position {1} in method type {2} at {3} will always return false -isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type {2} will always return true -isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} will always return false +isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return true +isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return false -isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} will always return true -isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} will always return false
\ No newline at end of file +isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} at {2} will always return true +isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} at {2} will always return false
\ No newline at end of file diff --git a/src/jdk/nashorn/api/scripting/AbstractJSObject.java b/src/jdk/nashorn/api/scripting/AbstractJSObject.java new file mode 100644 index 00000000..a7761645 --- /dev/null +++ b/src/jdk/nashorn/api/scripting/AbstractJSObject.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.api.scripting; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +/** + * This is the base class for nashorn ScriptObjectMirror class. + * + * This class can also be subclassed by an arbitrary Java class. Nashorn will + * treat objects of such classes just like nashorn script objects. Usual nashorn + * operations like obj[i], obj.foo, obj.func(), delete obj.foo will be glued + * to appropriate method call of this class. + */ +public abstract class AbstractJSObject implements JSObject { + /** + * Call this object as a JavaScript function. This is equivalent to + * 'func.apply(thiz, args)' in JavaScript. + * + * @param thiz 'this' object to be passed to the function + * @param args arguments to method + * @return result of call + */ + @Override + public Object call(final Object thiz, final Object... args) { + throw new UnsupportedOperationException("call"); + } + + /** + * Call this 'constructor' JavaScript function to create a new object. + * This is equivalent to 'new func(arg1, arg2...)' in JavaScript. + * + * @param args arguments to method + * @return result of constructor call + */ + @Override + public Object newObject(final Object... args) { + throw new UnsupportedOperationException("newObject"); + } + + /** + * Evaluate a JavaScript expression. + * + * @param s JavaScript expression to evaluate + * @return evaluation result + */ + @Override + public Object eval(final String s) { + throw new UnsupportedOperationException("eval"); + } + + /** + * Retrieves a named member of this JavaScript object. + * + * @param name of member + * @return member + */ + @Override + public Object getMember(final String name) { + return null; + } + + /** + * Retrieves an indexed member of this JavaScript object. + * + * @param index index slot to retrieve + * @return member + */ + @Override + public Object getSlot(final int index) { + return null; + } + + /** + * Does this object have a named member? + * + * @param name name of member + * @return true if this object has a member of the given name + */ + @Override + public boolean hasMember(final String name) { + return false; + } + + /** + * Does this object have a indexed property? + * + * @param slot index to check + * @return true if this object has a slot + */ + @Override + public boolean hasSlot(final int slot) { + return false; + } + + /** + * Remove a named member from this JavaScript object + * + * @param name name of the member + */ + @Override + public void removeMember(final String name) { + //empty + } + + /** + * Set a named member in this JavaScript object + * + * @param name name of the member + * @param value value of the member + */ + @Override + public void setMember(final String name, final Object value) { + //empty + } + + /** + * Set an indexed member in this JavaScript object + * + * @param index index of the member slot + * @param value value of the member + */ + @Override + public void setSlot(final int index, final Object value) { + //empty + } + + // property and value iteration + + /** + * Returns the set of all property names of this object. + * + * @return set of property names + */ + @Override + @SuppressWarnings("unchecked") + public Set<String> keySet() { + return Collections.EMPTY_SET; + } + + /** + * Returns the set of all property values of this object. + * + * @return set of property values. + */ + @Override + @SuppressWarnings("unchecked") + public Collection<Object> values() { + return Collections.EMPTY_SET; + } + + // JavaScript instanceof check + + /** + * Checking whether the given object is an instance of 'this' object. + * + * @param instance instace to check + * @return true if the given 'instance' is an instance of this 'function' object + */ + @Override + public boolean isInstance(final Object instance) { + return false; + } + + /** + * Checking whether this object is an instance of the given 'clazz' object. + * + * @param clazz clazz to check + * @return true if this object is an instance of the given 'clazz' + */ + @Override + public boolean isInstanceOf(final Object clazz) { + if (clazz instanceof JSObject) { + return ((JSObject)clazz).isInstance(this); + } + + return false; + } + + /** + * ECMA [[Class]] property + * + * @return ECMA [[Class]] property value of this object + */ + @Override + public String getClassName() { + return getClass().getName(); + } + + /** + * Is this a function object? + * + * @return if this mirror wraps a ECMAScript function instance + */ + @Override + public boolean isFunction() { + return false; + } + + /** + * Is this a 'use strict' function object? + * + * @return true if this mirror represents a ECMAScript 'use strict' function + */ + @Override + public boolean isStrictFunction() { + return false; + } + + /** + * Is this an array object? + * + * @return if this mirror wraps a ECMAScript array object + */ + @Override + public boolean isArray() { + return false; + } + + /** + * Returns this object's numeric value. + * + * @return this object's numeric value. + */ + @Override + public double toNumber() { + return Double.NaN; + } +} diff --git a/src/jdk/nashorn/api/scripting/JSObject.java b/src/jdk/nashorn/api/scripting/JSObject.java index 53b5790e..bd6e820b 100644 --- a/src/jdk/nashorn/api/scripting/JSObject.java +++ b/src/jdk/nashorn/api/scripting/JSObject.java @@ -30,14 +30,12 @@ import java.util.Collections; import java.util.Set; /** - * This is the base class for nashorn ScriptObjectMirror class. - * - * This class can also be subclassed by an arbitrary Java class. Nashorn will + * This interface can be implemented by an arbitrary Java class. Nashorn will * treat objects of such classes just like nashorn script objects. Usual nashorn * operations like obj[i], obj.foo, obj.func(), delete obj.foo will be glued - * to appropriate method call of this class. + * to appropriate method call of this interface. */ -public abstract class JSObject { +public interface JSObject { /** * Call this object as a JavaScript function. This is equivalent to * 'func.apply(thiz, args)' in JavaScript. @@ -46,9 +44,7 @@ public abstract class JSObject { * @param args arguments to method * @return result of call */ - public Object call(Object thiz, Object... args) { - throw new UnsupportedOperationException("call"); - } + public Object call(final Object thiz, final Object... args); /** * Call this 'constructor' JavaScript function to create a new object. @@ -57,9 +53,7 @@ public abstract class JSObject { * @param args arguments to method * @return result of constructor call */ - public Object newObject(Object... args) { - throw new UnsupportedOperationException("newObject"); - } + public Object newObject(final Object... args); /** * Evaluate a JavaScript expression. @@ -67,20 +61,7 @@ public abstract class JSObject { * @param s JavaScript expression to evaluate * @return evaluation result */ - public Object eval(String s) { - throw new UnsupportedOperationException("eval"); - } - - /** - * Call a JavaScript function member of this object. - * - * @param name name of the member function to call - * @param args arguments to be passed to the member function - * @return result of call - */ - public Object callMember(String name, Object... args) { - throw new UnsupportedOperationException("call"); - } + public Object eval(final String s); /** * Retrieves a named member of this JavaScript object. @@ -88,9 +69,7 @@ public abstract class JSObject { * @param name of member * @return member */ - public Object getMember(String name) { - return null; - } + public Object getMember(final String name); /** * Retrieves an indexed member of this JavaScript object. @@ -98,9 +77,7 @@ public abstract class JSObject { * @param index index slot to retrieve * @return member */ - public Object getSlot(int index) { - return null; - } + public Object getSlot(final int index); /** * Does this object have a named member? @@ -108,9 +85,7 @@ public abstract class JSObject { * @param name name of member * @return true if this object has a member of the given name */ - public boolean hasMember(String name) { - return false; - } + public boolean hasMember(final String name); /** * Does this object have a indexed property? @@ -118,17 +93,14 @@ public abstract class JSObject { * @param slot index to check * @return true if this object has a slot */ - public boolean hasSlot(int slot) { - return false; - } + public boolean hasSlot(final int slot); /** * Remove a named member from this JavaScript object * * @param name name of the member */ - public void removeMember(String name) { - } + public void removeMember(final String name); /** * Set a named member in this JavaScript object @@ -136,8 +108,7 @@ public abstract class JSObject { * @param name name of the member * @param value value of the member */ - public void setMember(String name, Object value) { - } + public void setMember(final String name, final Object value); /** * Set an indexed member in this JavaScript object @@ -145,8 +116,7 @@ public abstract class JSObject { * @param index index of the member slot * @param value value of the member */ - public void setSlot(int index, Object value) { - } + public void setSlot(final int index, final Object value); // property and value iteration @@ -155,20 +125,14 @@ public abstract class JSObject { * * @return set of property names */ - @SuppressWarnings("unchecked") - public Set<String> keySet() { - return Collections.EMPTY_SET; - } + public Set<String> keySet(); /** * Returns the set of all property values of this object. * * @return set of property values. */ - @SuppressWarnings("unchecked") - public Collection<Object> values() { - return Collections.EMPTY_SET; - } + public Collection<Object> values(); // JavaScript instanceof check @@ -178,9 +142,7 @@ public abstract class JSObject { * @param instance instace to check * @return true if the given 'instance' is an instance of this 'function' object */ - public boolean isInstance(final Object instance) { - return false; - } + public boolean isInstance(final Object instance); /** * Checking whether this object is an instance of the given 'clazz' object. @@ -188,47 +150,40 @@ public abstract class JSObject { * @param clazz clazz to check * @return true if this object is an instance of the given 'clazz' */ - public boolean isInstanceOf(final Object clazz) { - if (clazz instanceof JSObject) { - return ((JSObject)clazz).isInstance(this); - } - - return false; - } + public boolean isInstanceOf(final Object clazz); /** * ECMA [[Class]] property * * @return ECMA [[Class]] property value of this object */ - public String getClassName() { - return getClass().getName(); - } + public String getClassName(); /** * Is this a function object? * * @return if this mirror wraps a ECMAScript function instance */ - public boolean isFunction() { - return false; - } + public boolean isFunction(); /** * Is this a 'use strict' function object? * * @return true if this mirror represents a ECMAScript 'use strict' function */ - public boolean isStrictFunction() { - return false; - } + public boolean isStrictFunction(); /** * Is this an array object? * * @return if this mirror wraps a ECMAScript array object */ - public boolean isArray() { - return false; - } + public boolean isArray(); + + /** + * Returns this object's numeric value. + * + * @return this object's numeric value. + */ + public double toNumber(); } diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java index 4629665f..d0fb0c31 100644 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java @@ -285,11 +285,10 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C final URL url = ((URLReader)reader).getURL(); final Charset cs = ((URLReader)reader).getCharset(); return new Source(url.toString(), url, cs); - } else { - return new Source(getScriptName(ctxt), Source.readFully(reader)); } - } catch (final IOException ioExp) { - throw new ScriptException(ioExp); + return new Source(getScriptName(ctxt), Source.readFully(reader)); + } catch (final IOException e) { + throw new ScriptException(e); } } @@ -313,7 +312,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C if (! Modifier.isPublic(clazz.getModifiers())) { throw new SecurityException(getMessage("implementing.non.public.interface", clazz.getName())); } - Context.checkPackageAccess(clazz.getName()); + Context.checkPackageAccess(clazz); } ScriptObject realSelf = null; @@ -576,15 +575,14 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C return new CompiledScript() { @Override public Object eval(final ScriptContext ctxt) throws ScriptException { - final ScriptObject global = getNashornGlobalFrom(ctxt); + final ScriptObject globalObject = getNashornGlobalFrom(ctxt); // Are we running the script in the correct global? - if (func.getScope() == global) { - return evalImpl(func, ctxt, global); - } else { - // ScriptContext with a different global. Compile again! - // Note that we may still hit per-global compilation cache. - return evalImpl(compileImpl(source, ctxt), ctxt, global); + if (func.getScope() == globalObject) { + return evalImpl(func, ctxt, globalObject); } + // ScriptContext with a different global. Compile again! + // Note that we may still hit per-global compilation cache. + return evalImpl(compileImpl(source, ctxt), ctxt, globalObject); } @Override public ScriptEngine getEngine() { diff --git a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java index 3a27d23e..911f1663 100644 --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java @@ -41,8 +41,10 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import javax.script.Bindings; +import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.GlobalObject; +import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -50,7 +52,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime; /** * Mirror object that wraps a given Nashorn Script object. */ -public final class ScriptObjectMirror extends JSObject implements Bindings { +public final class ScriptObjectMirror extends AbstractJSObject implements Bindings { private static AccessControlContext getContextAccCtxt() { final Permissions perms = new Permissions(); perms.add(new RuntimePermission(Context.NASHORN_GET_CONTEXT)); @@ -161,7 +163,6 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { }); } - @Override public Object callMember(final String functionName, final Object... args) { functionName.getClass(); // null check final ScriptObject oldGlobal = Context.getGlobal(); @@ -594,14 +595,35 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { } /** - * Make a script object mirror on given object if needed. + * Utilitity to convert this script object to the given type. * - * @param obj object to be wrapped - * @param homeGlobal global to which this object belongs - * @return wrapped object + * @param type destination type to convert to + * @return converted object + */ + public <T> T to(final Class<T> type) { + return inGlobal(new Callable<T>() { + @Override + public T call() { + return type.cast(ScriptUtils.convert(sobj, type)); + } + }); + } + + /** + * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings. + * + * @param obj object to be wrapped/converted + * @param homeGlobal global to which this object belongs. Not used for ConsStrings. + * @return wrapped/converted object */ - public static Object wrap(final Object obj, final ScriptObject homeGlobal) { - return (obj instanceof ScriptObject && homeGlobal != null) ? new ScriptObjectMirror((ScriptObject)obj, homeGlobal) : obj; + public static Object wrap(final Object obj, final Object homeGlobal) { + if(obj instanceof ScriptObject) { + return homeGlobal instanceof ScriptObject ? new ScriptObjectMirror((ScriptObject)obj, (ScriptObject)homeGlobal) : obj; + } + if(obj instanceof ConsString) { + return obj.toString(); + } + return obj; } /** @@ -611,7 +633,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { * @param homeGlobal global to which this object belongs * @return unwrapped object */ - public static Object unwrap(final Object obj, final ScriptObject homeGlobal) { + public static Object unwrap(final Object obj, final Object homeGlobal) { if (obj instanceof ScriptObjectMirror) { final ScriptObjectMirror mirror = (ScriptObjectMirror)obj; return (mirror.global == homeGlobal)? mirror.sobj : obj; @@ -627,7 +649,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { * @param homeGlobal global to which this object belongs * @return wrapped array */ - public static Object[] wrapArray(final Object[] args, final ScriptObject homeGlobal) { + public static Object[] wrapArray(final Object[] args, final Object homeGlobal) { if (args == null || args.length == 0) { return args; } @@ -648,7 +670,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { * @param homeGlobal global to which this object belongs * @return unwrapped array */ - public static Object[] unwrapArray(final Object[] args, final ScriptObject homeGlobal) { + public static Object[] unwrapArray(final Object[] args, final Object homeGlobal) { if (args == null || args.length == 0) { return args; } @@ -705,4 +727,13 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { } } } + + @Override + public double toNumber() { + return inGlobal(new Callable<Double>() { + @Override public Double call() { + return JSType.toNumber(sobj); + } + }); + } } diff --git a/src/jdk/nashorn/api/scripting/ScriptUtils.java b/src/jdk/nashorn/api/scripting/ScriptUtils.java index ccd5879b..29d03db4 100644 --- a/src/jdk/nashorn/api/scripting/ScriptUtils.java +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java @@ -25,10 +25,17 @@ package jdk.nashorn.api.scripting; +import java.lang.invoke.MethodHandle; +import jdk.internal.dynalink.beans.StaticClass; +import jdk.internal.dynalink.linker.LinkerServices; +import jdk.nashorn.internal.runtime.linker.Bootstrap; +import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.ScriptFunction; +import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; /** - * Utilities that are to be called from script code + * Utilities that are to be called from script code. */ public final class ScriptUtils { private ScriptUtils() {} @@ -57,4 +64,109 @@ public final class ScriptUtils { public static String format(final String format, final Object[] args) { return Formatter.format(format, args); } + + /** + * Create a wrapper function that calls {@code func} synchronized on {@code sync} or, if that is undefined, + * {@code self}. Used to implement "sync" function in resources/mozilla_compat.js. + * + * @param func the function to invoke + * @param sync the object to synchronize on + * @return a synchronizing wrapper function + */ + public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) { + return func.makeSynchronizedFunction(sync); + } + + /** + * Make a script object mirror on given object if needed. + * + * @param obj object to be wrapped + * @return wrapped object + */ + public static Object wrap(final Object obj) { + if (obj instanceof ScriptObject) { + return ScriptObjectMirror.wrap(obj, Context.getGlobal()); + } + + return obj; + } + + /** + * Unwrap a script object mirror if needed. + * + * @param obj object to be unwrapped + * @return unwrapped object + */ + public static Object unwrap(final Object obj) { + if (obj instanceof ScriptObjectMirror) { + return ScriptObjectMirror.unwrap(obj, Context.getGlobal()); + } + + return obj; + } + + /** + * Wrap an array of object to script object mirrors if needed. + * + * @param args array to be unwrapped + * @return wrapped array + */ + public static Object[] wrapArray(final Object[] args) { + if (args == null || args.length == 0) { + return args; + } + + return ScriptObjectMirror.wrapArray(args, Context.getGlobal()); + } + + /** + * Unwrap an array of script object mirrors if needed. + * + * @param args array to be unwrapped + * @return unwrapped array + */ + public static Object[] unwrapArray(final Object[] args) { + if (args == null || args.length == 0) { + return args; + } + + return ScriptObjectMirror.unwrapArray(args, Context.getGlobal()); + } + + /** + * Convert the given object to the given type. + * + * @param obj object to be converted + * @param type destination type to convert to + * @return converted object + */ + public static Object convert(final Object obj, final Object type) { + if (obj == null) { + return null; + } + + final Class<?> clazz; + if (type instanceof Class) { + clazz = (Class<?>)type; + } else if (type instanceof StaticClass) { + clazz = ((StaticClass)type).getRepresentedClass(); + } else { + throw new IllegalArgumentException("type expected"); + } + + final LinkerServices linker = Bootstrap.getLinkerServices(); + final MethodHandle converter = linker.getTypeConverter(obj.getClass(), clazz); + if (converter == null) { + // no supported conversion! + throw new UnsupportedOperationException("conversion not supported"); + } + + try { + return converter.invoke(obj); + } catch (final RuntimeException | Error e) { + throw e; + } catch (final Throwable t) { + throw new RuntimeException(t); + } + } } diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java index 55355c3e..f1ced8ad 100644 --- a/src/jdk/nashorn/internal/codegen/Attr.java +++ b/src/jdk/nashorn/internal/codegen/Attr.java @@ -271,6 +271,7 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { functionNode.addDeclaredSymbol(symbol); if (varNode.isFunctionDeclaration()) { newType(symbol, FunctionNode.FUNCTION_TYPE); + symbol.setIsFunctionDeclaration(); } return varNode.setName((IdentNode)ident.setSymbol(lc, symbol)); } @@ -480,6 +481,10 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { } //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()) { @@ -489,11 +494,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { } } - if (newFunctionNode.hasLazyChildren()) { - //the final body has already been assigned as we have left the function node block body by now - objectifySymbols(body); - } - List<VarNode> syntheticInitializers = null; if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) { @@ -503,8 +503,8 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { syntheticInitializers.add(createSyntheticInitializer(newFunctionNode.getIdent(), CALLEE, newFunctionNode)); } - if(newFunctionNode.needsArguments()) { - if(syntheticInitializers == null) { + if (newFunctionNode.needsArguments()) { + if (syntheticInitializers == null) { syntheticInitializers = new ArrayList<>(1); } // "var arguments = :arguments" @@ -512,12 +512,12 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { ARGUMENTS, newFunctionNode)); } - if(syntheticInitializers != null) { - final List<Statement> stmts = body.getStatements(); + if (syntheticInitializers != null) { + final List<Statement> stmts = newFunctionNode.getBody().getStatements(); final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size()); newStatements.addAll(syntheticInitializers); newStatements.addAll(stmts); - newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements)); + newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements)); } if (returnTypes.peek().isUnknown()) { @@ -558,12 +558,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { } @Override - public Node leaveCONVERT(final UnaryNode unaryNode) { - assert false : "There should be no convert operators in IR during Attribution"; - return end(unaryNode); - } - - @Override public Node leaveIdentNode(final IdentNode identNode) { final String name = identNode.getName(); @@ -991,7 +985,7 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { @Override public Node leaveNEW(final UnaryNode unaryNode) { - return end(ensureSymbol(Type.OBJECT, unaryNode)); + return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew()))); } @Override @@ -1082,24 +1076,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { private boolean enterAssignmentNode(final BinaryNode binaryNode) { start(binaryNode); - final Node lhs = binaryNode.lhs(); - - if (lhs instanceof IdentNode) { - final Block block = lc.getCurrentBlock(); - final IdentNode ident = (IdentNode)lhs; - final String name = ident.getName(); - - Symbol symbol = findSymbol(block, name); - - if (symbol == null) { - symbol = defineSymbol(block, name, IS_GLOBAL); - } else { - maybeForceScope(symbol); - } - - addLocalDef(name); - } - return true; } @@ -1112,20 +1088,33 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { * @param binaryNode assignment node */ private Node leaveAssignmentNode(final BinaryNode binaryNode) { - BinaryNode newBinaryNode = binaryNode; - final Expression lhs = binaryNode.lhs(); final Expression rhs = binaryNode.rhs(); final Type type; + if (lhs instanceof IdentNode) { + final Block block = lc.getCurrentBlock(); + final IdentNode ident = (IdentNode)lhs; + final String name = ident.getName(); + final Symbol symbol = findSymbol(block, name); + + if (symbol == null) { + defineSymbol(block, name, IS_GLOBAL); + } else { + maybeForceScope(symbol); + } + + addLocalDef(name); + } + if (rhs.getType().isNumeric()) { - type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); + type = Type.widest(lhs.getType(), rhs.getType()); } else { type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too. } newType(lhs.getSymbol(), type); - return end(ensureSymbol(type, newBinaryNode)); + return end(ensureSymbol(type, binaryNode)); } private boolean isLocal(FunctionNode function, Symbol symbol) { @@ -1276,12 +1265,17 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { @Override public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { - return end(ensureSymbol(binaryNode.rhs().getType(), binaryNode)); + return leaveComma(binaryNode, binaryNode.rhs()); } @Override public Node leaveCOMMALEFT(final BinaryNode binaryNode) { - return end(ensureSymbol(binaryNode.lhs().getType(), binaryNode)); + return leaveComma(binaryNode, binaryNode.lhs()); + } + + private Node leaveComma(final BinaryNode commaNode, final Expression effectiveExpr) { + ensureTypeNotUnknown(effectiveExpr); + return end(ensureSymbol(effectiveExpr.getType(), commaNode)); } @Override @@ -1292,7 +1286,9 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { 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)); } @@ -1635,7 +1631,7 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) { LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to); Symbol symbol = node.getSymbol(); - if(symbol.isShared() && symbol.wouldChangeType(to)) { + if (symbol.isShared() && symbol.wouldChangeType(to)) { symbol = temporarySymbols.getTypedTemporarySymbol(to); } newType(symbol, to); @@ -1651,40 +1647,105 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { return !node.isLazy(); } - /** - * Eg. - * - * var d = 17; - * var e; - * e = d; //initially typed as int for node type, should retype as double - * e = object; - * - * var d = 17; - * var e; - * e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric - * e = object; - * - */ + // + // Eg. + // + // var d = 17; + // var e; + // e = d; //initially typed as int for node type, should retype as double + // e = object; + // + // var d = 17; + // var e; + // e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric + // e = object; + // @SuppressWarnings("fallthrough") @Override public Node leaveBinaryNode(final BinaryNode binaryNode) { final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); BinaryNode newBinaryNode = binaryNode; - switch (binaryNode.tokenType()) { - default: - if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) { + + if (isAdd(binaryNode)) { + newBinaryNode = (BinaryNode)widen(newBinaryNode, widest); + if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) { + return new RuntimeNode(newBinaryNode, Request.ADD); + } + } else if (binaryNode.isComparison()) { + final Expression lhs = newBinaryNode.lhs(); + final Expression rhs = newBinaryNode.rhs(); + + Type cmpWidest = Type.widest(lhs.getType(), rhs.getType()); + + boolean newRuntimeNode = false, finalized = false; + switch (newBinaryNode.tokenType()) { + case EQ_STRICT: + case NE_STRICT: + if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { + newRuntimeNode = true; + cmpWidest = Type.OBJECT; + finalized = true; + } + //fallthru + default: + if (newRuntimeNode || cmpWidest.isObject()) { + return new RuntimeNode(newBinaryNode, Request.requestFor(binaryNode)).setIsFinal(finalized); + } break; } + + return newBinaryNode; + } else { + if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) { + return newBinaryNode; + } + checkThisAssignment(binaryNode); newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest)); - case ADD: newBinaryNode = (BinaryNode)widen(newBinaryNode, widest); } + return newBinaryNode; + + } + + private boolean isAdd(final Node node) { + return node.isTokenType(TokenType.ADD); + } + + /** + * Determine if the outcome of + operator is a string. + * + * @param node Node to test. + * @return true if a string result. + */ + private boolean isAddString(final Node node) { + if (node instanceof BinaryNode && isAdd(node)) { + final BinaryNode binaryNode = (BinaryNode)node; + final Node lhs = binaryNode.lhs(); + final Node rhs = binaryNode.rhs(); + + return isAddString(lhs) || isAddString(rhs); + } + + return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString(); + } + + private void checkThisAssignment(final BinaryNode binaryNode) { + if (binaryNode.isAssignment()) { + if (binaryNode.lhs() instanceof AccessNode) { + final AccessNode accessNode = (AccessNode) binaryNode.lhs(); + + if (accessNode.getBase().getSymbol().isThis()) { + lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName()); + } + } + } } }); lc.replace(currentFunctionNode, newFunctionNode); currentFunctionNode = newFunctionNode; } while (!changed.isEmpty()); + return currentFunctionNode; } @@ -1697,7 +1758,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { final Expression lhs = binaryNode.lhs(); newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType -// ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine return end(ensureSymbol(destType, binaryNode)); } diff --git a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java index 02ea95f1..ad9bdb07 100644 --- a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java +++ b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java @@ -56,10 +56,6 @@ final class BranchOptimizer { branchOptimizer(node, label, state); } - private void load(final Expression node) { - codegen.load(node); - } - private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) { final Expression rhs = unaryNode.rhs(); @@ -67,19 +63,16 @@ final class BranchOptimizer { case NOT: branchOptimizer(rhs, label, !state); return; - case CONVERT: + default: if (unaryNode.getType().isBoolean()) { branchOptimizer(rhs, label, state); return; } break; - default: - break; } // convert to boolean - load(unaryNode); - method.convert(Type.BOOLEAN); + codegen.load(unaryNode, Type.BOOLEAN); if (state) { method.ifne(label); } else { @@ -118,45 +111,33 @@ final class BranchOptimizer { case EQ: case EQ_STRICT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType())); method.conditionalJump(state ? EQ : NE, true, label); return; case NE: case NE_STRICT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType())); method.conditionalJump(state ? NE : EQ, true, label); return; case GE: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType())); method.conditionalJump(state ? GE : LT, !state, label); return; case GT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType())); method.conditionalJump(state ? GT : LE, !state, label); return; case LE: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType())); method.conditionalJump(state ? LE : GT, state, label); return; case LT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol() + " in " + binaryNode; - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType())); method.conditionalJump(state ? LT : GE, state, label); return; @@ -164,8 +145,7 @@ final class BranchOptimizer { break; } - load(binaryNode); - method.convert(Type.BOOLEAN); + codegen.load(binaryNode, Type.BOOLEAN); if (state) { method.ifne(label); } else { @@ -187,8 +167,7 @@ final class BranchOptimizer { } } - load(node); - method.convert(Type.BOOLEAN); + codegen.load(node, Type.BOOLEAN); if (state) { method.ifne(label); } else { diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index d16a1e5b..f5c1fb87 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -43,7 +43,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; -import static jdk.nashorn.internal.codegen.CompilerConstants.staticField; import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; @@ -60,7 +59,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Locale; import java.util.Set; import java.util.TreeMap; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; @@ -111,7 +109,6 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.objects.Global; @@ -217,12 +214,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex * @param identNode an identity node to load * @return the method generator used */ - private MethodEmitter loadIdent(final IdentNode identNode) { + private MethodEmitter loadIdent(final IdentNode identNode, final Type type) { final Symbol symbol = identNode.getSymbol(); if (!symbol.isScope()) { assert symbol.hasSlot() || symbol.isParam(); - return method.load(symbol); + return method.load(symbol).convert(type); } final String name = symbol.getName(); @@ -243,11 +240,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex if (isFastScope(symbol)) { // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope. if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) { - return loadSharedScopeVar(identNode.getType(), symbol, flags); + return loadSharedScopeVar(type, symbol, flags); } - return loadFastScopeVar(identNode.getType(), symbol, flags, identNode.isFunction()); + return loadFastScopeVar(type, symbol, flags, identNode.isFunction()); } - return method.dynamicGet(identNode.getType(), identNode.getName(), flags, identNode.isFunction()); + return method.dynamicGet(type, identNode.getName(), flags, identNode.isFunction()); } } @@ -313,9 +310,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod); } - private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) { + private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) { loadFastScopeProto(symbol, true); - method.dynamicSet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE); + method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE); return method; } @@ -359,18 +356,64 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex * @return the method emitter used */ MethodEmitter load(final Expression node) { - return load(node, false); + return load(node, node.hasType() ? node.getType() : null, false); + } + + // Test whether conversion from source to target involves a call of ES 9.1 ToPrimitive + // with possible side effects from calling an object's toString or valueOf methods. + private boolean noToPrimitiveConversion(final Type source, final Type target) { + // Object to boolean conversion does not cause ToPrimitive call + return source.isJSPrimitive() || !target.isJSPrimitive() || target.isBoolean(); + } + + MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type) { + return loadBinaryOperands(lhs, rhs, type, false); + } + + private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type, final boolean baseAlreadyOnStack) { + // ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary + // expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT + // RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we + // can combine a LOAD with a CONVERT operation (e.g. use a dynamic getter with the conversion target type as its + // return value). What we do here is reorder LOAD RIGHT and CONVERT LEFT when possible; it is possible only when + // we can prove that executing CONVERT LEFT can't have a side effect that changes the value of LOAD RIGHT. + // Basically, if we know that either LEFT already is a primitive value, or does not have to be converted to + // a primitive value, or RIGHT is an expression that loads without side effects, then we can do the + // reordering and collapse LOAD/CONVERT into a single operation; otherwise we need to do the more costly + // separate operations to preserve specification semantics. + if (noToPrimitiveConversion(lhs.getType(), type) || rhs.isLocal()) { + // Can reorder. Combine load and convert into single operations. + load(lhs, type, baseAlreadyOnStack); + load(rhs, type, false); + } else { + // Can't reorder. Load and convert separately. + load(lhs, lhs.getType(), baseAlreadyOnStack); + load(rhs, rhs.getType(), false); + method.swap().convert(type).swap().convert(type); + } + + return method; + } + + MethodEmitter loadBinaryOperands(final BinaryNode node) { + return loadBinaryOperands(node.lhs(), node.rhs(), node.getType(), false); + } + + MethodEmitter load(final Expression node, final Type type) { + return load(node, type, false); } - private MethodEmitter load(final Expression node, final boolean baseAlreadyOnStack) { + private MethodEmitter load(final Expression node, final Type type, final boolean baseAlreadyOnStack) { final Symbol symbol = node.getSymbol(); // If we lack symbols, we just generate what we see. - if (symbol == null) { + if (symbol == null || type == null) { node.accept(this); return method; } + assert !type.isUnknown(); + /* * The load may be of type IdentNode, e.g. "x", AccessNode, e.g. "x.y" * or IndexNode e.g. "x[y]". Both AccessNodes and IndexNodes are @@ -378,30 +421,30 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex */ final CodeGenerator codegen = this; - node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + node.accept(new NodeVisitor<LexicalContext>(lc) { @Override public boolean enterIdentNode(final IdentNode identNode) { - loadIdent(identNode); + loadIdent(identNode, type); return false; } @Override public boolean enterAccessNode(final AccessNode accessNode) { if (!baseAlreadyOnStack) { - load(accessNode.getBase()).convert(Type.OBJECT); + load(accessNode.getBase(), Type.OBJECT); } assert method.peekType().isObject(); - method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction()); + method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction()); return false; } @Override public boolean enterIndexNode(final IndexNode indexNode) { if (!baseAlreadyOnStack) { - load(indexNode.getBase()).convert(Type.OBJECT); + load(indexNode.getBase(), Type.OBJECT); load(indexNode.getIndex()); } - method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction()); + method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction()); return false; } @@ -409,14 +452,36 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex public boolean enterFunctionNode(FunctionNode functionNode) { // function nodes will always leave a constructed function object on stack, no need to load the symbol // separately as in enterDefault() + lc.pop(functionNode); functionNode.accept(codegen); + // NOTE: functionNode.accept() will produce a different FunctionNode that we discard. This incidentally + // doesn't cause problems as we're never touching FunctionNode again after it's visited here - codegen + // is the last element in the compilation pipeline, the AST it produces is not used externally. So, we + // re-push the original functionNode. + lc.push(functionNode); + method.convert(type); return false; } @Override + public boolean enterCallNode(CallNode callNode) { + return codegen.enterCallNode(callNode, type); + } + + @Override + public boolean enterLiteralNode(LiteralNode<?> literalNode) { + return codegen.enterLiteralNode(literalNode, type); + } + + @Override public boolean enterDefault(final Node otherNode) { + final Node currentDiscard = codegen.lc.getCurrentDiscard(); otherNode.accept(codegen); // generate code for whatever we are looking at. - method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there) + if(currentDiscard != otherNode) { + method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there) + assert method.peekType() != null; + method.convert(type); + } return false; } }); @@ -566,11 +631,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final Type[] params = signature == null ? null : Type.getMethodArguments(signature); for (final Expression arg : args) { assert arg != null; - load(arg); if (n >= argCount) { + load(arg); method.pop(); // we had to load the arg for its side effects } else if (params != null) { - method.convert(params[n]); + load(arg, params[n]); + } else { + load(arg); } n++; } @@ -583,15 +650,19 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex return argCount; } + @Override public boolean enterCallNode(final CallNode callNode) { + return enterCallNode(callNode, callNode.getType()); + } + + private boolean enterCallNode(final CallNode callNode, final Type callNodeType) { lineNumber(callNode.getLineNumber()); final List<Expression> args = callNode.getArgs(); final Expression function = callNode.getFunction(); final Block currentBlock = lc.getCurrentBlock(); final CodeGeneratorLexicalContext codegenLexicalContext = lc; - final Type callNodeType = callNode.getType(); function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @@ -612,16 +683,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } private void scopeCall(final IdentNode node, final int flags) { - load(node); - method.convert(Type.OBJECT); // foo() makes no sense if foo == 3 + load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. method.loadNull(); //the 'this' method.dynamicCall(callNodeType, 2 + loadArgs(args), flags); } private void evalCall(final IdentNode node, final int flags) { - load(node); - method.convert(Type.OBJECT); // foo() makes no sense if foo == 3 + load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3 final Label not_eval = new Label("not_eval"); final Label eval_done = new Label("eval_done"); @@ -638,8 +707,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); // load evaluated code - load(evalArgs.getCode()); - method.convert(Type.OBJECT); + load(evalArgs.getCode(), Type.OBJECT); + // load second and subsequent args for side-effect + final List<Expression> args = callNode.getArgs(); + final int numArgs = args.size(); + for (int i = 1; i < numArgs; i++) { + load(args.get(i)).pop(); + } // special/extra 'eval' arguments load(evalArgs.getThis()); method.load(evalArgs.getLocation()); @@ -690,13 +764,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override public boolean enterAccessNode(final AccessNode node) { - load(node.getBase()); - method.convert(Type.OBJECT); + load(node.getBase(), Type.OBJECT); method.dup(); method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true); method.swap(); method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags()); - assert method.peekType().equals(callNodeType); return false; } @@ -727,18 +799,17 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override public boolean enterIndexNode(final IndexNode node) { - load(node.getBase()); - method.convert(Type.OBJECT); + load(node.getBase(), Type.OBJECT); method.dup(); - load(node.getIndex()); final Type indexType = node.getIndex().getType(); if (indexType.isObject() || indexType.isBoolean()) { - method.convert(Type.OBJECT); //TODO + load(node.getIndex(), Type.OBJECT); //TODO + } else { + load(node.getIndex()); } method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true); method.swap(); method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags()); - assert method.peekType().equals(callNode.getType()); return false; } @@ -746,11 +817,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override protected boolean enterDefault(final Node node) { // Load up function. - load(function); - method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions + load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE); - assert method.peekType().equals(callNode.getType()); return false; } @@ -853,8 +922,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final Expression init = forNode.getInit(); - load(modify); - assert modify.getType().isObject(); + load(modify, Type.OBJECT); method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR); method.store(iter); method._goto(forNode.getContinueLabel()); @@ -1203,8 +1271,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex if (element == null) { method.loadEmpty(elementType); } else { - assert elementType.isEquivalentTo(element.getType()) : "array element type doesn't match array type"; - load(element); + load(element, elementType); } method.arraystore(); @@ -1217,7 +1284,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex for (int i = 0; i < args.size(); i++) { method.dup(); method.load(i); - load(args.get(i)).convert(Type.OBJECT); //has to be upcast to object or we fail + load(args.get(i), Type.OBJECT); //has to be upcast to object or we fail method.arraystore(); } @@ -1274,7 +1341,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } // literal values - private MethodEmitter load(final LiteralNode<?> node) { + private MethodEmitter loadLiteral(final LiteralNode<?> node, final Type type) { final Object value = node.getValue(); if (value == null) { @@ -1294,15 +1361,26 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } else if (value instanceof Boolean) { method.load((Boolean)value); } else if (value instanceof Integer) { - method.load((Integer)value); + if(type.isEquivalentTo(Type.NUMBER)) { + method.load(((Integer)value).doubleValue()); + } else if(type.isEquivalentTo(Type.LONG)) { + method.load(((Integer)value).longValue()); + } else { + method.load((Integer)value); + } } else if (value instanceof Long) { - method.load((Long)value); + if(type.isEquivalentTo(Type.NUMBER)) { + method.load(((Long)value).doubleValue()); + } else { + method.load((Long)value); + } } else if (value instanceof Double) { method.load((Double)value); } else if (node instanceof ArrayLiteralNode) { - final ArrayType type = (ArrayType)node.getType(); - loadArray((ArrayLiteralNode)node, type); - globalAllocateArray(type); + final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node; + final ArrayType atype = arrayLiteral.getArrayType(); + loadArray(arrayLiteral, atype); + globalAllocateArray(atype); } else { assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value; } @@ -1346,8 +1424,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override public boolean enterLiteralNode(final LiteralNode<?> literalNode) { + return enterLiteralNode(literalNode, literalNode.getType()); + } + + private boolean enterLiteralNode(final LiteralNode<?> literalNode, final Type type) { assert literalNode.getSymbol() != null : literalNode + " has no symbol"; - load(literalNode).store(literalNode.getSymbol()); + loadLiteral(literalNode, type).convert(type).store(literalNode.getSymbol()); return false; } @@ -1622,10 +1704,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex return enterCmp(lhs, rhs, Condition.GT, type, symbol); case ADD: Type widest = Type.widest(lhs.getType(), rhs.getType()); - load(lhs); - method.convert(widest); - load(rhs); - method.convert(widest); + load(lhs, widest); + load(rhs, widest); method.add(); method.convert(type); method.store(symbol); @@ -1638,15 +1718,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) { - return false; + return false; } if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) { - return false; + return false; } for (final Expression arg : args) { - load(arg).convert(Type.OBJECT); //TODO this should not be necessary below Lower + load(arg, Type.OBJECT); } method.invokestatic( @@ -1827,6 +1907,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } if (cases.isEmpty()) { + // still evaluate expression for side-effects. + load(expression).pop(); method.label(breakLabel); return false; } @@ -1901,24 +1983,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex method.lookupswitch(defaultLabel, ints, labels); } } else { - load(expression); - - if (expression.getType().isInteger()) { - method.convert(Type.NUMBER).dup(); - method.store(tag); - method.conditionalJump(Condition.NE, true, defaultLabel); - } else { - assert tag.getSymbolType().isObject(); - method.convert(Type.OBJECT); //e.g. 1 literal pushed and tag is object - method.store(tag); - } + load(expression, Type.OBJECT); + method.store(tag); for (final CaseNode caseNode : cases) { final Expression test = caseNode.getTest(); if (test != null) { method.load(tag); - load(test); + load(test, Type.OBJECT); method.invoke(ScriptRuntime.EQ_STRICT); method.ifne(caseNode.getEntry()); } @@ -1956,11 +2029,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final Expression expression = throwNode.getExpression(); final int position = throwNode.position(); - final int line = source.getLine(position); + final int line = throwNode.getLineNumber(); final int column = source.getColumn(position); - load(expression); - assert expression.getType().isObject(); + load(expression, Type.OBJECT); method.load(source.getName()); method.load(line); @@ -2040,7 +2112,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex if (exceptionCondition != null) { next = new Label("next"); - load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next); + load(exceptionCondition, Type.BOOLEAN).ifeq(next); } else { next = null; } @@ -2085,29 +2157,28 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex lineNumber(varNode); - final Symbol varSymbol = varNode.getName().getSymbol(); - assert varSymbol != null : "variable node " + varNode + " requires a name with a symbol"; + final IdentNode identNode = varNode.getName(); + final Symbol identSymbol = identNode.getSymbol(); + assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol"; assert method != null; - final boolean needsScope = varSymbol.isScope(); + final boolean needsScope = identSymbol.isScope(); if (needsScope) { method.loadCompilerConstant(SCOPE); } - load(init); if (needsScope) { + load(init); int flags = CALLSITE_SCOPE | getCallSiteFlags(); - final IdentNode identNode = varNode.getName(); - final Type type = identNode.getType(); - if (isFastScope(varSymbol)) { - storeFastScopeVar(type, varSymbol, flags); + if (isFastScope(identSymbol)) { + storeFastScopeVar(identSymbol, flags); } else { - method.dynamicSet(type, identNode.getName(), flags); + method.dynamicSet(identNode.getName(), flags); } } else { - method.convert(varNode.getName().getType()); // aw: convert moved here - method.store(varSymbol); + load(init, identNode.getType()); + method.store(identSymbol); } return false; @@ -2166,8 +2237,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex tryLabel = null; } - load(expression); - assert expression.getType().isObject() : "with expression needs to be object: " + expression; + load(expression, Type.OBJECT); if (hasScope) { // Construct a WithObject if we have a scope @@ -2209,54 +2279,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override public boolean enterADD(final UnaryNode unaryNode) { - load(unaryNode.rhs()); - assert unaryNode.rhs().getType().isNumber() : unaryNode.rhs().getType() + " "+ unaryNode.getSymbol(); + load(unaryNode.rhs(), unaryNode.getType()); + assert unaryNode.getType().isNumeric(); method.store(unaryNode.getSymbol()); - return false; } @Override public boolean enterBIT_NOT(final UnaryNode unaryNode) { - load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol()); - return false; - } - - // do this better with convert calls to method. TODO - @Override - public boolean enterCONVERT(final UnaryNode unaryNode) { - final Expression rhs = unaryNode.rhs(); - final Type to = unaryNode.getType(); - - if (to.isObject() && rhs instanceof LiteralNode) { - final LiteralNode<?> literalNode = (LiteralNode<?>)rhs; - final Object value = literalNode.getValue(); - - if (value instanceof Number) { - assert !to.isArray() : "type hygiene - cannot convert number to array: (" + to.getTypeClass().getSimpleName() + ')' + value; - if (value instanceof Integer) { - method.load((Integer)value); - } else if (value instanceof Long) { - method.load((Long)value); - } else if (value instanceof Double) { - method.load((Double)value); - } else { - assert false; - } - method.convert(Type.OBJECT); - } else if (value instanceof Boolean) { - method.getField(staticField(Boolean.class, value.toString().toUpperCase(Locale.ENGLISH), Boolean.class)); - } else { - load(rhs); - method.convert(unaryNode.getType()); - } - } else { - load(rhs); - method.convert(unaryNode.getType()); - } - - method.store(unaryNode.getSymbol()); - + load(unaryNode.rhs(), Type.INT).load(-1).xor().store(unaryNode.getSymbol()); return false; } @@ -2274,9 +2305,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override protected void evaluate() { - load(rhs, true); - - method.convert(type); + load(rhs, type, true); if (!isPostfix) { if (type.isInteger()) { method.load(isIncrement ? 1 : -1); @@ -2330,7 +2359,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final List<Expression> args = callNode.getArgs(); // Load function reference. - load(callNode.getFunction()).convert(Type.OBJECT); // must detect type error + load(callNode.getFunction(), Type.OBJECT); // must detect type error method.dynamicNew(1 + loadArgs(args), getCallSiteFlags()); method.store(unaryNode.getSymbol()); @@ -2342,12 +2371,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex public boolean enterNOT(final UnaryNode unaryNode) { final Expression rhs = unaryNode.rhs(); - load(rhs); + load(rhs, Type.BOOLEAN); final Label trueLabel = new Label("true"); final Label afterLabel = new Label("after"); - method.convert(Type.BOOLEAN); method.ifne(trueLabel); method.load(true); method._goto(afterLabel); @@ -2361,8 +2389,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override public boolean enterSUB(final UnaryNode unaryNode) { - load(unaryNode.rhs()).neg().store(unaryNode.getSymbol()); - + assert unaryNode.getType().isNumeric(); + load(unaryNode.rhs(), unaryNode.getType()).neg().store(unaryNode.getSymbol()); return false; } @@ -2375,9 +2403,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } private void enterNumericAdd(final Expression lhs, final Expression rhs, final Type type, final Symbol symbol) { - assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs); - load(lhs); - load(rhs); + loadBinaryOperands(lhs, rhs, type); method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack? method.store(symbol); } @@ -2391,8 +2417,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex if (type.isNumeric()) { enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol()); } else { - load(lhs).convert(Type.OBJECT); - load(rhs).convert(Type.OBJECT); + loadBinaryOperands(binaryNode); method.add(); method.store(binaryNode.getSymbol()); } @@ -2406,7 +2431,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final Label skip = new Label("skip"); - load(lhs).convert(Type.OBJECT).dup().convert(Type.BOOLEAN); + load(lhs, Type.OBJECT).dup().convert(Type.BOOLEAN); if (binaryNode.tokenType() == TokenType.AND) { method.ifeq(skip); @@ -2415,7 +2440,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } method.pop(); - load(rhs).convert(Type.OBJECT); + load(rhs, Type.OBJECT); method.label(skip); method.store(binaryNode.getSymbol()); @@ -2437,13 +2462,16 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex if (!lhsType.isEquivalentTo(rhsType)) { //this is OK if scoped, only locals are wrong - assert !(lhs instanceof IdentNode) || lhs.getSymbol().isScope() : new ASTWriter(binaryNode); } new Store<BinaryNode>(binaryNode, lhs) { @Override protected void evaluate() { - load(rhs); + if ((lhs instanceof IdentNode) && !lhs.getSymbol().isScope()) { + load(rhs, lhsType); + } else { + load(rhs); + } } }.store(); @@ -2482,8 +2510,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override protected void evaluate() { - load(assignNode.lhs(), true).convert(opType); - load(assignNode.rhs()).convert(opType); + loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), opType, true); op(); method.convert(assignNode.getType()); } @@ -2654,8 +2681,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex protected abstract void op(); protected void evaluate(final BinaryNode node) { - load(node.lhs()); - load(node.rhs()); + loadBinaryOperands(node); op(); method.store(node.getSymbol()); } @@ -2737,11 +2763,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final Type widest = Type.widest(lhsType, rhsType); assert widest.isNumeric() || widest.isBoolean() : widest; - load(lhs); - method.convert(widest); - load(rhs); - method.convert(widest); - + loadBinaryOperands(lhs, rhs, widest); final Label trueLabel = new Label("trueLabel"); final Label afterLabel = new Label("skip"); @@ -2860,6 +2882,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex public boolean enterSHR(final BinaryNode binaryNode) { new BinaryArith() { @Override + protected void evaluate(final BinaryNode node) { + loadBinaryOperands(node.lhs(), node.rhs(), Type.INT); + op(); + method.store(node.getSymbol()); + } + @Override protected void op() { method.shr(); method.convert(Type.LONG).load(JSType.MAX_UINT).and(); @@ -2891,26 +2919,22 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final Label falseLabel = new Label("ternary_false"); final Label exitLabel = new Label("ternary_exit"); - Type widest = Type.widest(trueExpr.getType(), falseExpr.getType()); + Type widest = Type.widest(ternaryNode.getType(), Type.widest(trueExpr.getType(), falseExpr.getType())); if (trueExpr.getType().isArray() || falseExpr.getType().isArray()) { //loadArray creates a Java array type on the stack, calls global allocate, which creates a native array type widest = Type.OBJECT; } - load(test); - assert test.getType().isBoolean() : "lhs in ternary must be boolean"; - + load(test, Type.BOOLEAN); // we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17 // will left as (Object)x=55 : (Object)17 by Lower. Then the first term can be {I}x=55 of type int, which breaks the // symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions - // to early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to + // too early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to // do this property. Then we never need any conversions in CodeGenerator method.ifeq(falseLabel); - load(trueExpr); - method.convert(widest); + load(trueExpr, widest); method._goto(exitLabel); method.label(falseLabel); - load(falseExpr); - method.convert(widest); + load(falseExpr, widest); method.label(exitLabel); method.store(symbol); @@ -3042,8 +3066,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final BaseNode baseNode = (BaseNode)target; final Expression base = baseNode.getBase(); - load(base); - method.convert(Type.OBJECT); + load(base, Type.OBJECT); depth += Type.OBJECT.getSlots(); if (isSelfModifying()) { @@ -3062,10 +3085,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex enterBaseNode(); final Expression index = node.getIndex(); - // could be boolean here as well - load(index); if (!index.getType().isNumeric()) { - method.convert(Type.OBJECT); + // could be boolean here as well + load(index, Type.OBJECT); + } else { + load(index); } depth += index.getType().getSlots(); @@ -3134,8 +3158,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex * need to do a conversion on non-equivalent types exists, but is * very rare. See for example test/script/basic/access-specializer.js */ - method.convert(target.getType()); - target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override protected boolean enterDefault(Node node) { @@ -3143,24 +3165,17 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex } @Override - public boolean enterUnaryNode(final UnaryNode node) { - if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) { - method.convert(node.rhs().getType()); - } - return true; - } - - @Override public boolean enterIdentNode(final IdentNode node) { final Symbol symbol = node.getSymbol(); assert symbol != null; if (symbol.isScope()) { if (isFastScope(symbol)) { - storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags()); + storeFastScopeVar(symbol, CALLSITE_SCOPE | getCallSiteFlags()); } else { - method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags()); + method.dynamicSet(node.getName(), CALLSITE_SCOPE | getCallSiteFlags()); } } else { + method.convert(node.getType()); method.store(symbol); } return false; @@ -3169,7 +3184,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex @Override public boolean enterAccessNode(final AccessNode node) { - method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags()); + method.dynamicSet(node.getProperty().getName(), getCallSiteFlags()); return false; } diff --git a/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/src/jdk/nashorn/internal/codegen/CompilationPhase.java index be8faab9..6c0a673d 100644 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java @@ -162,7 +162,7 @@ enum CompilationPhase { LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) { @Override FunctionNode transform(final Compiler compiler, final FunctionNode fn) { - return (FunctionNode)fn.accept(new Lower()); + return (FunctionNode)fn.accept(new Lower(compiler.getCodeInstaller())); } @Override diff --git a/src/jdk/nashorn/internal/codegen/CompileUnit.java b/src/jdk/nashorn/internal/codegen/CompileUnit.java index 0156716d..73127259 100644 --- a/src/jdk/nashorn/internal/codegen/CompileUnit.java +++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java @@ -28,7 +28,7 @@ package jdk.nashorn.internal.codegen; /** * Used to track split class compilation. */ -public class CompileUnit { +public class CompileUnit implements Comparable<CompileUnit> { /** Current class name */ private final String className; @@ -116,4 +116,9 @@ public class CompileUnit { public String toString() { return "[classname=" + className + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']'; } + + @Override + public int compareTo(CompileUnit o) { + return className.compareTo(o.className); + } } diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java index a31358f6..24173c3e 100644 --- a/src/jdk/nashorn/internal/codegen/Compiler.java +++ b/src/jdk/nashorn/internal/codegen/Compiler.java @@ -36,8 +36,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; -import jdk.nashorn.internal.ir.TemporarySymbols; - import java.io.File; import java.lang.reflect.Field; import java.security.AccessController; @@ -48,18 +46,20 @@ import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.TreeSet; import java.util.logging.Level; import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.TemporarySymbols; import jdk.nashorn.internal.ir.debug.ClassHistogramElement; import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator; import jdk.nashorn.internal.runtime.CodeInstaller; @@ -256,8 +256,8 @@ public final class Compiler { this.sequence = sequence; this.installer = installer; this.constantData = new ConstantData(); - this.compileUnits = new HashSet<>(); - this.bytecode = new HashMap<>(); + this.compileUnits = new TreeSet<>(); + this.bytecode = new LinkedHashMap<>(); } private void initCompiler(final FunctionNode functionNode) { diff --git a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java index 63c1fdb7..15341484 100644 --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java @@ -28,49 +28,22 @@ package jdk.nashorn.internal.codegen; import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.ir.AccessNode; -import jdk.nashorn.internal.ir.Assignment; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; -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.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; -import jdk.nashorn.internal.ir.IdentNode; -import jdk.nashorn.internal.ir.IfNode; -import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LexicalContext; -import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.ReturnNode; -import jdk.nashorn.internal.ir.RuntimeNode; -import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.TemporarySymbols; -import jdk.nashorn.internal.ir.TernaryNode; -import jdk.nashorn.internal.ir.ThrowNode; -import jdk.nashorn.internal.ir.TypeOverride; 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; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; -import jdk.nashorn.internal.runtime.Debug; import jdk.nashorn.internal.runtime.DebugLogger; -import jdk.nashorn.internal.runtime.JSType; /** * Lower to more primitive operations. After lowering, an AST has symbols and @@ -97,272 +70,32 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> { } @Override - public Node leaveCallNode(final CallNode callNode) { - // AccessSpecializer - call return type may change the access for this location - final Node function = callNode.getFunction(); - if (function instanceof FunctionNode) { - return setTypeOverride(callNode, ((FunctionNode)function).getReturnType()); - } - return callNode; - } - - private Node leaveUnary(final UnaryNode unaryNode) { - return unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType())); - } - - @Override - public Node leaveADD(final UnaryNode unaryNode) { - return leaveUnary(unaryNode); - } - - @Override - public Node leaveBIT_NOT(final UnaryNode unaryNode) { - return leaveUnary(unaryNode); - } - - @Override - public Node leaveCONVERT(final UnaryNode unaryNode) { - assert unaryNode.rhs().tokenType() != TokenType.CONVERT : "convert(convert encountered. check its origin and remove it"; - return unaryNode; - } - - @Override - public Node leaveDECINC(final UnaryNode unaryNode) { - return specialize(unaryNode).node; - } - - @Override - public Node leaveNEW(final UnaryNode unaryNode) { - assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject(); - return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew()); - } - - @Override - public Node leaveSUB(final UnaryNode unaryNode) { - return leaveUnary(unaryNode); - } - - /** - * Add is a special binary, as it works not only on arithmetic, but for - * strings etc as well. - */ - @Override - public Expression leaveADD(final BinaryNode binaryNode) { - final Expression lhs = binaryNode.lhs(); - final Expression rhs = binaryNode.rhs(); - - final Type type = binaryNode.getType(); - - if (type.isObject()) { - if (!isAddString(binaryNode)) { - return new RuntimeNode(binaryNode, Request.ADD); - } - } - - return binaryNode.setLHS(convert(lhs, type)).setRHS(convert(rhs, type)); - } - - @Override - public Node leaveAND(final BinaryNode binaryNode) { - return binaryNode; - } - - @Override - public Node leaveASSIGN(final BinaryNode binaryNode) { - final SpecializedNode specialized = specialize(binaryNode); - final BinaryNode specBinaryNode = (BinaryNode)specialized.node; - Type destType = specialized.type; - if (destType == null) { - destType = specBinaryNode.getType(); - } - // Register assignments to this object in case this is used as constructor - if (binaryNode.lhs() instanceof AccessNode) { - AccessNode accessNode = (AccessNode) binaryNode.lhs(); - - if (accessNode.getBase().getSymbol().isThis()) { - lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName()); - } + public Node leaveForNode(final ForNode forNode) { + if (forNode.isForIn()) { + return forNode; } - return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType)); - } - - @Override - public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - private boolean symbolIsInteger(final Expression node) { - final Symbol symbol = node.getSymbol(); - assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + lc.getCurrentFunction().getSource(); - return true; - } - @Override - public Node leaveBIT_AND(final BinaryNode binaryNode) { - assert symbolIsInteger(binaryNode); - return leaveBinary(binaryNode, Type.INT, Type.INT); - } + final Expression init = forNode.getInit(); + final Expression test = forNode.getTest(); + final Expression modify = forNode.getModify(); - @Override - public Node leaveBIT_OR(final BinaryNode binaryNode) { - assert symbolIsInteger(binaryNode); - return leaveBinary(binaryNode, Type.INT, Type.INT); - } + assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction(); - @Override - public Node leaveBIT_XOR(final BinaryNode binaryNode) { - assert symbolIsInteger(binaryNode); - return leaveBinary(binaryNode, Type.INT, Type.INT); + return forNode. + setInit(lc, init == null ? null : discard(init)). + setModify(lc, modify == null ? null : discard(modify)); } @Override public Node leaveCOMMALEFT(final BinaryNode binaryNode) { assert binaryNode.getSymbol() != null; - final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs())); - // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed - // in that case, update the node type as well - return propagateType(newBinaryNode, newBinaryNode.lhs().getType()); + return binaryNode.setRHS(discard(binaryNode.rhs())); } @Override public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { assert binaryNode.getSymbol() != null; - final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs())); - // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed - // in that case, update the node type as well - return propagateType(newBinaryNode, newBinaryNode.rhs().getType()); - } - - @Override - public Node leaveDIV(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); - } - - - @Override - public Node leaveEQ(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ); - } - - @Override - public Node leaveEQ_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ_STRICT); - } - - @Override - public Node leaveGE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GE); - } - - @Override - public Node leaveGT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GT); - } - - @Override - public Node leaveLE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LE); - } - - @Override - public Node leaveLT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LT); - } - - @Override - public Node leaveMOD(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); - } - - @Override - public Node leaveMUL(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); - } - - @Override - public Node leaveNE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE); - } - - @Override - public Node leaveNE_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE_STRICT); - } - - @Override - public Node leaveOR(final BinaryNode binaryNode) { - return binaryNode; - } - - @Override - public Node leaveSAR(final BinaryNode binaryNode) { - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveSHL(final BinaryNode binaryNode) { - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveSHR(final BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong() : "long coercion expected: " + binaryNode.getSymbol(); - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveSUB(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); + return binaryNode.setLHS(discard(binaryNode.lhs())); } @Override @@ -372,38 +105,12 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> { } @Override - public Node leaveCatchNode(final CatchNode catchNode) { - final Expression exceptionCondition = catchNode.getExceptionCondition(); - if (exceptionCondition != null) { - return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); - } - return catchNode; - } - - @Override public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { temporarySymbols.reuse(); return expressionStatement.setExpression(discard(expressionStatement.getExpression())); } @Override - public Node leaveForNode(final ForNode forNode) { - final Expression init = forNode.getInit(); - final Expression test = forNode.getTest(); - final Expression modify = forNode.getModify(); - - if (forNode.isForIn()) { - return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 - } - assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction(); - - return forNode. - setInit(lc, init == null ? null : discard(init)). - setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)). - setModify(lc, modify == null ? null : discard(modify)); - } - - @Override public boolean enterFunctionNode(final FunctionNode functionNode) { if (functionNode.isLazy()) { return false; @@ -430,113 +137,6 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> { return functionNode.setState(lc, CompilationState.FINALIZED); } - @Override - public Node leaveIfNode(final IfNode ifNode) { - return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN)); - } - - @SuppressWarnings("rawtypes") - @Override - public boolean enterLiteralNode(final LiteralNode literalNode) { - if (literalNode instanceof ArrayLiteralNode) { - final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; - final Expression[] array = arrayLiteralNode.getValue(); - final Type elementType = arrayLiteralNode.getElementType(); - - for (int i = 0; i < array.length; i++) { - final Node element = array[i]; - if (element != null) { - array[i] = convert((Expression)element.accept(this), elementType); - } - } - } - - return false; - } - - @Override - public Node leaveReturnNode(final ReturnNode returnNode) { - final Expression expr = returnNode.getExpression(); - if (expr != null) { - return returnNode.setExpression(convert(expr, lc.getCurrentFunction().getReturnType())); - } - return returnNode; - } - - @Override - public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { - final List<Expression> args = runtimeNode.getArgs(); - for (final Expression arg : args) { - assert !arg.getType().isUnknown(); - } - return runtimeNode; - } - - @Override - public Node leaveSwitchNode(final SwitchNode switchNode) { - final boolean allInteger = switchNode.getTag().getSymbolType().isInteger(); - - if (allInteger) { - return switchNode; - } - - final Expression expression = switchNode.getExpression(); - final List<CaseNode> cases = switchNode.getCases(); - final List<CaseNode> newCases = new ArrayList<>(); - - for (final CaseNode caseNode : cases) { - final Expression test = caseNode.getTest(); - newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode); - } - - return switchNode. - setExpression(lc, convert(expression, Type.OBJECT)). - setCases(lc, newCases); - } - - @Override - public Node leaveTernaryNode(final TernaryNode ternaryNode) { - return ternaryNode.setTest(convert(ternaryNode.getTest(), Type.BOOLEAN)); - } - - @Override - public Node leaveThrowNode(final ThrowNode throwNode) { - return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT)); - } - - @Override - public Node leaveVarNode(final VarNode varNode) { - final Expression init = varNode.getInit(); - if (init != null) { - final SpecializedNode specialized = specialize(varNode); - final VarNode specVarNode = (VarNode)specialized.node; - Type destType = specialized.type; - if (destType == null) { - destType = specVarNode.getName().getType(); - } - assert specVarNode.getName().hasType() : specVarNode + " doesn't have a type"; - final Expression convertedInit = convert(init, destType); - temporarySymbols.reuse(); - return specVarNode.setInit(convertedInit); - } - temporarySymbols.reuse(); - return varNode; - } - - @Override - public Node leaveWhileNode(final WhileNode whileNode) { - final Expression test = whileNode.getTest(); - if (test != null) { - return whileNode.setTest(lc, convert(test, Type.BOOLEAN)); - } - return whileNode; - } - - @Override - public Node leaveWithNode(final WithNode withNode) { - return withNode.setExpression(lc, convert(withNode.getExpression(), Type.OBJECT)); - } - private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { if (LOG.isEnabled()) { if (!symbol.isScope()) { @@ -583,260 +183,6 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> { } } - /** - * Exit a comparison node and do the appropriate replacements. We need to introduce runtime - * nodes late for comparisons as types aren't known until the last minute - * - * Both compares and adds may turn into runtimes node at this level as when we first bump - * into the op in Attr, we may type it according to what we know there, which may be wrong later - * - * e.g. i (int) < 5 -> normal compare - * i = object - * then the post pass that would add the conversion to the 5 needs to - * - * @param binaryNode binary node to leave - * @param request runtime request - * @return lowered cmp node - */ - @SuppressWarnings("fallthrough") - private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { - final Expression lhs = binaryNode.lhs(); - final Expression rhs = binaryNode.rhs(); - - Type widest = Type.widest(lhs.getType(), rhs.getType()); - - boolean newRuntimeNode = false, finalized = false; - switch (request) { - case EQ_STRICT: - case NE_STRICT: - if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { - newRuntimeNode = true; - widest = Type.OBJECT; - finalized = true; - } - //fallthru - default: - if (newRuntimeNode || widest.isObject()) { - return new RuntimeNode(binaryNode, request).setIsFinal(finalized); - } - break; - } - - return binaryNode.setLHS(convert(lhs, widest)).setRHS(convert(rhs, widest)); - } - - /** - * Compute the binary arithmetic type given the lhs and an rhs of a binary expression - * @param lhsType the lhs type - * @param rhsType the rhs type - * @return the correct binary type - */ - private static Type binaryArithType(final Type lhsType, final Type rhsType) { - if (!Compiler.shouldUseIntegerArithmetic()) { - return Type.NUMBER; - } - return Type.widest(lhsType, rhsType, Type.NUMBER); - } - - private Node leaveBinaryArith(final BinaryNode binaryNode) { - final Type type = binaryArithType(binaryNode.lhs().getType(), binaryNode.rhs().getType()); - return leaveBinary(binaryNode, type, type); - } - - private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) { - Node b = binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType)); - return b; - } - - /** - * A symbol (and {@link jdk.nashorn.internal.runtime.Property}) can be tagged as "may be primitive". - * This is used a hint for dual fields that it is even worth it to try representing this - * field as something other than java.lang.Object. - * - * @param node node in which to tag symbols as primitive - * @param to which primitive type to use for tagging - */ - private static void setCanBePrimitive(final Node node, final Type to) { - final HashSet<Node> exclude = new HashSet<>(); - - node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - private void setCanBePrimitive(final Symbol symbol) { - LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol)); - symbol.setCanBePrimitive(to); - } - - @Override - public boolean enterIdentNode(final IdentNode identNode) { - if (!exclude.contains(identNode)) { - setCanBePrimitive(identNode.getSymbol()); - } - return false; - } - - @Override - public boolean enterAccessNode(final AccessNode accessNode) { - setCanBePrimitive(accessNode.getProperty().getSymbol()); - return false; - } - - @Override - public boolean enterIndexNode(final IndexNode indexNode) { - exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine - return true; - } - }); - } - - private static class SpecializedNode { - final Node node; - final Type type; - - SpecializedNode(Node node, Type type) { - this.node = node; - this.type = type; - } - } - - <T extends Expression> SpecializedNode specialize(final Assignment<T> assignment) { - final Node node = ((Node)assignment); - final T lhs = assignment.getAssignmentDest(); - final Expression rhs = assignment.getAssignmentSource(); - - if (!canHaveCallSiteType(lhs)) { - return new SpecializedNode(node, null); - } - - final Type to; - if (node.isSelfModifying()) { - to = node.getWidestOperationType(); - } else { - to = rhs.getType(); - } - - if (!isSupportedCallSiteType(to)) { - //meaningless to specialize to boolean or object - return new SpecializedNode(node, null); - } - - final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to)); - final Node typePropagatedNode; - if(newNode instanceof Expression) { - typePropagatedNode = propagateType((Expression)newNode, to); - } else if(newNode instanceof VarNode) { - // VarNode, being a statement, doesn't have its own symbol; it uses the symbol of its name instead. - final VarNode varNode = (VarNode)newNode; - typePropagatedNode = varNode.setName((IdentNode)propagateType(varNode.getName(), to)); - } else { - throw new AssertionError(); - } - return new SpecializedNode(typePropagatedNode, to); - } - - - /** - * Is this a node that can have its type overridden. This is true for - * AccessNodes, IndexNodes and IdentNodes - * - * @param node the node to check - * @return true if node can have a callsite type - */ - private static boolean canHaveCallSiteType(final Node node) { - return node instanceof TypeOverride && ((TypeOverride<?>)node).canHaveCallSiteType(); - } - - /** - * Is the specialization type supported. Currently we treat booleans as objects - * and have no special boolean type accessor, thus booleans are ignored. - * TODO - support booleans? NASHORN-590 - * - * @param castTo the type to check - * @return true if call site type is supported - */ - private static boolean isSupportedCallSiteType(final Type castTo) { - return castTo.isNumeric(); // don't specializable for boolean - } - - /** - * Override the type of a node for e.g. access specialization of scope - * objects. Normally a variable can only get a wider type and narrower type - * sets are ignored. Not that a variable can still be on object type as - * per the type analysis, but a specific access may be narrower, e.g. if it - * is used in an arithmetic op. This overrides a type, regardless of - * type environment and is used primarily by the access specializer - * - * @param node node for which to change type - * @param to new type - */ - @SuppressWarnings("unchecked") - <T extends Expression> T setTypeOverride(final T node, final Type to) { - final Type from = node.getType(); - if (!node.getType().equals(to)) { - LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to); - if (!to.isObject() && from.isObject()) { - setCanBePrimitive(node, to); - } - } - LOG.info("Type override for lhs in '", node, "' => ", to); - return ((TypeOverride<T>)node).setType(temporarySymbols, lc, to); - } - - /** - * Add an explicit conversion. This is needed when attribution has created types - * that do not mesh into an op type, e.g. a = b, where b is object and a is double - * at the end of Attr, needs explicit conversion logic. - * - * An explicit conversion can be one of the following: - * + Convert a literal - just replace it with another literal - * + Convert a scope object - just replace the type of the access, e.g. get()D->get()I - * + Explicit convert placement, e.g. a = (double)b - all other cases - * - * No other part of the world after {@link Attr} may introduce new symbols. This - * is the only place. - * - * @param node node to convert - * @param to destination type - * @return conversion node - */ - private Expression convert(final Expression node, final Type to) { - assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass(); - assert node != null : "node is null"; - assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + lc.getCurrentFunction(); - assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + lc.getCurrentFunction(); - - final Type from = node.getType(); - - if (Type.areEquivalent(from, to)) { - return node; - } - - if (from.isObject() && to.isObject()) { - return node; - } - - Expression resultNode = node; - - if (node instanceof LiteralNode && !(node instanceof ArrayLiteralNode) && !to.isObject()) { - final LiteralNode<?> newNode = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, to).eval(); - if (newNode != null) { - resultNode = newNode; - } - } else { - if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) { - assert node instanceof TypeOverride; - return setTypeOverride(node, to); - } - resultNode = new UnaryNode(Token.recast(node.getToken(), TokenType.CONVERT), node); - } - - LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'"); - - assert !node.isTerminal(); - - //This is the only place in this file that can create new temporaries - //FinalizeTypes may not introduce ANY node that is not a conversion. - return temporarySymbols.ensureSymbol(lc, to, resultNode); - } - private static Expression discard(final Expression node) { if (node.getSymbol() != null) { final UnaryNode discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node); @@ -849,82 +195,5 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> { return node; } - /** - * Whenever an expression like an addition or an assignment changes type, it - * may be that case that {@link Attr} created a symbol for an intermediate - * result of the expression, say for an addition. This also has to be updated - * if the expression type changes. - * - * Assignments use their lhs as node symbol, and in this case we can't modify - * it. Then {@link CodeGenerator.Store} needs to do an explicit conversion. - * This is happens very rarely. - * - * @param node - * @param to - */ - private Expression propagateType(final Expression node, final Type to) { - Symbol symbol = node.getSymbol(); - if (symbol.isTemp() && symbol.getSymbolType() != to) { - symbol = symbol.setTypeOverrideShared(to, temporarySymbols); - LOG.info("Type override for temporary in '", node, "' => ", to); - } - return node.setSymbol(lc, symbol); - } - - /** - * Determine if the outcome of + operator is a string. - * - * @param node Node to test. - * @return true if a string result. - */ - private boolean isAddString(final Node node) { - if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) { - final BinaryNode binaryNode = (BinaryNode)node; - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); - - return isAddString(lhs) || isAddString(rhs); - } - - return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString(); - } - /** - * Whenever an explicit conversion is needed and the convertee is a literal, we can - * just change the literal - */ - class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> { - private final Type type; - - LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) { - super(parent); - this.type = type; - } - - @Override - protected LiteralNode<?> eval() { - final Object value = ((LiteralNode<?>)parent).getValue(); - - LiteralNode<?> literalNode = null; - - if (type.isString()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toString(value)); - } else if (type.isBoolean()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toBoolean(value)); - } else if (type.isInteger()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toInt32(value)); - } else if (type.isLong()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toLong(value)); - } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toNumber(value)); - } - - if (literalNode != null) { - //inherit literal symbol for attr. - literalNode = (LiteralNode<?>)literalNode.setSymbol(lc, parent.getSymbol()); - } - - return literalNode; - } - } } diff --git a/src/jdk/nashorn/internal/codegen/FoldConstants.java b/src/jdk/nashorn/internal/codegen/FoldConstants.java index 49dfbb32..59362fa2 100644 --- a/src/jdk/nashorn/internal/codegen/FoldConstants.java +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java @@ -25,6 +25,8 @@ package jdk.nashorn.internal.codegen; +import java.util.ArrayList; +import java.util.List; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; @@ -37,8 +39,10 @@ import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.UnaryNode; +import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.JSType; @@ -88,12 +92,22 @@ final class FoldConstants extends NodeVisitor<LexicalContext> { @Override public Node leaveIfNode(final IfNode ifNode) { final Node test = ifNode.getTest(); - if (test instanceof LiteralNode) { - final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); - if (shortCut != null) { - return new BlockStatement(ifNode.getLineNumber(), shortCut); + if (test instanceof LiteralNode.PrimitiveLiteralNode) { + final boolean isTrue = ((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue(); + final Block executed = isTrue ? ifNode.getPass() : ifNode.getFail(); + final Block dropped = isTrue ? ifNode.getFail() : ifNode.getPass(); + final List<Statement> statements = new ArrayList<>(); + + if (executed != null) { + statements.addAll(executed.getStatements()); // Get statements form executed branch } - return new EmptyNode(ifNode); + if (dropped != null) { + extractVarNodes(dropped, statements); // Get var-nodes from non-executed branch + } + if (statements.isEmpty()) { + return new EmptyNode(ifNode); + } + return BlockStatement.createReplacement(ifNode, ifNode.getFinish(), statements); } return ifNode; } @@ -101,8 +115,8 @@ final class FoldConstants extends NodeVisitor<LexicalContext> { @Override public Node leaveTernaryNode(final TernaryNode ternaryNode) { final Node test = ternaryNode.getTest(); - if (test instanceof LiteralNode) { - return ((LiteralNode<?>)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression(); + if (test instanceof LiteralNode.PrimitiveLiteralNode) { + return ((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression(); } return ternaryNode; } @@ -131,6 +145,17 @@ final class FoldConstants extends NodeVisitor<LexicalContext> { protected abstract LiteralNode<?> eval(); } + private static void extractVarNodes(final Block block, final List<Statement> statements) { + final LexicalContext lc = new LexicalContext(); + block.accept(lc, new NodeVisitor<LexicalContext>(lc) { + @Override + public boolean enterVarNode(VarNode varNode) { + statements.add(varNode.setInit(null)); + return false; + } + }); + } + private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> { UnaryNodeConstantEvaluator(final UnaryNode parent) { super(parent); diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java index 032d27bc..bf6872fe 100644 --- a/src/jdk/nashorn/internal/codegen/Lower.java +++ b/src/jdk/nashorn/internal/codegen/Lower.java @@ -68,6 +68,7 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; +import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.Source; @@ -86,10 +87,13 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { private static final DebugLogger LOG = new DebugLogger("lower"); + // needed only to get unique eval id + private final CodeInstaller<?> installer; + /** * Constructor. */ - Lower() { + Lower(final CodeInstaller<?> installer) { super(new BlockLexicalContext() { @Override @@ -132,6 +136,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { return block.setIsTerminal(this, false); } }); + this.installer = installer; } @Override @@ -529,11 +534,17 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { */ private String evalLocation(final IdentNode node) { final Source source = lc.getCurrentFunction().getSource(); + final int pos = node.position(); + // Code installer is null when running with --compile-only, use 0 as id in that case + final long id = installer == null ? 0 : installer.getUniqueEvalId(); return new StringBuilder(). append(source.getName()). append('#'). - append(source.getLine(node.position())). - append("<eval>"). + append(source.getLine(pos)). + append(':'). + append(source.getColumn(pos)). + append("<eval>@"). + append(id). toString(); } diff --git a/src/jdk/nashorn/internal/codegen/MapCreator.java b/src/jdk/nashorn/internal/codegen/MapCreator.java index 2921ea9e..8012adf5 100644 --- a/src/jdk/nashorn/internal/codegen/MapCreator.java +++ b/src/jdk/nashorn/internal/codegen/MapCreator.java @@ -134,6 +134,10 @@ public class MapCreator { flags |= Property.CAN_BE_UNDEFINED; } + if (symbol.isFunctionDeclaration()) { + flags |= Property.IS_FUNCTION_DECLARATION; + } + return flags; } diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java index 8a012ccb..9b6d12aa 100644 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java @@ -69,7 +69,6 @@ import java.io.PrintStream; import java.lang.reflect.Array; import java.util.EnumSet; import java.util.List; - import jdk.internal.dynalink.support.NameCodec; import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.MethodVisitor; @@ -1560,7 +1559,7 @@ public class MethodEmitter implements Emitter { MethodEmitter convert(final Type to) { final Type type = peekType().convert(method, to); if (type != null) { - if (peekType() != to) { + if (!peekType().isEquivalentTo(to)) { debug("convert", peekType(), "->", to); } popType(); @@ -1790,15 +1789,14 @@ public class MethodEmitter implements Emitter { * @param name name of property * @param flags call site flags */ - void dynamicSet(final Type valueType, final String name, final int flags) { + void dynamicSet(final String name, final int flags) { debug("dynamic_set", name, peekType()); - Type type = valueType; + Type type = peekType(); if (type.isObject() || type.isBoolean()) { //promote strings to objects etc type = Type.OBJECT; convert(Type.OBJECT); //TODO bad- until we specialize boolean setters, } - popType(type); popType(Type.SCOPE); diff --git a/src/jdk/nashorn/internal/codegen/Namespace.java b/src/jdk/nashorn/internal/codegen/Namespace.java index 5de2fdf8..72f77a5b 100644 --- a/src/jdk/nashorn/internal/codegen/Namespace.java +++ b/src/jdk/nashorn/internal/codegen/Namespace.java @@ -81,7 +81,7 @@ public class Namespace { final int count = counter + 1; namespaceDirectory.put(base, count); - return base + "$" + count; + return base + '-' + count; } } diff --git a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java index d30d5645..1339d873 100644 --- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java +++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java @@ -32,7 +32,6 @@ import static jdk.nashorn.internal.codegen.types.Type.OBJECT; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; - import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.LiteralNode; @@ -143,7 +142,7 @@ public class SpillObjectCreator extends ObjectCreator { method.dup(); method.getField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor()); method.load(property.getSlot()); - codegen.load(values.get(i)).convert(OBJECT); + codegen.load(values.get(i), OBJECT); method.arraystore(); } } diff --git a/src/jdk/nashorn/internal/codegen/WeighNodes.java b/src/jdk/nashorn/internal/codegen/WeighNodes.java index 9ecfccd6..f5940488 100644 --- a/src/jdk/nashorn/internal/codegen/WeighNodes.java +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java @@ -297,11 +297,6 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> { } @Override - public Node leaveCONVERT(final UnaryNode unaryNode) { - return unaryNodeWeight(unaryNode); - } - - @Override public Node leaveDECINC(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } diff --git a/src/jdk/nashorn/internal/codegen/types/BooleanType.java b/src/jdk/nashorn/internal/codegen/types/BooleanType.java index 4331cb55..840b2910 100644 --- a/src/jdk/nashorn/internal/codegen/types/BooleanType.java +++ b/src/jdk/nashorn/internal/codegen/types/BooleanType.java @@ -136,8 +136,7 @@ public final class BooleanType extends Type { invokeStatic(method, JSType.TO_LONG); } else if (to.isString()) { invokeStatic(method, VALUE_OF); - invokeStatic(method, JSType.TO_PRIMITIVE); - invokeStatic(method, JSType.TO_STRING); + invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING); } else if (to.isObject()) { invokeStatic(method, VALUE_OF); } else { diff --git a/src/jdk/nashorn/internal/codegen/types/ObjectType.java b/src/jdk/nashorn/internal/codegen/types/ObjectType.java index cb2f876b..1f7a0221 100644 --- a/src/jdk/nashorn/internal/codegen/types/ObjectType.java +++ b/src/jdk/nashorn/internal/codegen/types/ObjectType.java @@ -153,8 +153,7 @@ class ObjectType extends Type { } else if (to.isBoolean()) { invokeStatic(method, JSType.TO_BOOLEAN); } else if (to.isString()) { - invokeStatic(method, JSType.TO_PRIMITIVE); - invokeStatic(method, JSType.TO_STRING); + invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING); } else { assert false : "Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString; } diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java index c1e8bfbb..e7c78953 100644 --- a/src/jdk/nashorn/internal/codegen/types/Type.java +++ b/src/jdk/nashorn/internal/codegen/types/Type.java @@ -292,6 +292,16 @@ public abstract class Type implements Comparable<Type>, BytecodeOps { } /** + * Determines whether this type represents an primitive type according to the ECMAScript specification, + * which includes Boolean, Number, and String. + * + * @return true if a JavaScript primitive type, false otherwise. + */ + public boolean isJSPrimitive() { + return !isObject() || isString(); + } + + /** * Determines whether a type is the BOOLEAN type * @return true if BOOLEAN, false otherwise */ @@ -441,7 +451,12 @@ public abstract class Type implements Comparable<Type>, BytecodeOps { if (type0.isArray() && type1.isArray()) { return ((ArrayType)type0).getElementType() == ((ArrayType)type1).getElementType() ? type0 : Type.OBJECT; } else if (type0.isArray() != type1.isArray()) { - return Type.OBJECT; //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense + //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense + return Type.OBJECT; + } else if (type0.isObject() && type1.isObject() && type0.getTypeClass() != type1.getTypeClass()) { + // Object<type=String> and Object<type=ScriptFunction> will produce Object + // TODO: maybe find most specific common superclass? + return Type.OBJECT; } return type0.weight() > type1.weight() ? type0 : type1; } diff --git a/src/jdk/nashorn/internal/ir/AccessNode.java b/src/jdk/nashorn/internal/ir/AccessNode.java index 55b0aa3f..957adf69 100644 --- a/src/jdk/nashorn/internal/ir/AccessNode.java +++ b/src/jdk/nashorn/internal/ir/AccessNode.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -46,12 +45,12 @@ public final class AccessNode extends BaseNode { * @param property property */ public AccessNode(final long token, final int finish, final Expression base, final IdentNode property) { - super(token, finish, base, false, false); + super(token, finish, base, false); this.property = property.setIsPropertyName(); } - private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) { - super(accessNode, base, isFunction, hasCallSiteType); + private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction) { + super(accessNode, base, isFunction); this.property = property; } @@ -73,13 +72,6 @@ public final class AccessNode extends BaseNode { public void toString(final StringBuilder sb) { final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true); - if (hasCallSiteType()) { - sb.append('{'); - final String desc = getType().getDescriptor(); - sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor()); - sb.append('}'); - } - if (needsParen) { sb.append('('); } @@ -107,21 +99,14 @@ public final class AccessNode extends BaseNode { if (this.base == base) { return this; } - return new AccessNode(this, base, property, isFunction(), hasCallSiteType()); + return new AccessNode(this, base, property, isFunction()); } private AccessNode setProperty(final IdentNode property) { if (this.property == property) { return this; } - return new AccessNode(this, base, property, isFunction(), hasCallSiteType()); - } - - @Override - public AccessNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - logTypeChange(type); - final AccessNode newAccessNode = (AccessNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts)); - return new AccessNode(newAccessNode, base, property.setType(ts, lc, type), isFunction(), hasCallSiteType()); + return new AccessNode(this, base, property, isFunction()); } @Override @@ -129,7 +114,7 @@ public final class AccessNode extends BaseNode { if (isFunction()) { return this; } - return new AccessNode(this, base, property, true, hasCallSiteType()); + return new AccessNode(this, base, property, true); } } diff --git a/src/jdk/nashorn/internal/ir/BaseNode.java b/src/jdk/nashorn/internal/ir/BaseNode.java index f945ef35..96f155d1 100644 --- a/src/jdk/nashorn/internal/ir/BaseNode.java +++ b/src/jdk/nashorn/internal/ir/BaseNode.java @@ -25,10 +25,6 @@ package jdk.nashorn.internal.ir; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; - -import jdk.nashorn.internal.codegen.ObjectClassGenerator; -import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; /** @@ -38,15 +34,13 @@ import jdk.nashorn.internal.ir.annotations.Immutable; * @see IndexNode */ @Immutable -public abstract class BaseNode extends Expression implements FunctionCall, TypeOverride<BaseNode> { +public abstract class BaseNode extends Expression implements FunctionCall { /** Base Node. */ protected final Expression base; private final boolean isFunction; - private final boolean hasCallSiteType; - /** * Constructor * @@ -54,13 +48,11 @@ public abstract class BaseNode extends Expression implements FunctionCall, TypeO * @param finish finish * @param base base node * @param isFunction is this a function - * @param hasCallSiteType does this access have a callsite type */ - public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction, final boolean hasCallSiteType) { + public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction) { super(token, base.getStart(), finish); this.base = base; this.isFunction = isFunction; - this.hasCallSiteType = hasCallSiteType; } /** @@ -68,13 +60,11 @@ public abstract class BaseNode extends Expression implements FunctionCall, TypeO * @param baseNode node to inherit from * @param base base * @param isFunction is this a function - * @param hasCallSiteType does this access have a callsite type */ - protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final boolean hasCallSiteType) { + protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction) { super(baseNode); this.base = base; this.isFunction = isFunction; - this.hasCallSiteType = hasCallSiteType; } /** @@ -96,26 +86,4 @@ public abstract class BaseNode extends Expression implements FunctionCall, TypeO */ public abstract BaseNode setIsFunction(); - @Override - public boolean canHaveCallSiteType() { - return true; //carried by the symbol and always the same nodetype==symboltype - } - - /** - * Does the access have a call site type override? - * @return true if overridden - */ - protected boolean hasCallSiteType() { - return hasCallSiteType; - } - - /** - * Debug type change - * @param type new type - */ - protected final void logTypeChange(final Type type) { - if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { - ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType()); - } - } } diff --git a/src/jdk/nashorn/internal/ir/BinaryNode.java b/src/jdk/nashorn/internal/ir/BinaryNode.java index 169772d7..0a1d1bfc 100644 --- a/src/jdk/nashorn/internal/ir/BinaryNode.java +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java @@ -90,6 +90,9 @@ public final class BinaryNode extends Expression implements Assignment<Expressio return Type.LONG; case ASSIGN_SAR: case ASSIGN_SHL: + case BIT_AND: + case BIT_OR: + case BIT_XOR: case ASSIGN_BIT_AND: case ASSIGN_BIT_OR: case ASSIGN_BIT_XOR: @@ -170,6 +173,42 @@ public final class BinaryNode extends Expression implements Assignment<Expressio } @Override + public boolean isLocal() { + switch (tokenType()) { + case SAR: + case SHL: + case SHR: + case BIT_AND: + case BIT_OR: + case BIT_XOR: + case ADD: + case DIV: + case MOD: + case MUL: + case SUB: + return lhs.isLocal() && lhs.getType().isJSPrimitive() + && rhs.isLocal() && rhs.getType().isJSPrimitive(); + case ASSIGN_ADD: + case ASSIGN_BIT_AND: + case ASSIGN_BIT_OR: + case ASSIGN_BIT_XOR: + case ASSIGN_DIV: + case ASSIGN_MOD: + case ASSIGN_MUL: + case ASSIGN_SAR: + case ASSIGN_SHL: + case ASSIGN_SHR: + case ASSIGN_SUB: + return lhs instanceof IdentNode && lhs.isLocal() && lhs.getType().isJSPrimitive() + && rhs.isLocal() && rhs.getType().isJSPrimitive(); + case ASSIGN: + return lhs instanceof IdentNode && lhs.isLocal() && rhs.isLocal(); + default: + return false; + } + } + + @Override public void toString(final StringBuilder sb) { final TokenType type = tokenType(); diff --git a/src/jdk/nashorn/internal/ir/CallNode.java b/src/jdk/nashorn/internal/ir/CallNode.java index ffaae078..0d38f241 100644 --- a/src/jdk/nashorn/internal/ir/CallNode.java +++ b/src/jdk/nashorn/internal/ir/CallNode.java @@ -36,9 +36,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; * IR representation for a function call. */ @Immutable -public final class CallNode extends LexicalContextExpression implements TypeOverride<CallNode> { - - private final Type type; +public final class CallNode extends LexicalContextExpression { /** Function identifier or function body. */ private final Expression function; @@ -150,18 +148,16 @@ public final class CallNode extends LexicalContextExpression implements TypeOver this.function = function; this.args = args; this.flags = 0; - this.type = null; this.evalArgs = null; this.lineNumber = lineNumber; } - private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type type, final EvalArgs evalArgs) { + private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final EvalArgs evalArgs) { super(callNode); this.lineNumber = callNode.lineNumber; this.function = function; this.args = args; this.flags = flags; - this.type = type; this.evalArgs = evalArgs; } @@ -175,29 +171,9 @@ public final class CallNode extends LexicalContextExpression implements TypeOver @Override public Type getType() { - if (hasCallSiteType()) { - return type; - } return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT; } - @Override - public CallNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - if (this.type == type) { - return this; - } - return new CallNode(this, function, args, flags, type, evalArgs); - } - - private boolean hasCallSiteType() { - return this.type != null; - } - - @Override - public boolean canHaveCallSiteType() { - return true; - } - /** * Assist in IR navigation. * @@ -212,7 +188,6 @@ public final class CallNode extends LexicalContextExpression implements TypeOver setFunction((Expression)function.accept(visitor)). setArgs(Node.accept(visitor, Expression.class, args)). setFlags(flags). - setType(null, lc, type). setEvalArgs(evalArgs == null ? null : evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)). @@ -229,13 +204,6 @@ public final class CallNode extends LexicalContextExpression implements TypeOver @Override public void toString(final StringBuilder sb) { - if (hasCallSiteType()) { - sb.append('{'); - final String desc = getType().getDescriptor(); - sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor()); - sb.append('}'); - } - function.toString(sb); sb.append('('); @@ -271,7 +239,7 @@ public final class CallNode extends LexicalContextExpression implements TypeOver if (this.args == args) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } /** @@ -293,7 +261,7 @@ public final class CallNode extends LexicalContextExpression implements TypeOver if (this.evalArgs == evalArgs) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } /** @@ -321,7 +289,7 @@ public final class CallNode extends LexicalContextExpression implements TypeOver if (this.function == function) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } /** @@ -344,6 +312,6 @@ public final class CallNode extends LexicalContextExpression implements TypeOver if (this.flags == flags) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } } diff --git a/src/jdk/nashorn/internal/ir/Expression.java b/src/jdk/nashorn/internal/ir/Expression.java index 9006fa8d..f8074fe4 100644 --- a/src/jdk/nashorn/internal/ir/Expression.java +++ b/src/jdk/nashorn/internal/ir/Expression.java @@ -96,4 +96,16 @@ public abstract class Expression extends Node { assert hasType() : this + " has no type"; return symbol.getSymbolType(); } + + /** + * Returns {@code true} if this expression depends exclusively on state that is constant + * or local to the currently running function and thus inaccessible to other functions. + * This implies that a local expression must not call any other functions (neither directly + * nor implicitly through a getter, setter, or object-to-primitive type conversion). + * + * @return true if this expression does not depend on state shared with other functions. + */ + public boolean isLocal() { + return false; + } } diff --git a/src/jdk/nashorn/internal/ir/IdentNode.java b/src/jdk/nashorn/internal/ir/IdentNode.java index 4b139b25..30e8ed11 100644 --- a/src/jdk/nashorn/internal/ir/IdentNode.java +++ b/src/jdk/nashorn/internal/ir/IdentNode.java @@ -28,9 +28,7 @@ package jdk.nashorn.internal.ir; import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__; import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__; import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; -import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -39,10 +37,11 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; * IR representation for an identifier. */ @Immutable -public final class IdentNode extends Expression implements PropertyKey, TypeOverride<IdentNode>, FunctionCall { - private static final int PROPERTY_NAME = 1 << 0; - private static final int INITIALIZED_HERE = 1 << 1; - private static final int FUNCTION = 1 << 2; +public final class IdentNode extends Expression implements PropertyKey, FunctionCall { + private static final int PROPERTY_NAME = 1 << 0; + private static final int INITIALIZED_HERE = 1 << 1; + private static final int FUNCTION = 1 << 2; + private static final int FUTURESTRICT_NAME = 1 << 3; /** Identifier. */ private final String name; @@ -100,19 +99,6 @@ public final class IdentNode extends Expression implements PropertyKey, TypeOver return callSiteType != null; } - @Override - public IdentNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't - if (this.callSiteType == type) { - return this; - } - if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { - ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType()); - } - - return new IdentNode(this, name, type, flags); - } - /** * Assist in IR navigation. * @@ -152,29 +138,9 @@ public final class IdentNode extends Expression implements PropertyKey, TypeOver return getName(); } - /** - * We can only override type if the symbol lives in the scope, as otherwise - * it is strongly determined by the local variable already allocated. - * - * <p>We also return true if the symbol represents the return value of a function with a - * non-generic return type as in this case we need to propagate the type instead of - * converting to object, for example if the symbol is used as the left hand side of an - * assignment such as in the code below.</p> - * - * <pre> - * try { - * return 2; - * } finally { - * return 3; - * } - * } - * </pre> - * - * @return true if can have callsite type - */ @Override - public boolean canHaveCallSiteType() { - return getSymbol() != null && (getSymbol().isScope() || getSymbol().isNonGenericReturn()); + public boolean isLocal() { + return !getSymbol().isScope(); } /** @@ -197,6 +163,25 @@ public final class IdentNode extends Expression implements PropertyKey, TypeOver } /** + * Check if this IdentNode is a future strict name + * @return true if this is a future strict name + */ + public boolean isFutureStrictName() { + return (flags & FUTURESTRICT_NAME) != 0; + } + + /** + * Flag this IdentNode as a future strict name + * @return a node equivalent to this one except for the requested change. + */ + public IdentNode setIsFutureStrictName() { + if (isFutureStrictName()) { + return this; + } + return new IdentNode(this, name, callSiteType, flags | FUTURESTRICT_NAME); + } + + /** * Helper function for local def analysis. * @return true if IdentNode is initialized on creation */ diff --git a/src/jdk/nashorn/internal/ir/IndexNode.java b/src/jdk/nashorn/internal/ir/IndexNode.java index 2799e9be..83f58ffc 100644 --- a/src/jdk/nashorn/internal/ir/IndexNode.java +++ b/src/jdk/nashorn/internal/ir/IndexNode.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -46,12 +45,12 @@ public final class IndexNode extends BaseNode { * @param index index for access */ public IndexNode(final long token, final int finish, final Expression base, final Expression index) { - super(token, finish, base, false, false); + super(token, finish, base, false); this.index = index; } - private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final boolean hasCallSiteType) { - super(indexNode, base, isFunction, hasCallSiteType); + private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction) { + super(indexNode, base, isFunction); this.index = index; } @@ -69,13 +68,6 @@ public final class IndexNode extends BaseNode { public void toString(final StringBuilder sb) { final boolean needsParen = tokenType().needsParens(base.tokenType(), true); - if (hasCallSiteType()) { - sb.append('{'); - final String desc = getType().getDescriptor(); - sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor()); - sb.append('}'); - } - if (needsParen) { sb.append('('); } @@ -103,7 +95,7 @@ public final class IndexNode extends BaseNode { if (this.base == base) { return this; } - return new IndexNode(this, base, index, isFunction(), hasCallSiteType()); + return new IndexNode(this, base, index, isFunction()); } /** @@ -115,7 +107,7 @@ public final class IndexNode extends BaseNode { if(this.index == index) { return this; } - return new IndexNode(this, base, index, isFunction(), hasCallSiteType()); + return new IndexNode(this, base, index, isFunction()); } @Override @@ -123,14 +115,7 @@ public final class IndexNode extends BaseNode { if (isFunction()) { return this; } - return new IndexNode(this, base, index, true, hasCallSiteType()); - } - - @Override - public IndexNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - logTypeChange(type); - final IndexNode newIndexNode = (IndexNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts)); - return new IndexNode(newIndexNode, base, index, isFunction(), true); + return new IndexNode(this, base, index, true); } } diff --git a/src/jdk/nashorn/internal/ir/LexicalContext.java b/src/jdk/nashorn/internal/ir/LexicalContext.java index 666be7e3..1b380d38 100644 --- a/src/jdk/nashorn/internal/ir/LexicalContext.java +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java @@ -587,11 +587,11 @@ public class LexicalContext { final FunctionNode fn = (FunctionNode)node; final Source source = fn.getSource(); String src = source.toString(); - if (src.indexOf(File.pathSeparator) != -1) { + if (src.contains(File.pathSeparator)) { src = src.substring(src.lastIndexOf(File.pathSeparator)); } src += ' '; - src += source.getLine(fn.getStart()); + src += fn.getLineNumber(); sb.append(src); } sb.append(' '); diff --git a/src/jdk/nashorn/internal/ir/LiteralNode.java b/src/jdk/nashorn/internal/ir/LiteralNode.java index b2d9e9fa..8d6823cb 100644 --- a/src/jdk/nashorn/internal/ir/LiteralNode.java +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java @@ -28,10 +28,13 @@ package jdk.nashorn.internal.ir; import java.util.Arrays; import java.util.Collections; import java.util.List; + import jdk.nashorn.internal.codegen.CompileUnit; +import jdk.nashorn.internal.codegen.types.ArrayType; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.objects.NativeArray; import jdk.nashorn.internal.parser.Lexer.LexerToken; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; @@ -96,14 +99,6 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { return value == null; } - /** - * Check if the literal value is boolean true - * @return true if literal value is boolean true - */ - public boolean isTrue() { - return JSType.toBoolean(value); - } - @Override public Type getType() { return Type.typeFor(value.getClass()); @@ -259,8 +254,36 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { return new NullLiteralNode(parent.getToken(), parent.getFinish()); } + /** + * Super class for primitive (side-effect free) literals. + * + * @param <T> the literal type + */ + public static class PrimitiveLiteralNode<T> extends LiteralNode<T> { + private PrimitiveLiteralNode(final long token, final int finish, final T value) { + super(token, finish, value); + } + + private PrimitiveLiteralNode(final PrimitiveLiteralNode<T> literalNode) { + super(literalNode); + } + + /** + * Check if the literal value is boolean true + * @return true if literal value is boolean true + */ + public boolean isTrue() { + return JSType.toBoolean(value); + } + + @Override + public boolean isLocal() { + return true; + } + } + @Immutable - private static final class BooleanLiteralNode extends LiteralNode<Boolean> { + private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> { private BooleanLiteralNode(final long token, final int finish, final boolean value) { super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value); @@ -312,7 +335,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { } @Immutable - private static final class NumberLiteralNode extends LiteralNode<Number> { + private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> { private final Type type = numberGetType(value); @@ -374,7 +397,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { return new NumberLiteralNode(parent.getToken(), parent.getFinish(), value); } - private static class UndefinedLiteralNode extends LiteralNode<Undefined> { + private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> { private UndefinedLiteralNode(final long token, final int finish) { super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED); } @@ -410,7 +433,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { } @Immutable - private static class StringLiteralNode extends LiteralNode<String> { + private static class StringLiteralNode extends PrimitiveLiteralNode<String> { private StringLiteralNode(final long token, final int finish, final String value) { super(Token.recast(token, TokenType.STRING), finish, value); } @@ -511,18 +534,12 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { return object; } else if (object instanceof LiteralNode) { return objectAsConstant(((LiteralNode<?>)object).getValue()); - } else if (object instanceof UnaryNode) { - final UnaryNode unaryNode = (UnaryNode)object; - - if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) { - return objectAsConstant(unaryNode.rhs()); - } } return POSTSET_MARKER; } - private static final class NullLiteralNode extends LiteralNode<Object> { + private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> { private NullLiteralNode(final long token, final int finish) { super(Token.recast(token, TokenType.OBJECT), finish, null); @@ -767,8 +784,11 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { return value; } - @Override - public Type getType() { + /** + * Get the array element type as Java format, e.g. [I + * @return array element type + */ + public ArrayType getArrayType() { if (elementType.isInteger()) { return Type.INT_ARRAY; } else if (elementType.isLong()) { @@ -780,6 +800,11 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey { } } + @Override + public Type getType() { + return Type.typeFor(NativeArray.class); + } + /** * Get the element type of this array literal * @return element type diff --git a/src/jdk/nashorn/internal/ir/RuntimeNode.java b/src/jdk/nashorn/internal/ir/RuntimeNode.java index d100b408..269cb905 100644 --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java @@ -38,7 +38,7 @@ import jdk.nashorn.internal.parser.TokenType; * IR representation for a runtime call. */ @Immutable -public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> { +public class RuntimeNode extends Expression { /** * Request enum used for meta-information about the runtime request @@ -159,6 +159,36 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> } /** + * Derive a runtime node request type for a node + * @param node the node + * @return request type + */ + public static Request requestFor(final Node node) { + assert node.isComparison(); + switch (node.tokenType()) { + case EQ_STRICT: + return Request.EQ_STRICT; + case NE_STRICT: + return Request.NE_STRICT; + case EQ: + return Request.EQ; + case NE: + return Request.NE; + case LT: + return Request.LT; + case LE: + return Request.LE; + case GT: + return Request.GT; + case GE: + return Request.GE; + default: + assert false; + return null; + } + } + + /** * Is this an EQ or EQ_STRICT? * * @param request a request @@ -268,9 +298,6 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> /** Call arguments. */ private final List<Expression> args; - /** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */ - private final Type callSiteType; - /** is final - i.e. may not be removed again, lower in the code pipeline */ private final boolean isFinal; @@ -287,16 +314,14 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> this.request = request; this.args = args; - this.callSiteType = null; this.isFinal = false; } - private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final Type callSiteType, final boolean isFinal, final List<Expression> args) { + private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List<Expression> args) { super(runtimeNode); this.request = request; this.args = args; - this.callSiteType = callSiteType; this.isFinal = isFinal; } @@ -335,7 +360,6 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> this.request = request; this.args = args; - this.callSiteType = null; this.isFinal = false; } @@ -376,7 +400,7 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> if (this.isFinal == isFinal) { return this; } - return new RuntimeNode(this, request, callSiteType, isFinal, args); + return new RuntimeNode(this, request, isFinal, args); } /** @@ -384,24 +408,7 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> */ @Override public Type getType() { - return hasCallSiteType() ? callSiteType : request.getReturnType(); - } - - @Override - public RuntimeNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - if (this.callSiteType == type) { - return this; - } - return new RuntimeNode(this, request, type, isFinal, args); - } - - @Override - public boolean canHaveCallSiteType() { - return request == Request.ADD; - } - - private boolean hasCallSiteType() { - return callSiteType != null; + return request.getReturnType(); } @Override @@ -450,7 +457,7 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> if (this.args == args) { return this; } - return new RuntimeNode(this, request, callSiteType, isFinal, args); + return new RuntimeNode(this, request, isFinal, args); } /** diff --git a/src/jdk/nashorn/internal/ir/Symbol.java b/src/jdk/nashorn/internal/ir/Symbol.java index 69e98ac6..2906893f 100644 --- a/src/jdk/nashorn/internal/ir/Symbol.java +++ b/src/jdk/nashorn/internal/ir/Symbol.java @@ -75,6 +75,8 @@ public final class Symbol implements Comparable<Symbol> { public static final int IS_SPECIALIZED_PARAM = 1 << 13; /** Is this symbol a shared temporary? */ public static final int IS_SHARED = 1 << 14; + /** Is this a function declaration? */ + public static final int IS_FUNCTION_DECLARATION = 1 << 15; /** Null or name identifying symbol. */ private final String name; @@ -360,6 +362,14 @@ public final class Symbol implements Comparable<Symbol> { } /** + * Check if this symbol is a function declaration + * @return true if a function declaration + */ + public boolean isFunctionDeclaration() { + return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION; + } + + /** * Creates an unshared copy of a symbol. The symbol must be currently shared. * @param newName the name for the new symbol. * @return a new, unshared symbol. @@ -396,6 +406,16 @@ public final class Symbol implements Comparable<Symbol> { /** + * Mark this symbol as a function declaration. + */ + public void setIsFunctionDeclaration() { + if (!isFunctionDeclaration()) { + trace("SET IS FUNCTION DECLARATION"); + flags |= IS_FUNCTION_DECLARATION; + } + } + + /** * Check if this symbol is a variable * @return true if variable */ diff --git a/src/jdk/nashorn/internal/ir/TernaryNode.java b/src/jdk/nashorn/internal/ir/TernaryNode.java index 70e1c726..26c14b76 100644 --- a/src/jdk/nashorn/internal/ir/TernaryNode.java +++ b/src/jdk/nashorn/internal/ir/TernaryNode.java @@ -109,6 +109,13 @@ public final class TernaryNode extends Expression { } } + @Override + public boolean isLocal() { + return getTest().isLocal() + && getTrueExpression().isLocal() + && getFalseExpression().isLocal(); + } + /** * Get the test expression for this ternary expression, i.e. "x" in x ? y : z * @return the test expression diff --git a/src/jdk/nashorn/internal/ir/TypeOverride.java b/src/jdk/nashorn/internal/ir/TypeOverride.java deleted file mode 100644 index 929d0128..00000000 --- a/src/jdk/nashorn/internal/ir/TypeOverride.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.ir; - -import jdk.nashorn.internal.codegen.types.Type; - -/** - * A type override makes it possible to change the return type of a node, if we know - * that the linker can provide it directly. For example, an identity node that is - * in the scope, can very well look like an object to the compiler of the method it - * is in, but if someone does (int)x, it make senses to ask for it directly - * with an int getter instead of loading it as an object and explicitly converting it - * by using JSType.toInt32. Especially in scenarios where the field is already stored - * as a primitive, this will be much faster than the "object is all I see" scope - * available in the method - * @param <T> the type of the node implementing the interface - */ - -public interface TypeOverride<T extends Node> { - /** - * Set the override type - * - * @param ts temporary symbols - * @param lc the current lexical context - * @param type the type - * @return a node equivalent to this one except for the requested change. - */ - public T setType(final TemporarySymbols ts, final LexicalContext lc, final Type type); - - /** - * Returns true if this node can have a callsite override, e.g. all scope ident nodes - * which lead to dynamic getters can have it, local variable nodes (slots) can't. - * Call nodes can have it unconditionally and so on - * - * @return true if it is possible to assign a type override to this node - */ - public boolean canHaveCallSiteType(); - -} diff --git a/src/jdk/nashorn/internal/ir/UnaryNode.java b/src/jdk/nashorn/internal/ir/UnaryNode.java index 2de2ca5d..4923d31e 100644 --- a/src/jdk/nashorn/internal/ir/UnaryNode.java +++ b/src/jdk/nashorn/internal/ir/UnaryNode.java @@ -26,7 +26,6 @@ package jdk.nashorn.internal.ir; import static jdk.nashorn.internal.parser.TokenType.BIT_NOT; -import static jdk.nashorn.internal.parser.TokenType.CONVERT; import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; @@ -130,6 +129,26 @@ public final class UnaryNode extends Expression implements Assignment<Expression } @Override + public boolean isLocal() { + switch (tokenType()) { + case NEW: + return false; + case ADD: + case SUB: + case NOT: + case BIT_NOT: + return rhs.isLocal() && rhs.getType().isJSPrimitive(); + case DECPOSTFIX: + case DECPREFIX: + case INCPOSTFIX: + case INCPREFIX: + return rhs instanceof IdentNode && rhs.isLocal() && rhs.getType().isJSPrimitive(); + default: + return rhs.isLocal(); + } + } + + @Override public void toString(final StringBuilder sb) { toString(sb, new Runnable() { @Override @@ -150,19 +169,10 @@ public final class UnaryNode extends Expression implements Assignment<Expression final TokenType type = tokenType(); final String name = type.getName(); final boolean isPostfix = type == DECPOSTFIX || type == INCPOSTFIX; - final boolean isConvert = type == CONVERT && getSymbol() != null; boolean rhsParen = type.needsParens(rhs().tokenType(), false); - int convertPos = 0; - if (isConvert) { - convertPos = sb.length(); - sb.append("("); - sb.append(getType()); - sb.append(")("); - } - - if (!isPostfix && !isConvert) { + if (!isPostfix) { if (name == null) { sb.append(type.name()); rhsParen = true; @@ -186,16 +196,6 @@ public final class UnaryNode extends Expression implements Assignment<Expression if (isPostfix) { sb.append(type == DECPOSTFIX ? "--" : "++"); } - - if (isConvert) { - // strip extra cast parenthesis which makes the printout harder to read - final boolean endsWithParenthesis = sb.charAt(sb.length() - 1) == ')'; - if (!endsWithParenthesis) { - sb.append(')'); - } else { - sb.setCharAt(convertPos, ' '); - } - } } /** diff --git a/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java b/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java index 2d4412e7..89269d5f 100644 --- a/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java +++ b/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java @@ -25,10 +25,10 @@ package jdk.nashorn.internal.ir.debug; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryPoolMXBean; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.ArrayList; @@ -51,9 +51,9 @@ import java.util.Map; * switch, it can not detect * this fact and will report incorrect sizes, as it will presume the default JVM * behavior. - * - * @author Attila Szegedi */ + +@SuppressWarnings("StaticNonFinalUsedInInitialization") public class ObjectSizeCalculator { /** @@ -368,6 +368,29 @@ public class ObjectSizeCalculator { type.getName()); } + // ALERT: java.lang.management is not available in compact 1. We need + // to use reflection to soft link test memory statistics. + + static Class<?> managementFactory = null; + static Class<?> memoryPoolMXBean = null; + static Class<?> memoryUsage = null; + static Method getMemoryPoolMXBeans = null; + static Method getUsage = null; + static Method getMax = null; + static { + try { + managementFactory = Class.forName("java.lang.management.ManagementFactory"); + memoryPoolMXBean = Class.forName("java.lang.management.MemoryPoolMXBean"); + memoryUsage = Class.forName("java.lang.management.MemoryUsage"); + + getMemoryPoolMXBeans = managementFactory.getMethod("getMemoryPoolMXBeans"); + getUsage = memoryPoolMXBean.getMethod("getUsage"); + getMax = memoryUsage.getMethod("getMax"); + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException ex) { + // Pass thru, asserts when attempting to use. + } + } + /** * Return the current memory usage * @return current memory usage derived from system configuration @@ -409,9 +432,33 @@ public class ObjectSizeCalculator { strVmVersion.indexOf('.'))); if (vmVersion >= 17) { long maxMemory = 0; - for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) { - maxMemory += mp.getUsage().getMax(); + + /* + See ALERT above. The reflection code below duplicates the following + sequence, and avoids hard coding of java.lang.management. + + for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) { + maxMemory += mp.getUsage().getMax(); + } + */ + + if (getMemoryPoolMXBeans == null) { + throw new AssertionError("java.lang.management not available in compact 1"); } + + try { + final List<?> memoryPoolMXBeans = (List<?>)getMemoryPoolMXBeans.invoke(managementFactory); + for (final Object mp : memoryPoolMXBeans) { + final Object usage = getUsage.invoke(mp); + final Object max = getMax.invoke(usage); + maxMemory += ((Long)max).longValue(); + } + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new AssertionError("java.lang.management not available in compact 1"); + } + if (maxMemory < 30L * 1024 * 1024 * 1024) { // HotSpot 17.0 and above use compressed OOPs below 30GB of RAM total // for all memory pools (yes, including code cache). diff --git a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java index 71600395..2b1de7c7 100644 --- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java +++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java @@ -51,8 +51,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T return enterADD(unaryNode); case BIT_NOT: return enterBIT_NOT(unaryNode); - case CONVERT: - return enterCONVERT(unaryNode); case DELETE: return enterDELETE(unaryNode); case DISCARD: @@ -84,8 +82,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T return leaveADD(unaryNode); case BIT_NOT: return leaveBIT_NOT(unaryNode); - case CONVERT: - return leaveCONVERT(unaryNode); case DELETE: return leaveDELETE(unaryNode); case DISCARD: @@ -323,26 +319,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T } /** - * Unary enter - callback for entering a conversion - * - * @param unaryNode the node - * @return true if traversal should continue and node children be traversed, false otherwise - */ - public boolean enterCONVERT(final UnaryNode unaryNode) { - return enterDefault(unaryNode); - } - - /** - * Unary leave - callback for leaving a conversion - * - * @param unaryNode the node - * @return processed node, which will replace the original one, or the original node - */ - public Node leaveCONVERT(final UnaryNode unaryNode) { - return leaveDefault(unaryNode); - } - - /** * Unary enter - callback for entering a ++ or -- operator * * @param unaryNode the node diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java index 161909bb..0a09370a 100644 --- a/src/jdk/nashorn/internal/objects/Global.java +++ b/src/jdk/nashorn/internal/objects/Global.java @@ -53,19 +53,19 @@ import jdk.nashorn.internal.runtime.GlobalFunctions; import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.NativeJavaPackage; -import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.PropertyDescriptor; -import jdk.nashorn.internal.runtime.arrays.ArrayData; -import jdk.nashorn.internal.runtime.regexp.RegExpResult; +import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.Scope; +import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.ScriptingFunctions; import jdk.nashorn.internal.runtime.Source; +import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; +import jdk.nashorn.internal.runtime.regexp.RegExpResult; import jdk.nashorn.internal.scripts.JO; /** @@ -491,8 +491,8 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { // GlobalObject interface implementation @Override - public boolean isOfContext(final Context context) { - return this.context == context; + public boolean isOfContext(final Context ctxt) { + return this.context == ctxt; } @Override @@ -1665,9 +1665,9 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { final ScriptObject stringPrototype = getStringPrototype(); stringPrototype.addOwnProperty("length", Attribute.NON_ENUMERABLE_CONSTANT, 0.0); - // add Array.prototype.length + // set isArray flag on Array.prototype final ScriptObject arrayPrototype = getArrayPrototype(); - arrayPrototype.addOwnProperty("length", Attribute.NOT_ENUMERABLE|Attribute.NOT_CONFIGURABLE, 0.0); + arrayPrototype.setIsArray(); this.DEFAULT_DATE = new NativeDate(Double.NaN, this); diff --git a/src/jdk/nashorn/internal/objects/NativeArray.java b/src/jdk/nashorn/internal/objects/NativeArray.java index 8cb1ded9..67b9f1f1 100644 --- a/src/jdk/nashorn/internal/objects/NativeArray.java +++ b/src/jdk/nashorn/internal/objects/NativeArray.java @@ -372,9 +372,7 @@ public final class NativeArray extends ScriptObject { */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object isArray(final Object self, final Object arg) { - return isArray(arg) || (arg == Global.instance().getArrayPrototype()) - || (arg instanceof NativeRegExpExecResult) - || (arg instanceof JSObject && ((JSObject)arg).isArray()); + return isArray(arg) || (arg instanceof JSObject && ((JSObject)arg).isArray()); } /** @@ -403,6 +401,26 @@ public final class NativeArray extends ScriptObject { } } + /** + * Prototype length getter + * @param self self reference + * @return the length of the object + */ + @Getter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) + public static Object getProtoLength(final Object self) { + return length(self); // Same as instance getter but we can't make nasgen use the same method for prototype + } + + /** + * Prototype length setter + * @param self self reference + * @param length new length property + */ + @Setter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) + public static void setProtoLength(final Object self, final Object length) { + length(self, length); // Same as instance setter but we can't make nasgen use the same method for prototype + } + static long validLength(final Object length, final boolean reject) { final double doubleLength = JSType.toNumber(length); if (!Double.isNaN(doubleLength) && JSType.isRepresentableAsLong(doubleLength)) { @@ -819,8 +837,15 @@ public final class NativeArray extends ScriptObject { if (bulkable(sobj)) { sobj.getArray().shiftLeft(1); } else { + boolean hasPrevious = true; for (long k = 1; k < len; k++) { - sobj.set(k - 1, sobj.get(k), true); + boolean hasCurrent = sobj.has(k); + if (hasCurrent) { + sobj.set(k - 1, sobj.get(k), true); + } else if (hasPrevious) { + sobj.delete(k - 1, true); + } + hasPrevious = hasCurrent; } } sobj.delete(--len, true); @@ -844,6 +869,10 @@ public final class NativeArray extends ScriptObject { @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object slice(final Object self, final Object start, final Object end) { final Object obj = Global.toObject(self); + if (!(obj instanceof ScriptObject)) { + return ScriptRuntime.UNDEFINED; + } + final ScriptObject sobj = (ScriptObject)obj; final long len = JSType.toUint32(sobj.getLength()); final long relativeStart = JSType.toLong(start); @@ -860,9 +889,12 @@ public final class NativeArray extends ScriptObject { return new NativeArray(sobj.getArray().slice(k, finale)); } - final NativeArray copy = new NativeArray(0); + // Construct array with proper length to have a deleted filter on undefined elements + final NativeArray copy = new NativeArray(finale - k); for (long n = 0; k < finale; n++, k++) { - copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k)); + if (sobj.has(k)) { + copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k)); + } } return copy; @@ -993,19 +1025,42 @@ public final class NativeArray extends ScriptObject { final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart); - final NativeArray array = new NativeArray(actualDeleteCount); + NativeArray returnValue; + + if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) { + try { + returnValue = new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length)); + + // Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements + int k = (int) actualStart; + for (int i = 0; i < items.length; i++, k++) { + sobj.defineOwnProperty(k, items[i]); + } + } catch (UnsupportedOperationException uoe) { + returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len); + } + } else { + returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len); + } + + return returnValue; + } + + private static NativeArray slowSplice(final ScriptObject sobj, final long start, final long deleteCount, final Object[] items, final long len) { + + final NativeArray array = new NativeArray(deleteCount); - for (long k = 0; k < actualDeleteCount; k++) { - final long from = actualStart + k; + for (long k = 0; k < deleteCount; k++) { + final long from = start + k; if (sobj.has(from)) { array.defineOwnProperty(ArrayIndex.getArrayIndex(k), sobj.get(from)); } } - if (items.length < actualDeleteCount) { - for (long k = actualStart; k < (len - actualDeleteCount); k++) { - final long from = k + actualDeleteCount; + if (items.length < deleteCount) { + for (long k = start; k < (len - deleteCount); k++) { + final long from = k + deleteCount; final long to = k + items.length; if (sobj.has(from)) { @@ -1015,12 +1070,12 @@ public final class NativeArray extends ScriptObject { } } - for (long k = len; k > (len - actualDeleteCount + items.length); k--) { + for (long k = len; k > (len - deleteCount + items.length); k--) { sobj.delete(k - 1, true); } - } else if (items.length > actualDeleteCount) { - for (long k = len - actualDeleteCount; k > actualStart; k--) { - final long from = k + actualDeleteCount - 1; + } else if (items.length > deleteCount) { + for (long k = len - deleteCount; k > start; k--) { + final long from = k + deleteCount - 1; final long to = k + items.length - 1; if (sobj.has(from)) { @@ -1032,12 +1087,12 @@ public final class NativeArray extends ScriptObject { } } - long k = actualStart; + long k = start; for (int i = 0; i < items.length; i++, k++) { sobj.set(k, items[i], true); } - final long newLength = len - actualDeleteCount + items.length; + final long newLength = len - deleteCount + items.length; sobj.set("length", newLength, true); return array; @@ -1108,12 +1163,16 @@ public final class NativeArray extends ScriptObject { try { final ScriptObject sobj = (ScriptObject)Global.toObject(self); final long len = JSType.toUint32(sobj.getLength()); - final long n = JSType.toLong(fromIndex); + if (len == 0) { + return -1; + } - if (len == 0 || n >= len) { + final long n = JSType.toLong(fromIndex); + if (n >= len) { return -1; } + for (long k = Math.max(0, (n < 0) ? (len - Math.abs(n)) : n); k < len; k++) { if (sobj.has(k)) { if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) { diff --git a/src/jdk/nashorn/internal/objects/NativeError.java b/src/jdk/nashorn/internal/objects/NativeError.java index dc6aef90..d3e2effe 100644 --- a/src/jdk/nashorn/internal/objects/NativeError.java +++ b/src/jdk/nashorn/internal/objects/NativeError.java @@ -30,6 +30,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; + import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; @@ -37,10 +38,12 @@ import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; import jdk.nashorn.internal.objects.annotations.Where; +import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptRuntime; /** @@ -133,12 +136,16 @@ public final class NativeError extends ScriptObject { * @param errorObj the error object * @return undefined */ + @SuppressWarnings("unused") @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object captureStackTrace(final Object self, final Object errorObj) { Global.checkObject(errorObj); final ScriptObject sobj = (ScriptObject)errorObj; - final ECMAException exp = new ECMAException(sobj, null); - sobj.set("stack", getScriptStackString(sobj, exp), false); + new ECMAException(sobj, null); //constructor has side effects + sobj.delete("stack", false); + final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", GET_STACK); + final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", SET_STACK); + sobj.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); return UNDEFINED; } diff --git a/src/jdk/nashorn/internal/objects/NativeFunction.java b/src/jdk/nashorn/internal/objects/NativeFunction.java index c77002e3..d092cfb6 100644 --- a/src/jdk/nashorn/internal/objects/NativeFunction.java +++ b/src/jdk/nashorn/internal/objects/NativeFunction.java @@ -221,6 +221,7 @@ public final class NativeFunction { final StringBuilder sb = new StringBuilder(); sb.append("(function ("); + final String funcBody; if (args.length > 0) { final StringBuilder paramListBuf = new StringBuilder(); for (int i = 0; i < args.length - 1; i++) { @@ -230,15 +231,20 @@ public final class NativeFunction { } } + // now convert function body to a string + funcBody = JSType.toString(args[args.length - 1]); + final String paramList = paramListBuf.toString(); if (! paramList.isEmpty()) { checkFunctionParameters(paramList); sb.append(paramList); } + } else { + funcBody = null; } + sb.append(") {\n"); if (args.length > 0) { - final String funcBody = JSType.toString(args[args.length - 1]); checkFunctionBody(funcBody); sb.append(funcBody); sb.append('\n'); diff --git a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java index 5b89e2ca..668ca8cd 100644 --- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java +++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java @@ -629,7 +629,8 @@ public final class NativeJSAdapter extends ScriptObject { // to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice. return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class, func.makeBoundFunction(this, new Object[] { name })), 0, Object.class), - adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), __call__), testJSAdaptor(adaptee, null, null, null)); + adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), __call__), + testJSAdaptor(adaptee, null, null, null)); } } throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this)); diff --git a/src/jdk/nashorn/internal/objects/NativeObject.java b/src/jdk/nashorn/internal/objects/NativeObject.java index 94e3bef2..c7db39a5 100644 --- a/src/jdk/nashorn/internal/objects/NativeObject.java +++ b/src/jdk/nashorn/internal/objects/NativeObject.java @@ -60,6 +60,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; +import jdk.nashorn.internal.runtime.linker.NashornBeansLinker; /** * ECMA 15.2 Object objects @@ -729,8 +730,7 @@ public final class NativeObject { final MethodType methodType, final Object source) { final GuardedInvocation inv; try { - inv = linker.getGuardedInvocation(createLinkRequest(operation, methodType, source), - Bootstrap.getLinkerServices()); + inv = NashornBeansLinker.getGuardedInvocation(linker, createLinkRequest(operation, methodType, source), Bootstrap.getLinkerServices()); assert passesGuard(source, inv.getGuard()); } catch(RuntimeException|Error e) { throw e; diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java index 1694b3da..9fd59191 100644 --- a/src/jdk/nashorn/internal/objects/NativeString.java +++ b/src/jdk/nashorn/internal/objects/NativeString.java @@ -505,7 +505,7 @@ public final class NativeString extends ScriptObject { */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object charAt(final Object self, final Object pos) { - return charAt(self, JSType.toInteger(pos)); + return charAtImpl(checkObjectToString(self), JSType.toInteger(pos)); } /** @@ -527,7 +527,10 @@ public final class NativeString extends ScriptObject { */ @SpecializedFunction public static String charAt(final Object self, final int pos) { - final String str = checkObjectToString(self); + return charAtImpl(checkObjectToString(self), pos); + } + + private static String charAtImpl(final String str, final int pos) { return (pos < 0 || pos >= str.length()) ? "" : String.valueOf(str.charAt(pos)); } @@ -539,7 +542,7 @@ public final class NativeString extends ScriptObject { */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object charCodeAt(final Object self, final Object pos) { - return charCodeAt(self, JSType.toInteger(pos)); + return charCodeAtImpl(checkObjectToString(self), JSType.toInteger(pos)); } /** @@ -561,7 +564,10 @@ public final class NativeString extends ScriptObject { */ @SpecializedFunction public static double charCodeAt(final Object self, final int pos) { - final String str = checkObjectToString(self); + return charCodeAtImpl(checkObjectToString(self), pos); + } + + private static double charCodeAtImpl(final String str, final int pos) { return (pos < 0 || pos >= str.length()) ? Double.NaN : str.charAt(pos); } diff --git a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java index 88421a70..638d18a2 100644 --- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java +++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java @@ -25,6 +25,7 @@ package jdk.nashorn.internal.objects; +import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import java.lang.invoke.MethodHandle; @@ -255,6 +256,12 @@ public class ScriptFunctionImpl extends ScriptFunction { return makeFunction(name, methodHandle, null); } + @Override + public ScriptFunction makeSynchronizedFunction(final Object sync) { + final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync); + return makeFunction(getName(), mh); + } + /** * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we * can expose it to methods in this package. diff --git a/src/jdk/nashorn/internal/parser/AbstractParser.java b/src/jdk/nashorn/internal/parser/AbstractParser.java index 80f7ce07..587ae869 100644 --- a/src/jdk/nashorn/internal/parser/AbstractParser.java +++ b/src/jdk/nashorn/internal/parser/AbstractParser.java @@ -378,7 +378,7 @@ public abstract class AbstractParser { next(); // Create IDENT node. - return new IdentNode(identToken, finish, ident); + return new IdentNode(identToken, finish, ident).setIsFutureStrictName(); } // Get IDENT. diff --git a/src/jdk/nashorn/internal/parser/Lexer.java b/src/jdk/nashorn/internal/parser/Lexer.java index 09269c6c..a01705dd 100644 --- a/src/jdk/nashorn/internal/parser/Lexer.java +++ b/src/jdk/nashorn/internal/parser/Lexer.java @@ -47,7 +47,6 @@ import static jdk.nashorn.internal.parser.TokenType.XML; import jdk.nashorn.internal.runtime.ECMAErrors; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSErrorType; -import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ParserException; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.options.Options; @@ -1054,16 +1053,6 @@ public class Lexer extends Scanner { } /** - * Convert string to number. - * - * @param valueString String to convert. - * @return Converted number. - */ - private static Number valueOf(final String valueString) throws NumberFormatException { - return JSType.narrowestIntegerRepresentation(Double.valueOf(valueString)); - } - - /** * Scan a number. */ protected void scanNumber() { @@ -1623,7 +1612,7 @@ public class Lexer extends Scanner { case HEXADECIMAL: return Lexer.valueOf(source.getString(start + 2, len - 2), 16); // number case FLOATING: - return Lexer.valueOf(source.getString(start, len)); // number + return Double.valueOf(source.getString(start, len)); // number case STRING: return source.getString(start, len); // String case ESCSTRING: diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java index 9663401f..869746cf 100644 --- a/src/jdk/nashorn/internal/parser/Parser.java +++ b/src/jdk/nashorn/internal/parser/Parser.java @@ -909,6 +909,10 @@ loop: default: break; } + + if (ident.isFutureStrictName()) { + throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken()); + } } } @@ -2103,13 +2107,13 @@ loop: final String ident = (String)expectValue(IDENT); if (type != COLON) { - final long getSetToken = token; + final long getSetToken = propertyToken; switch (ident) { case "get": final PropertyKey getIdent = propertyName(); final String getterName = getIdent.getPropertyName(); - final IdentNode getNameNode = new IdentNode(((Node)getIdent).getToken(), finish, "get " + NameCodec.encode(getterName)); + final IdentNode getNameNode = new IdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName)); expect(LPAREN); expect(RPAREN); functionNode = functionBody(getSetToken, getNameNode, new ArrayList<IdentNode>(), FunctionNode.Kind.GETTER); @@ -2118,7 +2122,7 @@ loop: case "set": final PropertyKey setIdent = propertyName(); final String setterName = setIdent.getPropertyName(); - final IdentNode setNameNode = new IdentNode(((Node)setIdent).getToken(), finish, "set " + NameCodec.encode(setterName)); + final IdentNode setNameNode = new IdentNode(((Node)setIdent).getToken(), finish, NameCodec.encode("set " + setterName)); expect(LPAREN); final IdentNode argIdent = getIdent(); verifyStrictIdent(argIdent, "setter argument"); @@ -2436,7 +2440,7 @@ loop: // name is null, generate anonymous name boolean isAnonymous = false; if (name == null) { - final String tmpName = "_L" + source.getLine(Token.descPosition(token)); + final String tmpName = "_L" + functionLine; name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName); isAnonymous = true; } diff --git a/src/jdk/nashorn/internal/parser/TokenType.java b/src/jdk/nashorn/internal/parser/TokenType.java index a976215b..6287a234 100644 --- a/src/jdk/nashorn/internal/parser/TokenType.java +++ b/src/jdk/nashorn/internal/parser/TokenType.java @@ -178,7 +178,6 @@ public enum TokenType { ARRAY (LITERAL, null), COMMALEFT (IR, null), - CONVERT (IR, null), DISCARD (IR, null), DECPOSTFIX (IR, null), INCPOSTFIX (IR, null); diff --git a/src/jdk/nashorn/internal/runtime/CodeInstaller.java b/src/jdk/nashorn/internal/runtime/CodeInstaller.java index be9976e7..28c9630e 100644 --- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java +++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java @@ -68,4 +68,10 @@ public interface CodeInstaller<T> { * @return unique script id */ public long getUniqueScriptId(); + + /** + * Get next unique eval id + * @return unique eval id + */ + public long getUniqueEvalId(); } diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunction.java b/src/jdk/nashorn/internal/runtime/CompiledFunction.java index afa3657c..18b20a3d 100644 --- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java @@ -48,6 +48,7 @@ final class CompiledFunction implements Comparable<CompiledFunction> { } CompiledFunction(final MethodType type, final MethodHandle invoker, final MethodHandle constructor) { + assert type != null; this.type = type; this.invoker = invoker; this.constructor = constructor; @@ -80,7 +81,37 @@ final class CompiledFunction implements Comparable<CompiledFunction> { @Override public int compareTo(final CompiledFunction o) { - return weight() - o.weight(); + return compareMethodTypes(type(), o.type()); + } + + private static int compareMethodTypes(final MethodType ownType, final MethodType otherType) { + // Comparable interface demands that compareTo() should only return 0 if objects are equal. + // Failing to meet this requirement causes same weight functions to replace each other in TreeSet, + // so we go some lengths to come up with an ordering between same weight functions, + // first falling back to parameter count and then to hash code. + if (ownType.equals(otherType)) { + return 0; + } + + final int diff = weight(ownType) - weight(otherType); + if (diff != 0) { + return diff; + } + if (ownType.parameterCount() != otherType.parameterCount()) { + return ownType.parameterCount() - otherType.parameterCount(); + } + // We're just interested in not returning 0 here, not correct ordering + return ownType.hashCode() - otherType.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CompiledFunction && type().equals(((CompiledFunction)obj).type()); + } + + @Override + public int hashCode() { + return type().hashCode(); } private int weight() { @@ -119,14 +150,14 @@ final class CompiledFunction implements Comparable<CompiledFunction> { * a semantically equivalent linkage can be performed. * * @param mt type to check against - * @return + * @return true if types are compatible */ boolean typeCompatible(final MethodType mt) { - final Class<?>[] wantedParams = mt.parameterArray(); - final Class<?>[] existingParams = type().parameterArray(); + final int wantedParamCount = mt.parameterCount(); + final int existingParamCount = type.parameterCount(); //if we are not examining a varargs type, the number of parameters must be the same - if (wantedParams.length != existingParams.length && !isVarArgsType(mt)) { + if (wantedParamCount != existingParamCount && !isVarArgsType(mt)) { return false; } @@ -134,10 +165,10 @@ final class CompiledFunction implements Comparable<CompiledFunction> { //parameters lengths do not match is if our type ends with a varargs argument. //then every trailing parameter in the given callsite can be folded into it, making //us compatible (albeit slower than a direct specialization) - final int lastParamIndex = Math.min(wantedParams.length, existingParams.length); + final int lastParamIndex = Math.min(wantedParamCount, existingParamCount); for (int i = 0; i < lastParamIndex; i++) { - final Type w = Type.typeFor(wantedParams[i]); - final Type e = Type.typeFor(existingParams[i]); + final Type w = Type.typeFor(mt.parameterType(i)); + final Type e = Type.typeFor(type.parameterType(i)); //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution //we also currently don't support boolean as a javascript function callsite type. diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunctions.java b/src/jdk/nashorn/internal/runtime/CompiledFunctions.java index 1ddc42b5..ba5ae66c 100644 --- a/src/jdk/nashorn/internal/runtime/CompiledFunctions.java +++ b/src/jdk/nashorn/internal/runtime/CompiledFunctions.java @@ -24,6 +24,7 @@ */ package jdk.nashorn.internal.runtime; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.Iterator; import java.util.TreeSet; @@ -35,6 +36,8 @@ import java.util.TreeSet; @SuppressWarnings("serial") final class CompiledFunctions extends TreeSet<CompiledFunction> { + private CompiledFunction generic; + CompiledFunction best(final MethodType type) { final Iterator<CompiledFunction> iter = iterator(); while (iter.hasNext()) { @@ -43,13 +46,10 @@ final class CompiledFunctions extends TreeSet<CompiledFunction> { return next; } } - return mostGeneric(); + return generic(); } boolean needsCallee() { - for (final CompiledFunction inv : this) { - assert ScriptFunctionData.needsCallee(inv.getInvoker()) == ScriptFunctionData.needsCallee(mostGeneric().getInvoker()); - } return ScriptFunctionData.needsCallee(mostGeneric().getInvoker()); } @@ -57,6 +57,48 @@ final class CompiledFunctions extends TreeSet<CompiledFunction> { return last(); } + CompiledFunction generic() { + CompiledFunction gen = this.generic; + if (gen == null) { + gen = this.generic = makeGeneric(mostGeneric()); + } + return gen; + } + + private static CompiledFunction makeGeneric(final CompiledFunction func) { + final MethodHandle invoker = composeGenericMethod(func.getInvoker()); + final MethodHandle constructor = func.hasConstructor() ? composeGenericMethod(func.getConstructor()) : null; + return new CompiledFunction(invoker.type(), invoker, constructor); + } + + /** + * Takes a method handle, and returns a potentially different method handle that can be used in + * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}. + * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into + * {@code Object} as well, except for the following ones: + * <ul> + * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> + * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself + * (callee) as an argument.</li> + * </ul> + * + * @param mh the original method handle + * + * @return the new handle, conforming to the rules above. + */ + private static MethodHandle composeGenericMethod(final MethodHandle mh) { + final MethodType type = mh.type(); + final boolean isVarArg = ScriptFunctionData.isVarArg(mh); + final int paramCount = isVarArg ? type.parameterCount() - 1 : type.parameterCount(); + + MethodType newType = MethodType.genericMethodType(paramCount, isVarArg); + + if (ScriptFunctionData.needsCallee(mh)) { + newType = newType.changeParameterType(0, ScriptFunction.class); + } + return type.equals(newType) ? mh : mh.asType(newType); + } + /** * Is the given type even more specific than this entire list? That means * we have an opportunity for more specific versions of the method diff --git a/src/jdk/nashorn/internal/runtime/ConsString.java b/src/jdk/nashorn/internal/runtime/ConsString.java index 9cf51552..8f764f4b 100644 --- a/src/jdk/nashorn/internal/runtime/ConsString.java +++ b/src/jdk/nashorn/internal/runtime/ConsString.java @@ -57,10 +57,7 @@ public final class ConsString implements CharSequence { @Override public String toString() { - if (!flat) { - flatten(); - } - return (String) left; + return (String) flattened(); } @Override @@ -70,18 +67,19 @@ public final class ConsString implements CharSequence { @Override public char charAt(final int index) { - if (!flat) { - flatten(); - } - return left.charAt(index); + return flattened().charAt(index); } @Override public CharSequence subSequence(final int start, final int end) { + return flattened().subSequence(start, end); + } + + private CharSequence flattened() { if (!flat) { flatten(); } - return left.subSequence(start, end); + return left; } private void flatten() { diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java index 7c69eb52..c9deee35 100644 --- a/src/jdk/nashorn/internal/runtime/Context.java +++ b/src/jdk/nashorn/internal/runtime/Context.java @@ -91,6 +91,11 @@ public final class Context { */ public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; + // nashorn load psuedo URL prefixes + private static final String LOAD_CLASSPATH = "classpath:"; + private static final String LOAD_FX = "fx:"; + private static final String LOAD_NASHORN = "nashorn:"; + /* Force DebuggerSupport to be loaded. */ static { DebuggerSupport.FORCELOAD = true; @@ -134,6 +139,11 @@ public final class Context { public long getUniqueScriptId() { return context.getUniqueScriptId(); } + + @Override + public long getUniqueEvalId() { + return context.getUniqueEvalId(); + } } /** Is Context global debug mode enabled ? */ @@ -233,10 +243,14 @@ public final class Context { /** Unique id for script. Used only when --loader-per-compile=false */ private final AtomicLong uniqueScriptId; + /** Unique id for 'eval' */ + private final AtomicLong uniqueEvalId; + private static final ClassLoader myLoader = Context.class.getClassLoader(); private static final StructureLoader sharedLoader; - /*package-private*/ ClassLoader getSharedLoader() { + /*package-private*/ @SuppressWarnings("static-method") + ClassLoader getSharedLoader() { return sharedLoader; } @@ -315,6 +329,7 @@ public final class Context { this.uniqueScriptId = new AtomicLong(); } this.errors = errors; + this.uniqueEvalId = new AtomicLong(); // if user passed -classpath option, make a class loader with that and set it as // thread context class loader so that script can access classes from that path. @@ -501,21 +516,26 @@ public final class Context { // or a ScriptObject that has "name" and "source" (string valued) properties. if (src instanceof String) { final String srcStr = (String)src; - final File file = new File(srcStr); - if (srcStr.indexOf(':') != -1) { - if ((source = loadInternal(srcStr, "nashorn:", "resources/")) == null && - (source = loadInternal(srcStr, "fx:", "resources/fx/")) == null) { - URL url; - try { - //check for malformed url. if malformed, it may still be a valid file - url = new URL(srcStr); - } catch (final MalformedURLException e) { - url = file.toURI().toURL(); + if (srcStr.startsWith(LOAD_CLASSPATH)) { + URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); + source = (url != null)? new Source(url.toString(), url) : null; + } else { + final File file = new File(srcStr); + if (srcStr.indexOf(':') != -1) { + if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && + (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { + URL url; + try { + //check for malformed url. if malformed, it may still be a valid file + url = new URL(srcStr); + } catch (final MalformedURLException e) { + url = file.toURI().toURL(); + } + source = new Source(url.toString(), url); } - source = new Source(url.toString(), url); + } else if (file.isFile()) { + source = new Source(srcStr, file); } - } else if (file.isFile()) { - source = new Source(srcStr, file); } } else if (src instanceof File && ((File)src).isFile()) { final File file = (File)src; @@ -610,36 +630,53 @@ public final class Context { } /** + * Checks that the given Class can be accessed from no permissions context. + * + * @param clazz Class object + * @throw SecurityException if not accessible + */ + public static void checkPackageAccess(final Class<?> clazz) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + Class<?> bottomClazz = clazz; + while (bottomClazz.isArray()) { + bottomClazz = bottomClazz.getComponentType(); + } + checkPackageAccess(sm, bottomClazz.getName()); + } + } + + /** * Checks that the given package can be accessed from no permissions context. * + * @param sm current security manager instance * @param fullName fully qualified package name * @throw SecurityException if not accessible */ - public static void checkPackageAccess(final String fullName) { + private static void checkPackageAccess(final SecurityManager sm, final String fullName) { + sm.getClass(); // null check final int index = fullName.lastIndexOf('.'); if (index != -1) { - final SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - AccessController.doPrivileged(new PrivilegedAction<Void>() { - @Override - public Void run() { - sm.checkPackageAccess(fullName.substring(0, index)); - return null; - } - }, NO_PERMISSIONS_ACC_CTXT); - } + final String pkgName = fullName.substring(0, index); + AccessController.doPrivileged(new PrivilegedAction<Void>() { + @Override + public Void run() { + sm.checkPackageAccess(pkgName); + return null; + } + }, NO_PERMISSIONS_ACC_CTXT); } } /** - * Checks that the given package can be accessed from no permissions context. + * Checks that the given Class can be accessed from no permissions context. * - * @param fullName fully qualified package name + * @param clazz Class object * @return true if package is accessible, false otherwise */ - public static boolean isAccessiblePackage(final String fullName) { + private static boolean isAccessiblePackage(final Class<?> clazz) { try { - checkPackageAccess(fullName); + checkPackageAccess(clazz); return true; } catch (final SecurityException se) { return false; @@ -653,7 +690,7 @@ public final class Context { * @return true if Class is accessible, false otherwise */ public static boolean isAccessibleClass(final Class<?> clazz) { - return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz.getName()); + return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); } /** @@ -667,8 +704,16 @@ public final class Context { * @throws ClassNotFoundException if class cannot be resolved */ public Class<?> findClass(final String fullName) throws ClassNotFoundException { + if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { + // don't allow array class names or internal names. + throw new ClassNotFoundException(fullName); + } + // check package access as soon as possible! - checkPackageAccess(fullName); + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkPackageAccess(sm, fullName); + } // try the script -classpath loader, if that is set if (classPathLoader != null) { @@ -803,6 +848,18 @@ public final class Context { return Context.getContextTrusted(); } + private URL getResourceURL(final String resName) { + // try the classPathLoader if we have and then + // try the appLoader if non-null. + if (classPathLoader != null) { + return classPathLoader.getResource(resName); + } else if (appLoader != null) { + return appLoader.getResource(resName); + } + + return null; + } + private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { ScriptFunction script = null; @@ -907,6 +964,10 @@ public final class Context { }, CREATE_LOADER_ACC_CTXT); } + private long getUniqueEvalId() { + return uniqueEvalId.getAndIncrement(); + } + private long getUniqueScriptId() { return uniqueScriptId.getAndIncrement(); } diff --git a/src/jdk/nashorn/internal/runtime/DebugLogger.java b/src/jdk/nashorn/internal/runtime/DebugLogger.java index fa0dbede..e832c861 100644 --- a/src/jdk/nashorn/internal/runtime/DebugLogger.java +++ b/src/jdk/nashorn/internal/runtime/DebugLogger.java @@ -65,7 +65,17 @@ public final class DebugLogger { } else { this.logger = Logging.getLogger(loggerName); } - this.isEnabled = logger.getLevel() != Level.OFF; + assert logger != null; + this.isEnabled = getLevel() != Level.OFF; + } + + /** + * Do not currently support chaining this with parent logger. Logger level null + * means disabled + * @return level + */ + private Level getLevel() { + return logger.getLevel() == null ? Level.OFF : logger.getLevel(); } /** @@ -126,7 +136,7 @@ public final class DebugLogger { * @return true if level is above the given one */ public boolean levelAbove(final Level level) { - return logger.getLevel().intValue() > level.intValue(); + return getLevel().intValue() > level.intValue(); } /** diff --git a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java index f28ed6ae..c7a410ea 100644 --- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java +++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java @@ -40,7 +40,7 @@ final class FinalScriptFunctionData extends ScriptFunctionData { * * @param name name * @param arity arity - * @param list precompiled code + * @param functions precompiled code * @param isStrict strict * @param isBuiltin builtin * @param isConstructor constructor @@ -73,12 +73,13 @@ final class FinalScriptFunctionData extends ScriptFunctionData { } private void addInvoker(final MethodHandle mh) { - boolean needsCallee = needsCallee(mh); if (isConstructor(mh)) { - //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor - //is too conservative a check. However, isConstructor(mh) always implies isConstructor param + // only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor + // is too conservative a check. However, isConstructor(mh) always implies isConstructor param assert isConstructor(); - code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor + final MethodHandle invoker = MH.insertArguments(mh, 0, false); + final MethodHandle constructor = composeConstructor(MH.insertArguments(mh, 0, true)); + code.add(new CompiledFunction(mh.type(), invoker, constructor)); } else { code.add(new CompiledFunction(mh.type(), mh)); } diff --git a/src/jdk/nashorn/internal/runtime/GlobalObject.java b/src/jdk/nashorn/internal/runtime/GlobalObject.java index 7e01fd6e..3779f3bd 100644 --- a/src/jdk/nashorn/internal/runtime/GlobalObject.java +++ b/src/jdk/nashorn/internal/runtime/GlobalObject.java @@ -38,9 +38,10 @@ import jdk.nashorn.internal.runtime.linker.InvokeByName; public interface GlobalObject { /** * Is this global of the given Context? + * @param ctxt the context * @return true if this global belongs to the given Context */ - public boolean isOfContext(Context context); + public boolean isOfContext(final Context ctxt); /** * Does this global belong to a strict Context? diff --git a/src/jdk/nashorn/internal/runtime/JSType.java b/src/jdk/nashorn/internal/runtime/JSType.java index 93f0835b..54130196 100644 --- a/src/jdk/nashorn/internal/runtime/JSType.java +++ b/src/jdk/nashorn/internal/runtime/JSType.java @@ -31,6 +31,8 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; +import java.util.Deque; +import java.util.List; import jdk.internal.dynalink.beans.StaticClass; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.internal.codegen.CompilerConstants.Call; @@ -86,6 +88,9 @@ public enum JSType { /** JavaScript compliant conversion function from Object to number */ public static final Call TO_NUMBER = staticCall(myLookup, JSType.class, "toNumber", double.class, Object.class); + /** JavaScript compliant conversion function from Object to String */ + public static final Call TO_STRING = staticCall(myLookup, JSType.class, "toString", String.class, Object.class); + /** JavaScript compliant conversion function from Object to int32 */ public static final Call TO_INT32 = staticCall(myLookup, JSType.class, "toInt32", int.class, Object.class); @@ -104,14 +109,20 @@ public enum JSType { /** JavaScript compliant conversion function from number to int64 */ public static final Call TO_INT64_D = staticCall(myLookup, JSType.class, "toInt64", long.class, double.class); - /** JavaScript compliant conversion function from Object to String */ - public static final Call TO_STRING = staticCall(myLookup, JSType.class, "toString", String.class, Object.class); - /** JavaScript compliant conversion function from number to String */ public static final Call TO_STRING_D = staticCall(myLookup, JSType.class, "toString", String.class, double.class); - /** JavaScript compliant conversion function from Object to primitive */ - public static final Call TO_PRIMITIVE = staticCall(myLookup, JSType.class, "toPrimitive", Object.class, Object.class); + /** Combined call to toPrimitive followed by toString. */ + public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class, Object.class); + + /** Method handle to convert a JS Object to a Java array. */ + public static final Call TO_JAVA_ARRAY = staticCall(myLookup, JSType.class, "toJavaArray", Object.class, Object.class, Class.class); + + /** Method handle to convert a JS Object to a Java List. */ + public static final Call TO_JAVA_LIST = staticCall(myLookup, JSType.class, "toJavaList", List.class, Object.class); + + /** Method handle to convert a JS Object to a Java deque. */ + public static final Call TO_JAVA_DEQUE = staticCall(myLookup, JSType.class, "toJavaDeque", Deque.class, Object.class); private static final double INT32_LIMIT = 4294967296.0; @@ -202,26 +213,6 @@ public enum JSType { } /** - * Get the smallest integer representation of a number. Returns an Integer - * for something that is int representable, and Long for something that - * is long representable. If the number needs to be a double, this is an - * identity function - * - * @param number number to check - * - * @return Number instanceof the narrowest possible integer representation for number - */ - public static Number narrowestIntegerRepresentation(final double number) { - if (isRepresentableAsInt(number)) { - return (int)number; - } else if (isRepresentableAsLong(number)) { - return (long)number; - } else { - return number; - } - } - - /** * Check whether an object is primitive * * @param obj an object @@ -258,12 +249,11 @@ public enum JSType { * @return the primitive form of the object */ public static Object toPrimitive(final Object obj, final Class<?> hint) { - if (!(obj instanceof ScriptObject)) { - return obj; - } + return obj instanceof ScriptObject ? toPrimitive((ScriptObject)obj, hint) : obj; + } - final ScriptObject sobj = (ScriptObject)obj; - final Object result = sobj.getDefaultValue(hint); + private static Object toPrimitive(final ScriptObject sobj, final Class<?> hint) { + final Object result = sobj.getDefaultValue(hint); if (!isPrimitive(result)) { throw typeError("bad.default.value", result.toString()); @@ -273,6 +263,17 @@ public enum JSType { } /** + * Combines a hintless toPrimitive and a toString call. + * + * @param obj an object + * + * @return the string form of the primitive form of the object + */ + public static String toPrimitiveToString(Object obj) { + return toString(toPrimitive(obj)); + } + + /** * JavaScript compliant conversion of number to boolean * * @param num a number @@ -476,6 +477,19 @@ public enum JSType { return toNumberGeneric(obj); } + + /** + * JavaScript compliant conversion of Object to number + * See ECMA 9.3 ToNumber + * + * @param obj an object + * + * @return a number + */ + public static double toNumber(final ScriptObject obj) { + return toNumber(toPrimitive(obj, Number.class)); + } + /** * Digit representation for a character * @@ -872,9 +886,9 @@ public enum JSType { */ public static Object toJavaArray(final Object obj, final Class<?> componentType) { if (obj instanceof ScriptObject) { - return convertArray(((ScriptObject)obj).getArray().asObjectArray(), componentType); + return ((ScriptObject)obj).getArray().asArrayOfType(componentType); } else if (obj instanceof JSObject) { - final ArrayLikeIterator itr = ArrayLikeIterator.arrayLikeIterator(obj); + final ArrayLikeIterator<?> itr = ArrayLikeIterator.arrayLikeIterator(obj); final int len = (int) itr.getLength(); final Object[] res = new Object[len]; int idx = 0; @@ -882,6 +896,8 @@ public enum JSType { res[idx++] = itr.next(); } return convertArray(res, componentType); + } else if(obj == null) { + return null; } else { throw new IllegalArgumentException("not a script object"); } @@ -895,6 +911,15 @@ public enum JSType { * @return converted Java array */ public static Object convertArray(final Object[] src, final Class<?> componentType) { + if(componentType == Object.class) { + for(int i = 0; i < src.length; ++i) { + final Object e = src[i]; + if(e instanceof ConsString) { + src[i] = e.toString(); + } + } + } + final int l = src.length; final Object dst = Array.newInstance(componentType, l); final MethodHandle converter = Bootstrap.getLinkerServices().getTypeConverter(Object.class, componentType); @@ -911,6 +936,24 @@ public enum JSType { } /** + * Converts a JavaScript object to a Java List. See {@link ListAdapter} for details. + * @param obj the object to convert. Can be any array-like object. + * @return a List that is live-backed by the JavaScript object. + */ + public static List<?> toJavaList(final Object obj) { + return ListAdapter.create(obj); + } + + /** + * Converts a JavaScript object to a Java Deque. See {@link ListAdapter} for details. + * @param obj the object to convert. Can be any array-like object. + * @return a Deque that is live-backed by the JavaScript object. + */ + public static Deque<?> toJavaDeque(final Object obj) { + return ListAdapter.create(obj); + } + + /** * Check if an object is null or undefined * * @param obj object to check @@ -1009,7 +1052,11 @@ public enum JSType { } if (obj instanceof ScriptObject) { - return toNumber(toPrimitive(obj, Number.class)); + return toNumber((ScriptObject)obj); + } + + if (obj instanceof JSObject) { + return ((JSObject)obj).toNumber(); } return Double.NaN; diff --git a/src/jdk/nashorn/internal/runtime/ListAdapter.java b/src/jdk/nashorn/internal/runtime/ListAdapter.java index 41d34600..26c22c39 100644 --- a/src/jdk/nashorn/internal/runtime/ListAdapter.java +++ b/src/jdk/nashorn/internal/runtime/ListAdapter.java @@ -33,6 +33,7 @@ import java.util.NoSuchElementException; import java.util.RandomAccess; import java.util.concurrent.Callable; import jdk.nashorn.api.scripting.JSObject; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; @@ -119,10 +120,11 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random }); } + /** wrapped object */ protected final Object obj; // allow subclasses only in this package - ListAdapter(Object obj) { + ListAdapter(final Object obj) { this.obj = obj; } @@ -134,7 +136,8 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random */ public static ListAdapter create(final Object obj) { if (obj instanceof ScriptObject) { - return new ScriptObjectListAdapter((ScriptObject)obj); + final Object mirror = ScriptObjectMirror.wrap(obj, Context.getGlobal()); + return new JSObjectListAdapter((JSObject)mirror); } else if (obj instanceof JSObject) { return new JSObjectListAdapter((JSObject)obj); } else { @@ -143,22 +146,32 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override - public final Object get(int index) { + public final Object get(final int index) { checkRange(index); return getAt(index); } + /** + * Get object at an index + * @param index index in list + * @return object + */ protected abstract Object getAt(final int index); @Override - public Object set(int index, Object element) { + public Object set(final int index, final Object element) { checkRange(index); final Object prevValue = getAt(index); setAt(index, element); return prevValue; } - protected abstract void setAt(int index, Object element); + /** + * Set object at an index + * @param index index in list + * @param element element + */ + protected abstract void setAt(final int index, final Object element); private void checkRange(int index) { if(index < 0 || index >= size()) { @@ -167,18 +180,18 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override - public final void push(Object e) { + public final void push(final Object e) { addFirst(e); } @Override - public final boolean add(Object e) { + public final boolean add(final Object e) { addLast(e); return true; } @Override - public final void addFirst(Object e) { + public final void addFirst(final Object e) { try { final InvokeByName unshiftInvoker = getUNSHIFT(); final Object fn = unshiftInvoker.getGetter().invokeExact(obj); @@ -192,7 +205,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override - public final void addLast(Object e) { + public final void addLast(final Object e) { try { final InvokeByName pushInvoker = getPUSH(); final Object fn = pushInvoker.getGetter().invokeExact(obj); @@ -206,7 +219,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override - public final void add(int index, Object e) { + public final void add(final int index, final Object e) { try { if(index < 0) { throw invalidIndex(index); @@ -225,35 +238,35 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random throw invalidIndex(index); } } - } catch(RuntimeException | Error ex) { + } catch(final RuntimeException | Error ex) { throw ex; - } catch(Throwable t) { + } catch(final Throwable t) { throw new RuntimeException(t); } } - private static void checkFunction(Object fn, InvokeByName invoke) { + private static void checkFunction(final Object fn, final InvokeByName invoke) { if(!(Bootstrap.isCallable(fn))) { throw new UnsupportedOperationException("The script object doesn't have a function named " + invoke.getName()); } } - private static IndexOutOfBoundsException invalidIndex(int index) { + private static IndexOutOfBoundsException invalidIndex(final int index) { return new IndexOutOfBoundsException(String.valueOf(index)); } @Override - public final boolean offer(Object e) { + public final boolean offer(final Object e) { return offerLast(e); } @Override - public final boolean offerFirst(Object e) { + public final boolean offerFirst(final Object e) { addFirst(e); return true; } @Override - public final boolean offerLast(Object e) { + public final boolean offerLast(final Object e) { addLast(e); return true; } @@ -287,7 +300,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override - public final Object remove(int index) { + public final Object remove(final int index) { if(index < 0) { throw invalidIndex(index); } else if (index == 0) { @@ -333,11 +346,11 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override - protected final void removeRange(int fromIndex, int toIndex) { + protected final void removeRange(final int fromIndex, final int toIndex) { invokeSpliceRemove(fromIndex, toIndex - fromIndex); } - private void invokeSpliceRemove(int fromIndex, int count) { + private void invokeSpliceRemove(final int fromIndex, final int count) { try { final InvokeByName spliceRemoveInvoker = getSPLICE_REMOVE(); final Object fn = spliceRemoveInvoker.getGetter().invokeExact(obj); @@ -419,16 +432,16 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override - public final boolean removeFirstOccurrence(Object o) { + public final boolean removeFirstOccurrence(final Object o) { return removeOccurrence(o, iterator()); } @Override - public final boolean removeLastOccurrence(Object o) { + public final boolean removeLastOccurrence(final Object o) { return removeOccurrence(o, descendingIterator()); } - private static boolean removeOccurrence(Object o, Iterator<Object> it) { + private static boolean removeOccurrence(final Object o, final Iterator<Object> it) { while(it.hasNext()) { final Object e = it.next(); if(o == null ? e == null : o.equals(e)) { diff --git a/src/jdk/nashorn/internal/runtime/Property.java b/src/jdk/nashorn/internal/runtime/Property.java index e735ed11..d1dbe2ad 100644 --- a/src/jdk/nashorn/internal/runtime/Property.java +++ b/src/jdk/nashorn/internal/runtime/Property.java @@ -56,33 +56,36 @@ public abstract class Property { public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000; /** ECMA 8.6.1 - Is this property not writable? */ - public static final int NOT_WRITABLE = 0b0000_0000_0001; + public static final int NOT_WRITABLE = 1 << 0; /** ECMA 8.6.1 - Is this property not enumerable? */ - public static final int NOT_ENUMERABLE = 0b0000_0000_0010; + public static final int NOT_ENUMERABLE = 1 << 1; /** ECMA 8.6.1 - Is this property not configurable? */ - public static final int NOT_CONFIGURABLE = 0b0000_0000_0100; + public static final int NOT_CONFIGURABLE = 1 << 2; - private static final int MODIFY_MASK = 0b0000_0000_1111; + private static final int MODIFY_MASK = (NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE); /** Is this a spill property? See {@link AccessorProperty} */ - public static final int IS_SPILL = 0b0000_0001_0000; + public static final int IS_SPILL = 1 << 3; /** Is this a function parameter? */ - public static final int IS_PARAMETER = 0b0000_0010_0000; + public static final int IS_PARAMETER = 1 << 4; /** Is parameter accessed thru arguments? */ - public static final int HAS_ARGUMENTS = 0b0000_0100_0000; + public static final int HAS_ARGUMENTS = 1 << 5; /** Is this property always represented as an Object? See {@link ObjectClassGenerator} and dual fields flag. */ - public static final int IS_ALWAYS_OBJECT = 0b0000_1000_0000; + public static final int IS_ALWAYS_OBJECT = 1 << 6; /** Can this property be primitive? */ - public static final int CAN_BE_PRIMITIVE = 0b0001_0000_0000; + public static final int CAN_BE_PRIMITIVE = 1 << 7; /** Can this property be undefined? */ - public static final int CAN_BE_UNDEFINED = 0b0010_0000_0000; + public static final int CAN_BE_UNDEFINED = 1 << 8; + + /* Is this a function declaration property ? */ + public static final int IS_FUNCTION_DECLARATION = 1 << 9; /** Property key. */ private final String key; @@ -522,4 +525,12 @@ public abstract class Property { public boolean canBeUndefined() { return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED; } + + /** + * Check whether this property represents a function declaration. + * @return whether this property is a function declaration or not. + */ + public boolean isFunctionDeclaration() { + return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION; + } } diff --git a/src/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk/nashorn/internal/runtime/PropertyMap.java index e24364d6..03c4978a 100644 --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java @@ -26,6 +26,8 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.runtime.PropertyHashMap.EMPTY_HASHMAP; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; import java.lang.invoke.SwitchPoint; import java.lang.ref.WeakReference; @@ -50,6 +52,8 @@ import java.util.WeakHashMap; public final class PropertyMap implements Iterable<Object>, PropertyListener { /** Used for non extensible PropertyMaps, negative logic as the normal case is extensible. See {@link ScriptObject#preventExtensions()} */ public static final int NOT_EXTENSIBLE = 0b0000_0001; + /** Does this map contain valid array keys? */ + public static final int CONTAINS_ARRAY_KEYS = 0b0000_0010; /** This mask is used to preserve certain flags when cloning the PropertyMap. Others should not be copied */ private static final int CLONEABLE_FLAGS_MASK = 0b0000_1111; /** Has a listener been added to this property map. This flag is not copied when cloning a map. See {@link PropertyListener} */ @@ -91,12 +95,16 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @param fieldCount Number of fields in use. * @param fieldMaximum Number of fields available. * @param spillLength Number of spill slots used. + * @param containsArrayKeys True if properties contain numeric keys */ - private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum, final int spillLength) { + private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) { this.properties = properties; this.fieldCount = fieldCount; this.fieldMaximum = fieldMaximum; this.spillLength = spillLength; + if (containsArrayKeys) { + setContainsArrayKeys(); + } if (Context.DEBUG) { count++; @@ -104,15 +112,6 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** - * Constructor. - * - * @param properties A {@link PropertyHashMap} with initial contents. - */ - private PropertyMap(final PropertyHashMap properties) { - this(properties, 0, 0, 0); - } - - /** * Cloning constructor. * * @param propertyMap Existing property map. @@ -152,12 +151,15 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { if (Context.DEBUG) { duplicatedCount++; } - return new PropertyMap(this.properties); + return new PropertyMap(this.properties, 0, 0, 0, containsArrayKeys()); } /** * Public property map allocator. * + * <p>It is the caller's responsibility to make sure that {@code properties} does not contain + * properties with keys that are valid array indices.</p> + * * @param properties Collection of initial properties. * @param fieldCount Number of fields in use. * @param fieldMaximum Number of fields available. @@ -166,11 +168,15 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { */ public static PropertyMap newMap(final Collection<Property> properties, final int fieldCount, final int fieldMaximum, final int spillLength) { PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties); - return new PropertyMap(newProperties, fieldCount, fieldMaximum, spillLength); + return new PropertyMap(newProperties, fieldCount, fieldMaximum, spillLength, false); } /** * Public property map allocator. Used by nasgen generated code. + * + * <p>It is the caller's responsibility to make sure that {@code properties} does not contain + * properties with keys that are valid array indices.</p> + * * @param properties Collection of initial properties. * @return New {@link PropertyMap}. */ @@ -184,7 +190,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return New empty {@link PropertyMap}. */ public static PropertyMap newMap() { - return new PropertyMap(EMPTY_HASHMAP); + return new PropertyMap(EMPTY_HASHMAP, 0, 0, 0, false); } /** @@ -294,6 +300,9 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { if(!property.isSpill()) { newMap.fieldCount = Math.max(newMap.fieldCount, property.getSlot() + 1); } + if (isValidArrayIndex(getArrayIndex(property.getKey()))) { + newMap.setContainsArrayKeys(); + } newMap.spillLength += property.getSpillCount(); } @@ -408,6 +417,9 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { final PropertyMap newMap = new PropertyMap(this, newProperties); for (final Property property : otherProperties) { + if (isValidArrayIndex(getArrayIndex(property.getKey()))) { + newMap.setContainsArrayKeys(); + } newMap.spillLength += property.getSpillCount(); } @@ -700,6 +712,22 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** + * Check if this map contains properties with valid array keys + * + * @return {@code true} if this map contains properties with valid array keys + */ + public final boolean containsArrayKeys() { + return (flags & CONTAINS_ARRAY_KEYS) != 0; + } + + /** + * Flag this object as having array keys in defined properties + */ + private void setContainsArrayKeys() { + flags |= CONTAINS_ARRAY_KEYS; + } + + /** * Check whether a {@link PropertyListener} has been added to this map. * * @return {@code true} if {@link PropertyListener} exists diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java index a64479a5..e5ca3c9a 100644 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java @@ -33,6 +33,7 @@ import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; +import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.CompilerConstants; @@ -52,7 +53,7 @@ import jdk.nashorn.internal.parser.TokenType; public final class RecompilableScriptFunctionData extends ScriptFunctionData { /** FunctionNode with the code for this ScriptFunction */ - private volatile FunctionNode functionNode; + private FunctionNode functionNode; /** Source from which FunctionNode was parsed. */ private final Source source; @@ -64,7 +65,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { private final PropertyMap allocatorMap; /** Code installer used for all further recompilation/specialization of this ScriptFunction */ - private volatile CodeInstaller<ScriptEnvironment> installer; + private CodeInstaller<ScriptEnvironment> installer; /** Name of class where allocator function resides */ private final String allocatorClassName; @@ -100,9 +101,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { * @param allocatorMap allocator map to seed instances with, when constructing */ public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) { - super(functionNode.isAnonymous() ? - "" : - functionNode.getIdent().getName(), + super(functionName(functionNode), functionNode.getParameters().size(), functionNode.isStrict(), false, @@ -132,13 +131,27 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { if (source != null) { sb.append(source.getName()) .append(':') - .append(source.getLine(Token.descPosition(token))) + .append(functionNode.getLineNumber()) .append(' '); } return sb.toString() + super.toString(); } + private static String functionName(final FunctionNode fn) { + if (fn.isAnonymous()) { + return ""; + } else { + final FunctionNode.Kind kind = fn.getKind(); + if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) { + final String name = NameCodec.decode(fn.getIdent().getName()); + return name.substring(4); // 4 is "get " or "set " + } else { + return fn.getIdent().getName(); + } + } + } + private static long tokenFor(final FunctionNode fn) { final int position = Token.descPosition(fn.getFirstToken()); final int length = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken()); @@ -165,7 +178,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { } @Override - protected void ensureCodeGenerated() { + protected synchronized void ensureCodeGenerated() { if (!code.isEmpty()) { return; // nothing to do, we have code, at least some. } @@ -323,7 +336,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { } @Override - MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { + synchronized MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { final MethodType runtimeType = runtimeType(callSiteType, args); assert runtimeType.parameterCount() == callSiteType.parameterCount(); diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java index 14aab756..ae59855c 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java @@ -59,6 +59,9 @@ public abstract class ScriptFunction extends ScriptObject { /** Method handle for name getter for this ScriptFunction */ public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class); + /** Method handle used for implementing sync() in mozilla_compat */ + public static final MethodHandle INVOKE_SYNC = findOwnMH("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class); + /** Method handle for allocate function for this ScriptFunction */ static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class); @@ -301,6 +304,14 @@ public abstract class ScriptFunction extends ScriptObject { public abstract void setPrototype(Object prototype); /** + * Create a function that invokes this function synchronized on {@code sync} or the self object + * of the invocation. + * @param sync the Object to synchronize on, or undefined + * @return synchronized function + */ + public abstract ScriptFunction makeSynchronizedFunction(Object sync); + + /** * Return the most appropriate invoke handle if there are specializations * @param type most specific method type to look for invocation with * @param args args for trampoline invocation @@ -326,7 +337,7 @@ public abstract class ScriptFunction extends ScriptObject { * @param self self reference * @return bound invoke handle */ - public final MethodHandle getBoundInvokeHandle(final ScriptObject self) { + public final MethodHandle getBoundInvokeHandle(final Object self) { return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self); } @@ -614,6 +625,15 @@ public abstract class ScriptFunction extends ScriptObject { return result; } + @SuppressWarnings("unused") + private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args) + throws Throwable { + final Object syncObj = sync == UNDEFINED ? self : sync; + synchronized (syncObj) { + return func.invoke(self, args); + } + } + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { final Class<?> own = ScriptFunction.class; final MethodType mt = MH.type(rtype, types); diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java index 06a12c00..4dbbfba9 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java @@ -213,13 +213,13 @@ public abstract class ScriptFunctionData { */ public final MethodHandle getGenericInvoker() { ensureCodeGenerated(); - return composeGenericMethod(code.mostGeneric().getInvoker()); + return code.generic().getInvoker(); } final MethodHandle getGenericConstructor() { ensureCodeGenerated(); - ensureConstructor(code.mostGeneric()); - return composeGenericMethod(code.mostGeneric().getConstructor()); + ensureConstructor(code.generic()); + return code.generic().getConstructor(); } private CompiledFunction getBest(final MethodType callSiteType) { @@ -267,18 +267,17 @@ public abstract class ScriptFunctionData { } /** - * Compose a constructor given a primordial constructor handle - * - * @param ctor primordial constructor handle - * @param needsCallee do we need to pass a callee + * Compose a constructor given a primordial constructor handle. * + * @param ctor primordial constructor handle * @return the composed constructor */ - protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) { + protected MethodHandle composeConstructor(final MethodHandle ctor) { // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having // "this" in the first argument position is what allows the elegant folded composition of // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor // always returns Object. + final boolean needsCallee = needsCallee(ctor); MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor; composedCtor = changeReturnTypeToObject(composedCtor); @@ -472,33 +471,6 @@ public abstract class ScriptFunctionData { } /** - * Takes a method handle, and returns a potentially different method handle that can be used in - * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}. - * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into - * {@code Object} as well, except for the following ones: - * <ul> - * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> - * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself - * (callee) as an argument.</li> - * </ul> - * - * @param mh the original method handle - * - * @return the new handle, conforming to the rules above. - */ - protected MethodHandle composeGenericMethod(final MethodHandle mh) { - final MethodType type = mh.type(); - MethodType newType = type.generic(); - if (isVarArg(mh)) { - newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); - } - if (needsCallee(mh)) { - newType = newType.changeParameterType(0, ScriptFunction.class); - } - return type.equals(newType) ? mh : mh.asType(newType); - } - - /** * Execute this script function. * * @param self Target object. @@ -508,10 +480,9 @@ public abstract class ScriptFunctionData { * @throws Throwable if there is an exception/error with the invocation or thrown from it */ Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable { - final MethodHandle mh = getGenericInvoker(); - - final Object selfObj = convertThisObject(self); - final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; + final MethodHandle mh = getGenericInvoker(); + final Object selfObj = convertThisObject(self); + final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; if (isVarArg(mh)) { if (needsCallee(mh)) { @@ -531,6 +502,12 @@ public abstract class ScriptFunctionData { return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1)); case 5: return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); + case 6: + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); + case 7: + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); + case 8: + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); default: return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args)); } @@ -545,15 +522,20 @@ public abstract class ScriptFunctionData { return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); case 4: return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); + case 5: + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); + case 6: + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); + case 7: + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); default: return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args)); } } Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable { - final MethodHandle mh = getGenericConstructor(); - - final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; + final MethodHandle mh = getGenericConstructor(); + final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; if (isVarArg(mh)) { if (needsCallee(mh)) { @@ -573,6 +555,12 @@ public abstract class ScriptFunctionData { return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1)); case 4: return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2)); + case 5: + return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); + case 6: + return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); + case 7: + return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); default: return mh.invokeWithArguments(withArguments(fn, paramCount, args)); } @@ -587,6 +575,12 @@ public abstract class ScriptFunctionData { return mh.invokeExact(getArg(args, 0), getArg(args, 1)); case 3: return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2)); + case 4: + return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); + case 5: + return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); + case 6: + return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); default: return mh.invokeWithArguments(withArguments(null, paramCount, args)); } @@ -664,20 +658,21 @@ public abstract class ScriptFunctionData { * @return the adapted handle */ private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) { - return MH.asType(mh, mh.type().changeReturnType(Object.class)); + final MethodType type = mh.type(); + return (type.returnType() == Object.class) ? mh : MH.asType(mh, type.changeReturnType(Object.class)); } private void ensureConstructor(final CompiledFunction inv) { if (!inv.hasConstructor()) { - inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker()))); + inv.setConstructor(composeConstructor(inv.getInvoker())); } } /** - * Heuristic to figure out if the method handle has a callee argument. If it's type is either - * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has - * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly - * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore + * Heuristic to figure out if the method handle has a callee argument. If it's type is + * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as + * the constructor above is not passed this information, and can't just blindly assume it's false + * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore * they also always receive a callee). * * @param mh the examined method handle @@ -685,18 +680,8 @@ public abstract class ScriptFunctionData { * @return true if the method handle expects a callee, false otherwise */ protected static boolean needsCallee(final MethodHandle mh) { - final MethodType type = mh.type(); - final int length = type.parameterCount(); - - if (length == 0) { - return false; - } - - if (type.parameterType(0) == boolean.class) { - return length > 1 && type.parameterType(1) == ScriptFunction.class; - } - - return type.parameterType(0) == ScriptFunction.class; + final MethodType type = mh.type(); + return (type.parameterCount() > 0 && type.parameterType(0) == ScriptFunction.class); } /** diff --git a/src/jdk/nashorn/internal/runtime/ScriptLoader.java b/src/jdk/nashorn/internal/runtime/ScriptLoader.java index 736932ea..78185b1c 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptLoader.java +++ b/src/jdk/nashorn/internal/runtime/ScriptLoader.java @@ -52,23 +52,10 @@ final class ScriptLoader extends NashornLoader { @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { checkPackageAccess(name); - try { - return super.loadClass(name, resolve); - } catch (final ClassNotFoundException | SecurityException e) { - // We'll get ClassNotFoundException for Nashorn 'struct' classes. - // Also, we'll get SecurityException for jdk.nashorn.internal.* - // classes. So, load these using to context's 'shared' loader. - // All these classes start with "jdk.nashorn.internal." prefix. - try { - if (name.startsWith(NASHORN_PKG_PREFIX)) { - return context.getSharedLoader().loadClass(name); - } - } catch (final ClassNotFoundException ignored) { - } - - // throw the original exception from here - throw e; + if (name.startsWith(NASHORN_PKG_PREFIX)) { + return context.getSharedLoader().loadClass(name); } + return super.loadClass(name, resolve); } // package-private and private stuff below this point diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java index a4ca6017..c7c82027 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java @@ -37,6 +37,8 @@ import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET; import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -131,6 +133,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr static final MethodHandle GETPROTO = findOwnMH("getProto", ScriptObject.class); static final MethodHandle SETPROTOCHECK = findOwnMH("setProtoCheck", void.class, Object.class); + static final MethodHandle MEGAMORPHIC_GET = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class); static final MethodHandle SETFIELD = findOwnMH("setField", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class); static final MethodHandle SETSPILL = findOwnMH("setSpill", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class); @@ -223,14 +226,23 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr for (final Property property : properties) { final String key = property.getKey(); - - if (newMap.findProperty(key) == null) { + final Property oldProp = newMap.findProperty(key); + if (oldProp == null) { if (property instanceof UserAccessorProperty) { final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source)); newMap = newMap.addProperty(prop); } else { newMap = newMap.addPropertyBind((AccessorProperty)property, source); } + } else { + // See ECMA section 10.5 Declaration Binding Instantiation + // step 5 processing each function declaration. + if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) { + if (oldProp instanceof UserAccessorProperty || + !(oldProp.isWritable() && oldProp.isEnumerable())) { + throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this)); + } + } } } @@ -388,7 +400,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable); } - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -505,7 +517,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr if (property == null) { // promoting an arrayData value to actual property addOwnProperty(key, propFlags, value); - removeArraySlot(key); + checkIntegerKey(key); } else { // Now set the new flags modifyOwnProperty(property, propFlags); @@ -591,8 +603,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * @param index key for property * @param value value to define */ - protected final void defineOwnProperty(final int index, final Object value) { - assert ArrayIndex.isValidArrayIndex(index) : "invalid array index"; + public final void defineOwnProperty(final int index, final Object value) { + assert isValidArrayIndex(index) : "invalid array index"; final long longIndex = ArrayIndex.toLongIndex(index); if (longIndex >= getArray().length()) { // make array big enough to hold.. @@ -602,9 +614,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } private void checkIntegerKey(final String key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { @@ -613,15 +625,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } } - private void removeArraySlot(final String key) { - final int index = ArrayIndex.getArrayIndex(key); - final ArrayData array = getArray(); - - if (array.has(index)) { - setArray(array.delete(index)); - } - } - /** * Add a new property to the object. * @@ -717,6 +720,28 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } /** + * Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a + * {@code boolean} value instead of a {@link FindProperty} object. + * @param key Property key. + * @param deep Whether the search should look up proto chain. + * @return true if the property was found. + */ + boolean hasProperty(final String key, final boolean deep) { + if (getMap().findProperty(key) != null) { + return true; + } + + if (deep) { + final ScriptObject myProto = getProto(); + if (myProto != null) { + return myProto.hasProperty(key, deep); + } + } + + return false; + } + + /** * Add a new property to the object. * <p> * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s @@ -1178,21 +1203,10 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * Check if this ScriptObject has array entries. This means that someone has * set values with numeric keys in the object. * - * Note: this can be O(n) up to the array length - * * @return true if array entries exists. */ public boolean hasArrayEntries() { - final ArrayData array = getArray(); - final long length = array.length(); - - for (long i = 0; i < length; i++) { - if (array.has((int)i)) { - return true; - } - } - - return false; + return getArray().length() > 0 || getMap().containsArrayKeys(); } /** @@ -1708,8 +1722,11 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr */ protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - final FindProperty find = findProperty(name, true); + if (request.isCallSiteUnstable()) { + return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator)); + } + final FindProperty find = findProperty(name, true); MethodHandle methodHandle; if (find == null) { @@ -1727,10 +1744,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr throw new AssertionError(); // never invoked with any other operation } - if (request.isCallSiteUnstable()) { - return findMegaMorphicGetMethod(desc, name); - } - final Class<?> returnType = desc.getMethodType().returnType(); final Property property = find.getProperty(); methodHandle = find.getGetter(returnType); @@ -1757,11 +1770,21 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard); } - private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) { - final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class); - final GuardedInvocation inv = findGetIndexMethod(mhType); + private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) { + final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod); + final MethodHandle guard = getScriptObjectGuard(desc.getMethodType()); + return new GuardedInvocation(invoker, guard); + } - return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); + @SuppressWarnings("unused") + private Object megamorphicGet(final String key, final boolean isMethod) { + final FindProperty find = findProperty(key, true); + + if (find != null) { + return getObjectValue(find); + } + + return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key); } /** @@ -1810,7 +1833,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr */ protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - if(request.isCallSiteUnstable()) { + if (request.isCallSiteUnstable()) { return findMegaMorphicSetMethod(desc, name); } @@ -2045,6 +2068,26 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return UNDEFINED; } + /** + * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined. + * @param name the method name + * @return the bound function, or undefined + */ + private Object getNoSuchMethod(final String name) { + final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); + + if (find == null) { + return invokeNoSuchProperty(name); + } + + final Object value = getObjectValue(find); + if (! (value instanceof ScriptFunction)) { + return UNDEFINED; + } + + return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name}); + } + private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) { return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap())); } @@ -2302,13 +2345,34 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } if (newLength < arrayLength) { - setArray(getArray().shrink(newLength)); - getArray().setLength(newLength); + long actualLength = newLength; + + // Check for numeric keys in property map and delete them or adjust length, depending on whether + // they're defined as configurable. See ES5 #15.4.5.2 + if (getMap().containsArrayKeys()) { + + for (long l = arrayLength - 1; l >= newLength; l--) { + final FindProperty find = findProperty(JSType.toString(l), false); + + if (find != null) { + + if (find.getProperty().isConfigurable()) { + deleteOwnProperty(find.getProperty()); + } else { + actualLength = l + 1; + break; + } + } + } + } + + setArray(getArray().shrink(actualLength)); + getArray().setLength(actualLength); } } private int getInt(final int index, final String key) { - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject object = this; ; ) { final FindProperty find = object.findProperty(key, false, false, this); @@ -2339,7 +2403,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public int getInt(final Object key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2351,7 +2415,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public int getInt(final double key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2363,7 +2427,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public int getInt(final long key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2385,7 +2449,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } private long getLong(final int index, final String key) { - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject object = this; ; ) { final FindProperty find = object.findProperty(key, false, false, this); @@ -2416,7 +2480,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public long getLong(final Object key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2428,7 +2492,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public long getLong(final double key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2440,7 +2504,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public long getLong(final long key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2462,7 +2526,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } private double getDouble(final int index, final String key) { - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject object = this; ; ) { final FindProperty find = object.findProperty(key, false, false, this); @@ -2493,7 +2557,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public double getDouble(final Object key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2505,7 +2569,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public double getDouble(final double key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2517,7 +2581,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public double getDouble(final long key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2539,7 +2603,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } private Object get(final int index, final String key) { - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject object = this; ; ) { final FindProperty find = object.findProperty(key, false, false, this); @@ -2570,7 +2634,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public Object get(final Object key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2582,7 +2646,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public Object get(final double key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2594,7 +2658,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public Object get(final long key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -2626,7 +2690,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final long oldLength = getArray().length(); final long longIndex = index & JSType.MAX_UINT; - if (!getArray().has(index)) { + if (getMap().containsArrayKeys()) { final String key = JSType.toString(longIndex); final FindProperty find = findProperty(key, true); @@ -2710,9 +2774,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public void set(final Object key, final int value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2722,14 +2786,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(key, JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final Object key, final long value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2739,14 +2804,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(key, JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final Object key, final double value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2756,14 +2822,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(key, JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final Object key, final Object value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2773,17 +2840,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - final String propName = JSType.toString(key); - final FindProperty find = findProperty(propName, true); - - setObject(find, strict, propName, value); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, value); } @Override public void set(final double key, final int value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2793,14 +2858,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final double key, final long value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2810,14 +2876,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final double key, final double value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2827,14 +2894,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final double key, final Object value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2844,14 +2912,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), value, strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, value); } @Override public void set(final long key, final int value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2861,14 +2930,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final long key, final long value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2878,14 +2948,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final long key, final double value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2895,14 +2966,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final long key, final Object value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2912,14 +2984,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), value, strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, value); } @Override public void set(final int key, final int value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2929,14 +3002,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final int key, final long value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2946,14 +3020,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final int key, final double value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2963,14 +3038,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), JSType.toObject(value), strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, JSType.toObject(value)); } @Override public void set(final int key, final Object value, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { if (getArray().has(index)) { setArray(getArray().set(index, value, strict)); } else { @@ -2980,14 +3056,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return; } - set(JSType.toObject(key), value, strict); + final String propName = JSType.toString(key); + setObject(findProperty(propName, true), strict, propName, value); } @Override public boolean has(final Object key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject self = this; self != null; self = self.getProto()) { if (self.getArray().has(index)) { return true; @@ -2995,16 +3072,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } } - final FindProperty find = findProperty(JSType.toString(key), true); - - return find != null; + return hasProperty(JSType.toString(key), true); } @Override public boolean has(final double key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject self = this; self != null; self = self.getProto()) { if (self.getArray().has(index)) { return true; @@ -3012,16 +3087,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } } - final FindProperty find = findProperty(JSType.toString(key), true); - - return find != null; + return hasProperty(JSType.toString(key), true); } @Override public boolean has(final long key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject self = this; self != null; self = self.getProto()) { if (self.getArray().has(index)) { return true; @@ -3029,16 +3102,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } } - final FindProperty find = findProperty(JSType.toString(key), true); - - return find != null; + return hasProperty(JSType.toString(key), true); } @Override public boolean has(final int key) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); - if (ArrayIndex.isValidArrayIndex(index)) { + if (isValidArrayIndex(index)) { for (ScriptObject self = this; self != null; self = self.getProto()) { if (self.getArray().has(index)) { return true; @@ -3046,66 +3117,32 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } } - final FindProperty find = findProperty(JSType.toString(key), true); - - return find != null; + return hasProperty(JSType.toString(key), true); } @Override public boolean hasOwnProperty(final Object key) { - final int index = ArrayIndex.getArrayIndex(key); - - if (getArray().has(index)) { - return true; - } - - final FindProperty find = findProperty(JSType.toString(key), false); - - return find != null; + return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false); } @Override public boolean hasOwnProperty(final int key) { - final int index = ArrayIndex.getArrayIndex(key); - - if (getArray().has(index)) { - return true; - } - - final FindProperty find = findProperty(JSType.toString(key), false); - - return find != null; + return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false); } @Override public boolean hasOwnProperty(final long key) { - final int index = ArrayIndex.getArrayIndex(key); - - if (getArray().has(index)) { - return true; - } - - final FindProperty find = findProperty(JSType.toString(key), false); - - return find != null; + return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false); } @Override public boolean hasOwnProperty(final double key) { - final int index = ArrayIndex.getArrayIndex(key); - - if (getArray().has(index)) { - return true; - } - - final FindProperty find = findProperty(JSType.toString(key), false); - - return find != null; + return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false); } @Override public boolean delete(final int key, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -3121,7 +3158,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public boolean delete(final long key, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -3137,7 +3174,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public boolean delete(final double key, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { @@ -3153,7 +3190,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr @Override public boolean delete(final Object key, final boolean strict) { - final int index = ArrayIndex.getArrayIndex(key); + final int index = getArrayIndex(key); final ArrayData array = getArray(); if (array.has(index)) { diff --git a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java index c9b40512..d9705676 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java @@ -47,6 +47,7 @@ import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.ir.debug.JSONWriter; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -258,6 +259,11 @@ public final class ScriptRuntime { return ((Map<?,?>)obj).keySet().iterator(); } + final Object wrapped = Global.instance().wrapAsObject(obj); + if (wrapped instanceof ScriptObject) { + return ((ScriptObject)wrapped).propertyIterator(); + } + return Collections.emptyIterator(); } @@ -336,6 +342,11 @@ public final class ScriptRuntime { return ((Iterable<?>)obj).iterator(); } + final Object wrapped = Global.instance().wrapAsObject(obj); + if (wrapped instanceof ScriptObject) { + return ((ScriptObject)wrapped).valueIterator(); + } + return Collections.emptyIterator(); } diff --git a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java index 6bd0479f..8a03533e 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java +++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java @@ -190,7 +190,7 @@ public final class ScriptingFunctions { char buffer[] = new char[1024]; try (final InputStreamReader inputStream = new InputStreamReader(process.getErrorStream())) { for (int length; (length = inputStream.read(buffer, 0, buffer.length)) != -1; ) { - outBuffer.append(buffer, 0, length); + errBuffer.append(buffer, 0, length); } } catch (IOException ex) { exception[1] = ex; diff --git a/src/jdk/nashorn/internal/runtime/Source.java b/src/jdk/nashorn/internal/runtime/Source.java index 9273e7a8..0455a86c 100644 --- a/src/jdk/nashorn/internal/runtime/Source.java +++ b/src/jdk/nashorn/internal/runtime/Source.java @@ -272,6 +272,10 @@ public final class Source { /** * Return line number of character position. + * + * <p>This method can be expensive for large sources as it iterates through + * all characters up to {@code position}.</p> + * * @param position Position of character in source content. * @return Line number. */ diff --git a/src/jdk/nashorn/internal/runtime/WithObject.java b/src/jdk/nashorn/internal/runtime/WithObject.java index 7dc8307c..6e03dc60 100644 --- a/src/jdk/nashorn/internal/runtime/WithObject.java +++ b/src/jdk/nashorn/internal/runtime/WithObject.java @@ -316,6 +316,10 @@ public final class WithObject extends ScriptObject implements Scope { return expression; } + /** + * Get the parent scope for this {@code WithObject} + * @return the parent scope + */ public ScriptObject getParentScope() { return getProto(); } diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java index ca4adb62..5c0c0476 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java @@ -461,7 +461,23 @@ public abstract class ArrayData { */ public abstract ArrayData slice(long from, long to); - private static Class<?> widestType(final Object... items) { + /** + * Fast splice operation. This just modifies the array according to the number of + * elements added and deleted but does not insert the added elements. Throws + * {@code UnsupportedOperationException} if fast splice operation is not supported + * for this class or arguments. + * + * @param start start index of splice operation + * @param removed number of removed elements + * @param added number of added elements + * @throws UnsupportedOperationException if fast splice is not supported for the class or arguments. + */ + public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + + static Class<?> widestType(final Object... items) { assert items.length > 0; Class<?> widest = Integer.class; diff --git a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java index add39912..07ddda2d 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java +++ b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java @@ -269,4 +269,32 @@ final class IntArrayData extends ArrayData { return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); } + + @Override + public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { + final long oldLength = length(); + final long newLength = oldLength - removed + added; + if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { + throw new UnsupportedOperationException(); + } + final ArrayData returnValue = (removed == 0) ? + EMPTY_ARRAY : new IntArrayData(Arrays.copyOfRange(array, start, start + removed), removed); + + if (newLength != oldLength) { + final int[] newArray; + + if (newLength > array.length) { + newArray = new int[ArrayData.nextSize((int)newLength)]; + System.arraycopy(array, 0, newArray, 0, start); + } else { + newArray = array; + } + + System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed)); + array = newArray; + setLength(newLength); + } + + return returnValue; + } } diff --git a/src/jdk/nashorn/internal/runtime/arrays/JavaArrayIterator.java b/src/jdk/nashorn/internal/runtime/arrays/JavaArrayIterator.java index 92a80029..a6b6bb9a 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/JavaArrayIterator.java +++ b/src/jdk/nashorn/internal/runtime/arrays/JavaArrayIterator.java @@ -77,4 +77,4 @@ class JavaArrayIterator extends ArrayLikeIterator<Object> { public void remove() { throw new UnsupportedOperationException("remove"); } -}
\ No newline at end of file +} diff --git a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java index 59a63155..4bb96aae 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java +++ b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java @@ -92,7 +92,7 @@ final class LongArrayData extends ArrayData { @Override public ArrayData convert(final Class<?> type) { - if (type == Long.class) { + if (type == Integer.class || type == Long.class) { return this; } final int length = (int) length(); @@ -238,4 +238,32 @@ final class LongArrayData extends ArrayData { final long newLength = to - start; return new LongArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); } + + @Override + public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { + final long oldLength = length(); + final long newLength = oldLength - removed + added; + if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { + throw new UnsupportedOperationException(); + } + final ArrayData returnValue = (removed == 0) ? + EMPTY_ARRAY : new LongArrayData(Arrays.copyOfRange(array, start, start + removed), removed); + + if (newLength != oldLength) { + final long[] newArray; + + if (newLength > array.length) { + newArray = new long[ArrayData.nextSize((int)newLength)]; + System.arraycopy(array, 0, newArray, 0, start); + } else { + newArray = array; + } + + System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed)); + array = newArray; + setLength(newLength); + } + + return returnValue; + } } diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java index 2ecf9d74..8254db63 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java +++ b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java @@ -218,4 +218,32 @@ final class NumberArrayData extends ArrayData { final long newLength = to - start; return new NumberArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); } + + @Override + public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { + final long oldLength = length(); + final long newLength = oldLength - removed + added; + if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { + throw new UnsupportedOperationException(); + } + final ArrayData returnValue = (removed == 0) ? + EMPTY_ARRAY : new NumberArrayData(Arrays.copyOfRange(array, start, start + removed), removed); + + if (newLength != oldLength) { + final double[] newArray; + + if (newLength > array.length) { + newArray = new double[ArrayData.nextSize((int)newLength)]; + System.arraycopy(array, 0, newArray, 0, start); + } else { + newArray = array; + } + + System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed)); + array = newArray; + setLength(newLength); + } + + return returnValue; + } } diff --git a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java index 136e6437..a9a1b5b2 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java +++ b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java @@ -206,4 +206,32 @@ final class ObjectArrayData extends ArrayData { final long newLength = to - start; return new ObjectArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); } + + @Override + public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { + final long oldLength = length(); + final long newLength = oldLength - removed + added; + if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { + throw new UnsupportedOperationException(); + } + final ArrayData returnValue = (removed == 0) ? + EMPTY_ARRAY : new ObjectArrayData(Arrays.copyOfRange(array, start, start + removed), removed); + + if (newLength != oldLength) { + final Object[] newArray; + + if (newLength > array.length) { + newArray = new Object[ArrayData.nextSize((int)newLength)]; + System.arraycopy(array, 0, newArray, 0, start); + } else { + newArray = array; + } + + System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed)); + array = newArray; + setLength(newLength); + } + + return returnValue; + } } diff --git a/src/jdk/nashorn/internal/runtime/arrays/ReverseJavaArrayIterator.java b/src/jdk/nashorn/internal/runtime/arrays/ReverseJavaArrayIterator.java index dcb37cfd..b90bae68 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/ReverseJavaArrayIterator.java +++ b/src/jdk/nashorn/internal/runtime/arrays/ReverseJavaArrayIterator.java @@ -55,4 +55,4 @@ final class ReverseJavaArrayIterator extends JavaArrayIterator { protected long bumpIndex() { return index--; } -}
\ No newline at end of file +} diff --git a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java index f725817a..0d5f68a1 100644 --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java @@ -63,7 +63,7 @@ public final class Bootstrap { final DynamicLinkerFactory factory = new DynamicLinkerFactory(); factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(), new BoundDynamicMethodLinker(), new JavaSuperAdapterLinker(), new JSObjectLinker(), new ReflectionCheckLinker()); - factory.setFallbackLinkers(new BeansLinker(), new NashornBottomLinker()); + factory.setFallbackLinkers(new NashornBeansLinker(), new NashornBottomLinker()); factory.setSyncOnRelink(true); final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1); if (relinkThreshold > -1) { diff --git a/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java b/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java index ccd497d7..77c1618b 100644 --- a/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java @@ -72,7 +72,7 @@ final class BoundDynamicMethodLinker implements TypeBasedGuardingDynamicLinker { type.changeParameterType(0, dynamicMethodClass).changeParameterType(1, boundThis.getClass())); // Delegate to BeansLinker - final GuardedInvocation inv = BeansLinker.getLinkerForClass(dynamicMethodClass).getGuardedInvocation( + final GuardedInvocation inv = NashornBeansLinker.getGuardedInvocation(BeansLinker.getLinkerForClass(dynamicMethodClass), linkRequest.replaceArguments(newDescriptor, args), linkerServices); if(inv == null) { return null; diff --git a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java index 45451a8b..f0d45317 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java @@ -25,25 +25,28 @@ package jdk.nashorn.internal.runtime.linker; -import jdk.nashorn.internal.lookup.MethodHandleFunctionality; -import jdk.nashorn.internal.lookup.MethodHandleFactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.HashMap; +import java.util.Map; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.internal.dynalink.support.CallSiteDescriptorFactory; -import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.api.scripting.JSObject; +import jdk.nashorn.internal.lookup.MethodHandleFactory; +import jdk.nashorn.internal.lookup.MethodHandleFunctionality; +import jdk.nashorn.internal.runtime.JSType; /** * A Dynalink linker to handle web browser built-in JS (DOM etc.) objects as well * as ScriptObjects from other Nashorn contexts. */ -final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { +final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory { @Override public boolean canLinkType(final Class<?> type) { return canLinkTypeStatic(type); @@ -75,6 +78,22 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { return Bootstrap.asType(inv, linkerServices, desc); } + @Override + public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception { + final boolean sourceIsAlwaysJSObject = JSObject.class.isAssignableFrom(sourceType); + if(!sourceIsAlwaysJSObject && !sourceType.isAssignableFrom(JSObject.class)) { + return null; + } + + final MethodHandle converter = CONVERTERS.get(targetType); + if(converter == null) { + return null; + } + + return new GuardedInvocation(converter, sourceIsAlwaysJSObject ? null : IS_JSOBJECT_GUARD).asType(MethodType.methodType(targetType, sourceType)); + } + + private static GuardedInvocation lookup(final CallSiteDescriptor desc) { final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); final int c = desc.getNameTokenCount(); @@ -87,9 +106,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { case "setElem": return c > 2 ? findSetMethod(desc) : findSetIndexMethod(); case "call": - return findCallMethod(desc, operator); - case "callMethod": - return findCallMethodMethod(desc, operator); + return findCallMethod(desc); case "new": return findNewMethod(desc); default: @@ -115,14 +132,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { return new GuardedInvocation(JSOBJECTLINKER_PUT, null, IS_JSOBJECT_GUARD); } - private static GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final String operator) { - final String methodName = desc.getNameToken(2); - MethodHandle func = MH.insertArguments(JSOBJECT_CALLMEMBER, 1, methodName); - func = MH.asCollector(func, Object[].class, desc.getMethodType().parameterCount() - 1); - return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD); - } - - private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final String operator) { + private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) { final MethodHandle func = MH.asCollector(JSOBJECT_CALL, Object[].class, desc.getMethodType().parameterCount() - 2); return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD); } @@ -140,7 +150,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { @SuppressWarnings("unused") private static Object get(final Object jsobj, final Object key) { if (key instanceof Integer) { - return ((JSObject)jsobj).getSlot((int)(Integer)key); + return ((JSObject)jsobj).getSlot((Integer)key); } else if (key instanceof Number) { final int index = getIndex((Number)key); if (index > -1) { @@ -155,7 +165,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { @SuppressWarnings("unused") private static void put(final Object jsobj, final Object key, final Object value) { if (key instanceof Integer) { - ((JSObject)jsobj).setSlot((int)(Integer)key, value); + ((JSObject)jsobj).setSlot((Integer)key, value); } else if (key instanceof Number) { ((JSObject)jsobj).setSlot(getIndex((Number)key), value); } else if (key instanceof String) { @@ -163,6 +173,25 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { } } + @SuppressWarnings("unused") + private static int toInt32(final JSObject obj) { + return JSType.toInt32(toNumber(obj)); + } + + @SuppressWarnings("unused") + private static long toInt64(final JSObject obj) { + return JSType.toInt64(toNumber(obj)); + } + + private static double toNumber(final JSObject obj) { + return obj == null ? 0 : obj.toNumber(); + } + + @SuppressWarnings("unused") + private static boolean toBoolean(final JSObject obj) { + return obj != null; + } + private static int getIndex(final Number n) { final double value = n.doubleValue(); return JSType.isRepresentableAsInt(value) ? (int)value : -1; @@ -178,27 +207,31 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { // method handles of JSObject class private static final MethodHandle JSOBJECT_GETMEMBER = findJSObjectMH("getMember", Object.class, String.class); private static final MethodHandle JSOBJECT_SETMEMBER = findJSObjectMH("setMember", Void.TYPE, String.class, Object.class); - private static final MethodHandle JSOBJECT_CALLMEMBER = findJSObjectMH("callMember", Object.class, String.class, Object[].class); private static final MethodHandle JSOBJECT_CALL = findJSObjectMH("call", Object.class, Object.class, Object[].class); private static final MethodHandle JSOBJECT_NEW = findJSObjectMH("newObject", Object.class, Object[].class); + private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>(); + static { + CONVERTERS.put(boolean.class, findOwnMH("toBoolean", boolean.class, JSObject.class)); + CONVERTERS.put(int.class, findOwnMH("toInt32", int.class, JSObject.class)); + CONVERTERS.put(long.class, findOwnMH("toInt64", long.class, JSObject.class)); + CONVERTERS.put(double.class, findOwnMH("toNumber", double.class, JSObject.class)); + } + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { - final Class<?> own = JSObjectLinker.class; - final MethodType mt = MH.type(rtype, types); - try { - return MH.findStatic(MethodHandles.lookup(), own, name, mt); - } catch (final MethodHandleFactory.LookupException e) { - return MH.findVirtual(MethodHandles.lookup(), own, name, mt); - } + return findMH(name, JSObjectLinker.class, rtype, types); } private static MethodHandle findJSObjectMH(final String name, final Class<?> rtype, final Class<?>... types) { - final Class<?> own = JSObject.class; + return findMH(name, JSObject.class, rtype, types); + } + + private static MethodHandle findMH(final String name, final Class<?> target, final Class<?> rtype, final Class<?>... types) { final MethodType mt = MH.type(rtype, types); try { - return MH.findVirtual(MethodHandles.publicLookup(), own, name, mt); + return MH.findStatic(MethodHandles.lookup(), target, name, mt); } catch (final MethodHandleFactory.LookupException e) { - return MH.findVirtual(MethodHandles.lookup(), own, name, mt); + return MH.findVirtual(MethodHandles.lookup(), target, name, mt); } } } diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java index 8ebdc810..5b8d4cfe 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java @@ -93,7 +93,7 @@ import sun.reflect.CallerSensitive; * its last argument preceded by original constructor arguments. This constructor will use the passed function as the * implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method * name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is - * invoked with {@code null} as its "this". + * invoked with global or UNDEFINED as its "this" depending whether the function is non-strict or not. * </li> * <li> * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java index 5130f3d6..b221d000 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java @@ -109,7 +109,7 @@ public final class JavaAdapterFactory { if (sm != null) { for (Class<?> type : types) { // check for restricted package access - Context.checkPackageAccess(type.getName()); + Context.checkPackageAccess(type); } } return getAdapterInfo(types).getAdapterClassFor(classOverrides); diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java index 6614304f..06ae1b11 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java @@ -29,6 +29,7 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -53,8 +54,8 @@ public final class JavaAdapterServices { * @return the appropriately adapted method handle for invoking the script function. */ public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type) { - // JS "this" will be null for SAMs - return adaptHandle(fn.getBoundInvokeHandle(null), type); + // JS "this" will be global object or undefined depending on if 'fn' is strict or not + return adaptHandle(fn.getBoundInvokeHandle(fn.isStrict()? ScriptRuntime.UNDEFINED : Context.getGlobal()), type); } /** diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java index f035071b..984d0f41 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java @@ -25,9 +25,9 @@ package jdk.nashorn.internal.runtime.linker; +import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; -import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -211,6 +211,20 @@ final class JavaArgumentConverters { return null; } else if (obj instanceof Long) { return (Long) obj; + } else if (obj instanceof Integer) { + return ((Integer)obj).longValue(); + } else if (obj instanceof Double) { + final Double d = (Double)obj; + if(Double.isInfinite(d.doubleValue())) { + return 0L; + } + return d.longValue(); + } else if (obj instanceof Float) { + final Float f = (Float)obj; + if(Float.isInfinite(f.floatValue())) { + return 0L; + } + return f.longValue(); } else if (obj instanceof Number) { return ((Number)obj).longValue(); } else if (obj instanceof String || obj instanceof ConsString) { diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java b/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java index 8e40805a..c42af1d8 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java @@ -100,8 +100,9 @@ final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker { type.changeParameterType(0, adapterClass), 0); // Delegate to BeansLinker - final GuardedInvocation guardedInv = BeansLinker.getLinkerForClass(adapterClass).getGuardedInvocation( - linkRequest.replaceArguments(newDescriptor, args), linkerServices); + final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation( + BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args), + linkerServices); final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass); if(guardedInv == null) { diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java new file mode 100644 index 00000000..e2db2b11 --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.runtime.linker; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import jdk.internal.dynalink.beans.BeansLinker; +import jdk.internal.dynalink.linker.ConversionComparator.Comparison; +import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.GuardingDynamicLinker; +import jdk.internal.dynalink.linker.LinkRequest; +import jdk.internal.dynalink.linker.LinkerServices; +import jdk.internal.dynalink.support.Lookup; +import jdk.nashorn.internal.runtime.ConsString; + +/** + * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified + * {@code asType} method that will ensure that we never pass internal engine objects that should not be externally + * observable (currently only ConsString) to Java APIs, but rather that we flatten it into a String. We can't just add + * this functionality as custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when + * the target method handle parameter signature is {@code Object}. + */ +public class NashornBeansLinker implements GuardingDynamicLinker { + private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class); + + private final BeansLinker beansLinker = new BeansLinker(); + + @Override + public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + return getGuardedInvocation(beansLinker, linkRequest, linkerServices); + } + + /** + * Delegates to the specified linker but injects its linker services wrapper so that it will apply all special + * conversions that this class does. + * @param delegateLinker the linker to which the actual work is delegated to. + * @param linkRequest the delegated link request + * @param linkerServices the original link services that will be augmented with special conversions + * @return the guarded invocation from the delegate, possibly augmented with special conversions + * @throws Exception if the delegate throws an exception + */ + public static GuardedInvocation getGuardedInvocation(final GuardingDynamicLinker delegateLinker, final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + return delegateLinker.getGuardedInvocation(linkRequest, new NashornBeansLinkerServices(linkerServices)); + } + + @SuppressWarnings("unused") + private static Object exportArgument(final Object arg) { + return arg instanceof ConsString ? arg.toString() : arg; + } + + private static class NashornBeansLinkerServices implements LinkerServices { + private final LinkerServices linkerServices; + + NashornBeansLinkerServices(final LinkerServices linkerServices) { + this.linkerServices = linkerServices; + } + + @Override + public MethodHandle asType(final MethodHandle handle, final MethodType fromType) { + final MethodHandle typed = linkerServices.asType(handle, fromType); + + final MethodType handleType = handle.type(); + final int paramCount = handleType.parameterCount(); + assert fromType.parameterCount() == handleType.parameterCount(); + + MethodHandle[] filters = null; + for(int i = 0; i < paramCount; ++i) { + if(shouldConvert(handleType.parameterType(i), fromType.parameterType(i))) { + if(filters == null) { + filters = new MethodHandle[paramCount]; + } + filters[i] = EXPORT_ARGUMENT; + } + } + + return filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed; + } + + private static boolean shouldConvert(final Class<?> handleType, final Class<?> fromType) { + return handleType == Object.class && fromType == Object.class; + } + + @Override + public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) { + return linkerServices.getTypeConverter(sourceType, targetType); + } + + @Override + public boolean canConvert(final Class<?> from, final Class<?> to) { + return linkerServices.canConvert(from, to); + } + + @Override + public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest) throws Exception { + return linkerServices.getGuardedInvocation(linkRequest); + } + + @Override + public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) { + return linkerServices.compareConversion(sourceType, targetType1, targetType2); + } + } +} diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java index 6dbcbdd3..c94df15b 100644 --- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java @@ -33,14 +33,18 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.HashMap; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardingDynamicLinker; +import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.support.Guards; import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; /** @@ -50,7 +54,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime; * setters for Java objects that couldn't be linked by any other linker, and throw appropriate ECMAScript errors for * attempts to invoke arbitrary Java objects as functions or constructors. */ -final class NashornBottomLinker implements GuardingDynamicLinker { +final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeConverterFactory { @Override public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) @@ -129,6 +133,29 @@ final class NashornBottomLinker implements GuardingDynamicLinker { throw new AssertionError("unknown call type " + desc); } + @Override + public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception { + final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); + return gi == null ? null : gi.asType(MH.type(targetType, sourceType)); + } + + /** + * Main part of the implementation of {@link GuardingTypeConverterFactory#convertToType(Class, Class)} that doesn't + * care about adapting the method signature; that's done by the invoking method. Returns conversion from Object to String/number/boolean (JS primitive types). + * @param sourceType the source type + * @param targetType the target type + * @return a guarded invocation that converts from the source type to the target type. + * @throws Exception if something goes wrong + */ + private static GuardedInvocation convertToTypeNoCast(final Class<?> sourceType, final Class<?> targetType) throws Exception { + final MethodHandle mh = CONVERTERS.get(targetType); + if (mh != null) { + return new GuardedInvocation(mh, null); + } + + return null; + } + private static GuardedInvocation getInvocation(final MethodHandle handle, final Object self, final LinkerServices linkerServices, final CallSiteDescriptor desc) { return Bootstrap.asType(new GuardedInvocation(handle, Guards.getClassGuard(self.getClass())), linkerServices, desc); } @@ -161,6 +188,15 @@ final class NashornBottomLinker implements GuardingDynamicLinker { throw new AssertionError("unknown call type " + desc); } + private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>(); + static { + CONVERTERS.put(boolean.class, JSType.TO_BOOLEAN.methodHandle()); + CONVERTERS.put(double.class, JSType.TO_NUMBER.methodHandle()); + CONVERTERS.put(int.class, JSType.TO_INTEGER.methodHandle()); + CONVERTERS.put(long.class, JSType.TO_LONG.methodHandle()); + CONVERTERS.put(String.class, JSType.TO_STRING.methodHandle()); + } + private static String getArgument(final LinkRequest linkRequest) { final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor(); if (desc.getNameTokenCount() > 2) { diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java index 3a23b141..27e4573f 100644 --- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java @@ -30,6 +30,10 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Modifier; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import javax.script.Bindings; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.ConversionComparator; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -38,6 +42,12 @@ import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.internal.dynalink.support.Guards; +import jdk.nashorn.api.scripting.JSObject; +import jdk.nashorn.api.scripting.ScriptObjectMirror; +import jdk.nashorn.api.scripting.ScriptUtils; +import jdk.nashorn.internal.objects.NativeArray; +import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.Undefined; @@ -47,6 +57,13 @@ import jdk.nashorn.internal.runtime.Undefined; * includes {@link ScriptFunction} and its subclasses) as well as {@link Undefined}. */ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator { + private static final ClassValue<MethodHandle> ARRAY_CONVERTERS = new ClassValue<MethodHandle>() { + @Override + protected MethodHandle computeValue(Class<?> type) { + return createArrayConverter(type); + } + }; + /** * Returns true if {@code ScriptObject} is assignable from {@code type}, or it is {@code Undefined}. */ @@ -103,6 +120,17 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp if (mh != null) { return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : IS_NASHORN_OR_UNDEFINED_TYPE); } + + final GuardedInvocation arrayConverter = getArrayConverter(sourceType, targetType); + if(arrayConverter != null) { + return arrayConverter; + } + + final GuardedInvocation mirrorConverter = getMirrorConverter(sourceType, targetType); + if(mirrorConverter != null) { + return mirrorConverter; + } + return getSamTypeConverter(sourceType, targetType); } @@ -129,6 +157,53 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp return null; } + /** + * Returns a guarded invocation that converts from a source type that is NativeArray to a Java array or List or + * Deque type. + * @param sourceType the source type (presumably NativeArray a superclass of it) + * @param targetType the target type (presumably an array type, or List or Deque) + * @return a guarded invocation that converts from the source type to the target type. null is returned if + * either the source type is neither NativeArray, nor a superclass of it, or if the target type is not an array + * type, List, or Deque. + */ + private static GuardedInvocation getArrayConverter(final Class<?> sourceType, final Class<?> targetType) { + final boolean isSourceTypeNativeArray = sourceType == NativeArray.class; + // If source type is more generic than ScriptFunction class, we'll need to use a guard + final boolean isSourceTypeGeneric = !isSourceTypeNativeArray && sourceType.isAssignableFrom(NativeArray.class); + + if (isSourceTypeNativeArray || isSourceTypeGeneric) { + final MethodHandle guard = isSourceTypeGeneric ? IS_NATIVE_ARRAY : null; + if(targetType.isArray()) { + return new GuardedInvocation(ARRAY_CONVERTERS.get(targetType), guard); + } + if(targetType == List.class) { + return new GuardedInvocation(JSType.TO_JAVA_LIST.methodHandle(), guard); + } + if(targetType == Deque.class) { + return new GuardedInvocation(JSType.TO_JAVA_DEQUE.methodHandle(), guard); + } + } + return null; + } + + private static MethodHandle createArrayConverter(final Class<?> type) { + assert type.isArray(); + final MethodHandle converter = MH.insertArguments(JSType.TO_JAVA_ARRAY.methodHandle(), 1, type.getComponentType()); + return MH.asType(converter, converter.type().changeReturnType(type)); + } + + private static GuardedInvocation getMirrorConverter(Class<?> sourceType, Class<?> targetType) { + // Could've also used (targetType.isAssignableFrom(ScriptObjectMirror.class) && targetType != Object.class) but + // it's probably better to explicitly spell out the supported target types + if (targetType == Map.class || targetType == Bindings.class || targetType == JSObject.class || targetType == ScriptObjectMirror.class) { + if(ScriptObject.class.isAssignableFrom(sourceType)) { + return new GuardedInvocation(CREATE_MIRROR, null); + } + return new GuardedInvocation(CREATE_MIRROR, IS_SCRIPT_OBJECT); + } + return null; + } + private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) { return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) && JavaAdapterFactory.isAutoConvertibleFromFunction(clazz); @@ -148,7 +223,26 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp @Override public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) { + if(sourceType == NativeArray.class) { + // Prefer lists, as they're less costly to create than arrays. + if(isList(targetType1)) { + if(!isList(targetType2)) { + return Comparison.TYPE_1_BETTER; + } + } else if(isList(targetType2)) { + return Comparison.TYPE_2_BETTER; + } + // Then prefer arrays + if(targetType1.isArray()) { + if(!targetType2.isArray()) { + return Comparison.TYPE_1_BETTER; + } + } else if(targetType2.isArray()) { + return Comparison.TYPE_2_BETTER; + } + } if(ScriptObject.class.isAssignableFrom(sourceType)) { + // Prefer interfaces if(targetType1.isInterface()) { if(!targetType2.isInterface()) { return Comparison.TYPE_1_BETTER; @@ -160,16 +254,27 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp return Comparison.INDETERMINATE; } + private static boolean isList(Class<?> clazz) { + return clazz == List.class || clazz == Deque.class; + } + + private static final MethodHandle IS_SCRIPT_OBJECT = Guards.isInstance(ScriptObject.class, MH.type(Boolean.TYPE, Object.class)); private static final MethodHandle IS_SCRIPT_FUNCTION = Guards.isInstance(ScriptFunction.class, MH.type(Boolean.TYPE, Object.class)); + private static final MethodHandle IS_NATIVE_ARRAY = Guards.isOfClass(NativeArray.class, MH.type(Boolean.TYPE, Object.class)); - private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined", - Boolean.TYPE, Object.class); + private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined", Boolean.TYPE, Object.class); + private static final MethodHandle CREATE_MIRROR = findOwnMH("createMirror", Object.class, Object.class); @SuppressWarnings("unused") private static boolean isNashornTypeOrUndefined(final Object obj) { return obj instanceof ScriptObject || obj instanceof Undefined; } + @SuppressWarnings("unused") + private static Object createMirror(final Object obj) { + return ScriptUtils.wrap(obj); + } + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { return MH.findStatic(MethodHandles.lookup(), NashornLinker.class, name, MH.type(rtype, types)); } diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java index 9c9f5283..72ed9766 100644 --- a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java @@ -70,7 +70,7 @@ final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker { // We intercept "new" on StaticClass instances to provide additional capabilities if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) { // make sure new is on accessible Class - Context.checkPackageAccess(receiverClass.getName()); + Context.checkPackageAccess(receiverClass); // Is the class abstract? (This includes interfaces.) if (NashornLinker.isAbstractClass(receiverClass)) { @@ -93,7 +93,7 @@ final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker { } private static GuardedInvocation delegate(LinkerServices linkerServices, final LinkRequest request) throws Exception { - return staticClassLinker.getGuardedInvocation(request, linkerServices); + return NashornBeansLinker.getGuardedInvocation(staticClassLinker, request, linkerServices); } private static GuardedInvocation checkNullConstructor(final GuardedInvocation ctorInvocation, final Class<?> receiverClass) { diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/base.js b/src/jdk/nashorn/internal/runtime/resources/fx/base.js index b0bfc3fb..382eabb2 100644 --- a/src/jdk/nashorn/internal/runtime/resources/fx/base.js +++ b/src/jdk/nashorn/internal/runtime/resources/fx/base.js @@ -33,7 +33,6 @@ var JFX_SWING_CLASSES = []; var JFX_SWT_CLASSES = []; function LOAD_FX_CLASSES(clsList) { - for each (var cls in clsList) { // Ex. Stage = Java.type("javafx.stage.Stage"); this[cls[cls.length - 1]] = Java.type(cls.join(".")); @@ -146,3 +145,5 @@ function LOAD_FX_CLASSES(clsList) { } } })(); + +LOAD_FX_CLASSES(JFX_BASE_CLASSES); diff --git a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js index 6b934ad4..85e2161c 100644 --- a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js +++ b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js @@ -41,7 +41,12 @@ Object.defineProperty(this, "JavaAdapter", { } }); + // importPackage +// avoid unnecessary chaining of __noSuchProperty__ again +// in case user loads this script more than once. +if (typeof importPackage == 'undefined') { + Object.defineProperty(this, "importPackage", { configurable: true, enumerable: false, writable: true, value: (function() { @@ -91,6 +96,19 @@ Object.defineProperty(this, "importPackage", { })() }); +} + +// sync +Object.defineProperty(this, "sync", { + configurable: true, enumerable: false, writable: true, + value: function(func, syncobj) { + if (arguments.length < 1 || arguments.length > 2 ) { + throw "sync(function [,object]) parameter count mismatch"; + } + return Packages.jdk.nashorn.api.scripting.ScriptUtils.makeSynchronizedFunction(func, syncobj); + } +}); + // Object.prototype.__defineGetter__ Object.defineProperty(Object.prototype, "__defineGetter__", { configurable: true, enumerable: false, writable: true, @@ -344,13 +362,16 @@ Object.defineProperty(String.prototype, "sup", { // Rhino: global.importClass Object.defineProperty(this, "importClass", { configurable: true, enumerable: false, writable: true, - value: function(clazz) { - if (Java.isType(clazz)) { - var className = Java.typeName(clazz); - var simpleName = className.substring(className.lastIndexOf('.') + 1); - this[simpleName] = clazz; - } else { - throw new TypeError(clazz + " is not a Java class"); + value: function() { + for (var arg in arguments) { + var clazz = arguments[arg]; + if (Java.isType(clazz)) { + var className = Java.typeName(clazz); + var simpleName = className.substring(className.lastIndexOf('.') + 1); + this[simpleName] = clazz; + } else { + throw new TypeError(clazz + " is not a Java class"); + } } } }); diff --git a/src/overview.html b/src/overview.html index 5418854f..e242a5b9 100644 --- a/src/overview.html +++ b/src/overview.html @@ -108,6 +108,6 @@ identity and doesn't carry i.e. static members. Again, see the link for {@code N <h2>Other non-standard built-in objects</h2> In addition to {@code Java}, Nashorn also exposes some other non-standard built-in objects: <a href="jdk/nashorn/internal/objects/NativeJSAdapter.html">{@code JSAdapter}</a>, -<a href="jdk/nashorn/internal/objects/NativeJavaImporter.html">{@code JavaImporter}, +<a href="jdk/nashorn/internal/objects/NativeJavaImporter.html">{@code JavaImporter}</a>, <a href="jdk/nashorn/internal/runtime/NativeJavaPackage.html">{@code Packages}.</a> </body> diff --git a/test/examples/array-micro.js b/test/examples/array-micro.js index 075e78d8..9bb898f6 100644 --- a/test/examples/array-micro.js +++ b/test/examples/array-micro.js @@ -90,6 +90,24 @@ bench("set", function() { array[6] = 6; }); +bench("push", function() { + var arr = [1, 2, 3]; + arr.push(4); + arr.push(5); + arr.push(6); +}); + +bench("pop", function() { + var arr = [1, 2, 3]; + arr.pop(); + arr.pop(); + arr.pop(); +}); + +bench("splice", function() { + [1, 2, 3].splice(0, 2, 5, 6, 7); +}); + var all = function(e) { return true; }; var none = function(e) { return false; }; diff --git a/test/script/assert.js b/test/script/assert.js index 25bc2599..3656f86c 100644 --- a/test/script/assert.js +++ b/test/script/assert.js @@ -61,3 +61,22 @@ Object.defineProperty(this, "fail", { } } }); + +Object.defineProperty(this, "printError", { + configuable: true, + enumerable: false, + writable: true, + value: function (e) { + var msg = e.message; + var str = e.name + ':'; + if (e.lineNumber > 0) { + str += e.lineNumber + ':'; + } + if (e.columnNumber > 0) { + str += e.columnNumber + ':'; + } + str += msg.substring(msg.indexOf(' ') + 1); + print(str); + } +}); + diff --git a/test/script/basic/JDK-8015355.js b/test/script/basic/JDK-8015355.js index bc39d8d9..aa1a39de 100644 --- a/test/script/basic/JDK-8015355.js +++ b/test/script/basic/JDK-8015355.js @@ -28,10 +28,6 @@ * @run */ -function fail(msg) { - print(msg); -} - function check(callback) { try { callback(); diff --git a/test/script/basic/JDK-8019508.js b/test/script/basic/JDK-8019508.js index d23035c6..88ecbec3 100644 --- a/test/script/basic/JDK-8019508.js +++ b/test/script/basic/JDK-8019508.js @@ -36,7 +36,7 @@ function checkObjLiteral(str) { if (! (e instanceof SyntaxError)) { fail("expected SyntaxError, got " + e); } - print(e.message.replace(/\\/g, '/')); + printError(e); } } diff --git a/test/script/basic/JDK-8019508.js.EXPECTED b/test/script/basic/JDK-8019508.js.EXPECTED index d5f81409..a3d883f0 100644 --- a/test/script/basic/JDK-8019508.js.EXPECTED +++ b/test/script/basic/JDK-8019508.js.EXPECTED @@ -1,12 +1,12 @@ -test/script/basic/JDK-8019508.js#33<eval>:1:2 Expected property id but found , +SyntaxError:33:Expected property id but found , ({,}) ^ -test/script/basic/JDK-8019508.js#33<eval>:1:2 Expected property id but found , +SyntaxError:33:Expected property id but found , ({, a:2 }) ^ -test/script/basic/JDK-8019508.js#33<eval>:1:6 Expected property id but found , +SyntaxError:33:Expected property id but found , ({a:3,,}) ^ -test/script/basic/JDK-8019508.js#33<eval>:1:6 Expected comma but found ident +SyntaxError:33:Expected comma but found ident ({a:3 b:2} ^ diff --git a/test/script/basic/JDK-8019553.js b/test/script/basic/JDK-8019553.js index d7546187..bec3a528 100644 --- a/test/script/basic/JDK-8019553.js +++ b/test/script/basic/JDK-8019553.js @@ -33,7 +33,7 @@ function check(str) { eval(str); fail("SyntaxError expected for: " + str); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } } diff --git a/test/script/basic/JDK-8019553.js.EXPECTED b/test/script/basic/JDK-8019553.js.EXPECTED index 78d22bea..38e52866 100644 --- a/test/script/basic/JDK-8019553.js.EXPECTED +++ b/test/script/basic/JDK-8019553.js.EXPECTED @@ -1,12 +1,12 @@ -SyntaxError: test/script/basic/JDK-8019553.js#33<eval>:1:3 Expected l-value but found + +SyntaxError:33:Expected l-value but found + ++ +3 ^ -SyntaxError: test/script/basic/JDK-8019553.js#33<eval>:1:3 Expected l-value but found - +SyntaxError:33:Expected l-value but found - ++ -7 ^ -SyntaxError: test/script/basic/JDK-8019553.js#33<eval>:1:3 Expected l-value but found + +SyntaxError:33:Expected l-value but found + -- +2 ^ -SyntaxError: test/script/basic/JDK-8019553.js#33<eval>:1:3 Expected l-value but found - +SyntaxError:33:Expected l-value but found - -- -8 ^ diff --git a/test/script/basic/JDK-8019791.js b/test/script/basic/JDK-8019791.js index 75e92819..ad32ce3c 100644 --- a/test/script/basic/JDK-8019791.js +++ b/test/script/basic/JDK-8019791.js @@ -33,7 +33,7 @@ try { eval('"" ~ ""'); print("FAILED: SyntaxError expected for: \"\" ~ \"\""); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } // Used to crash instead of SyntaxError @@ -41,7 +41,7 @@ try { eval("function() { if (1~0) return 0; return 1 }"); print("FAILED: SyntaxError expected for: if (1~0) "); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } // The following are valid, but used to crash diff --git a/test/script/basic/JDK-8019791.js.EXPECTED b/test/script/basic/JDK-8019791.js.EXPECTED index 5aec5909..b214b2c6 100644 --- a/test/script/basic/JDK-8019791.js.EXPECTED +++ b/test/script/basic/JDK-8019791.js.EXPECTED @@ -1,6 +1,6 @@ -SyntaxError: test/script/basic/JDK-8019791.js#33<eval>:1:3 Expected ; but found ~ +SyntaxError:33:Expected ; but found ~ "" ~ "" ^ -SyntaxError: test/script/basic/JDK-8019791.js#41<eval>:1:18 Expected ) but found ~ +SyntaxError:41:Expected ) but found ~ function() { if (1~0) return 0; return 1 } ^ diff --git a/test/script/basic/JDK-8019805.js b/test/script/basic/JDK-8019805.js index 70371fb8..7e3e25b5 100644 --- a/test/script/basic/JDK-8019805.js +++ b/test/script/basic/JDK-8019805.js @@ -32,5 +32,5 @@ try { eval("for each(var v=0;false;);"); print("FAILED: for each(var v=0; false;); should have thrown error"); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } diff --git a/test/script/basic/JDK-8019805.js.EXPECTED b/test/script/basic/JDK-8019805.js.EXPECTED index 154c3326..76d335e4 100644 --- a/test/script/basic/JDK-8019805.js.EXPECTED +++ b/test/script/basic/JDK-8019805.js.EXPECTED @@ -1,3 +1,3 @@ -SyntaxError: test/script/basic/JDK-8019805.js#32<eval>:1:16 for each can only be used with for..in +SyntaxError:32:for each can only be used with for..in for each(var v=0;false;); ^ diff --git a/test/script/basic/JDK-8023026.js b/test/script/basic/JDK-8023026.js index 9080ec8c..0a6f0272 100644 --- a/test/script/basic/JDK-8023026.js +++ b/test/script/basic/JDK-8023026.js @@ -48,7 +48,7 @@ function checkIterations(obj) { function(x) x*x)); } -var array = new (Java.type("[I"))(4); +var array = new (Java.type("int[]"))(4); for (var i in array) { array[i] = i; } diff --git a/test/script/basic/JDK-8024847.js b/test/script/basic/JDK-8024847.js index 4a671ecd..2ead01fd 100644 --- a/test/script/basic/JDK-8024847.js +++ b/test/script/basic/JDK-8024847.js @@ -100,3 +100,9 @@ var obj = new JSObject() { var jlist = Java.to(obj, java.util.List); print(jlist instanceof java.util.List); print(jlist); + +var obj = new JSObject() { + toNumber: function() { return 42; } +}; + +print(32 + obj); diff --git a/test/script/basic/JDK-8024847.js.EXPECTED b/test/script/basic/JDK-8024847.js.EXPECTED index 015183a0..16bff3e2 100644 --- a/test/script/basic/JDK-8024847.js.EXPECTED +++ b/test/script/basic/JDK-8024847.js.EXPECTED @@ -10,3 +10,4 @@ true [hello, world] true [nashorn, js] +74 diff --git a/test/script/basic/JDK-8025213.js b/test/script/basic/JDK-8025213.js new file mode 100644 index 00000000..062a389e --- /dev/null +++ b/test/script/basic/JDK-8025213.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8025213: Assignment marks variable as defined too early + * + * @test + * @run + */ + +function test() { + if (String("")) { + var foo = 42; + } + foo = foo + 1; + print(foo); +} + +test(); diff --git a/test/script/basic/JDK-8025213.js.EXPECTED b/test/script/basic/JDK-8025213.js.EXPECTED new file mode 100644 index 00000000..736991a1 --- /dev/null +++ b/test/script/basic/JDK-8025213.js.EXPECTED @@ -0,0 +1 @@ +NaN diff --git a/test/script/basic/JDK-8025488.js b/test/script/basic/JDK-8025488.js new file mode 100644 index 00000000..8229b9fa --- /dev/null +++ b/test/script/basic/JDK-8025488.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8025488: Error.captureStackTrace should not format error stack + * + * @test + * @run + */ + + +function MyError () { + Error.call(this); + Error.captureStackTrace(this); + this.arr = {}; +}; + +MyError.prototype.toString = function() { + return this.arr.toString(); +} + +var e = new MyError(); +print(e.stack.replace(/\\/g, '/')); diff --git a/test/script/basic/JDK-8025488.js.EXPECTED b/test/script/basic/JDK-8025488.js.EXPECTED new file mode 100644 index 00000000..40b244a9 --- /dev/null +++ b/test/script/basic/JDK-8025488.js.EXPECTED @@ -0,0 +1,3 @@ +[object Object] + at MyError (test/script/basic/JDK-8025488.js:34) + at <program> (test/script/basic/JDK-8025488.js:42) diff --git a/test/script/basic/JDK-8025515.js b/test/script/basic/JDK-8025515.js new file mode 100644 index 00000000..f3d7cee4 --- /dev/null +++ b/test/script/basic/JDK-8025515.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8025515: Performance issues with Source.getLine() + * + * @test + * @run + */ + +// Make sure synthetic names of anonymous functions have correct line numbers + +function testMethodName(f, expected) { + try { + f(); + fail("expected error"); + } catch (e) { + var stack = e.getStackTrace(); + if (stack[0].methodName !== expected) { + fail("got " + stack[0].methodName + ", expected " + expected); + } + } +} + +testMethodName(function() { + return a.b.c; +}, "_L45"); + +testMethodName(function() { throw new Error() }, "_L49"); + +var f = (function() { + return function() { a.b.c; }; +})(); +testMethodName(f, "_L51$_L52"); + +testMethodName((function() { + return function() { return a.b.c; }; +})(), "_L56$_L57"); diff --git a/test/script/basic/JDK-8025520.js b/test/script/basic/JDK-8025520.js new file mode 100644 index 00000000..773a30ab --- /dev/null +++ b/test/script/basic/JDK-8025520.js @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8025520: Array.prototype.slice should only copy defined elements + * + * @test + * @run + */ + +var s = Array.prototype.slice.call({length: 6, 3: 1}, 2, 5); + +if (s.length != 3) { + fail("s.length != 3"); +} +if (0 in s) { + fail("0 in s"); +} +if (s.hasOwnProperty(0)) { + fail("s.hasOwnProperty(0)"); +} +if (s[1] !== 1) { + fail("s[1] !== 1"); +} +if (2 in s) { + fail("2 in s"); +} +if (s.hasOwnProperty(2)) { + fail("s.hasOwnProperty(2)"); +} diff --git a/test/script/basic/JDK-8025589.js b/test/script/basic/JDK-8025589.js new file mode 100644 index 00000000..a74261c1 --- /dev/null +++ b/test/script/basic/JDK-8025589.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8025589: Array.prototype.shift should only copy defined elements in generic mode + * + * @test + * @run + */ + +var s = {length: 4, 2: 1}; +Array.prototype.shift.call(s); + +if (s.length != 3) { + fail("s.length != 3"); +} +if (0 in s) { + fail("0 in s"); +} +if (s.hasOwnProperty(0)) { + fail("s.hasOwnProperty(0)"); +} +if (s[1] !== 1) { + fail("s[1] !== 1"); +} +if (2 in s) { + fail("2 in s"); +} +if (s.hasOwnProperty(2)) { + fail("s.hasOwnProperty(2)"); +} diff --git a/test/script/basic/JDK-8026008.js b/test/script/basic/JDK-8026008.js new file mode 100644 index 00000000..20c1d5f0 --- /dev/null +++ b/test/script/basic/JDK-8026008.js @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026008: Constant folding removes var statement + * + * @test + * @run + */ + +if (false) { + var x1 = 10; + if (false) { + var x2; + } +} else { + print(x1, x2); +} + +if (undefined) { + var z1; + if (null) { + var z2; + } +} + +print(z1, z2); + +if (1) { + print(y1, y2); +} else if (0) { + var y1 = 1; +} else { + var y2 = 2 +} diff --git a/test/script/basic/JDK-8026008.js.EXPECTED b/test/script/basic/JDK-8026008.js.EXPECTED new file mode 100644 index 00000000..6bbb6d93 --- /dev/null +++ b/test/script/basic/JDK-8026008.js.EXPECTED @@ -0,0 +1,3 @@ +undefined undefined +undefined undefined +undefined undefined diff --git a/test/script/basic/JDK-8026016.js b/test/script/basic/JDK-8026016.js new file mode 100644 index 00000000..43f268d8 --- /dev/null +++ b/test/script/basic/JDK-8026016.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026016: too many relinks dominate avatar.js http benchmark + * + * @test + * @run + */ + +function accessMegamorphic() { + for (var i = 0; i < 26; i++) { + var o = {}; + o[String.fromCharCode(i + 97)] = 1; + o._; + } +} + +function invokeMegamorphic() { + for (var i = 0; i < 26; i++) { + var o = {}; + o[String.fromCharCode(i + 97)] = 1; + try { + o._(i); + } catch (e) { + print(e); + } + } +} + +Object.prototype.__noSuchProperty__ = function() { + print("no such property", Array.prototype.slice.call(arguments)); +}; + +invokeMegamorphic(); +accessMegamorphic(); + +Object.prototype.__noSuchMethod__ = function() { + print("no such method", Array.prototype.slice.call(arguments)); +}; + +invokeMegamorphic(); +accessMegamorphic(); + +Object.prototype.__noSuchMethod__ = "nofunction"; + +invokeMegamorphic(); +accessMegamorphic(); diff --git a/test/script/basic/JDK-8026016.js.EXPECTED b/test/script/basic/JDK-8026016.js.EXPECTED new file mode 100644 index 00000000..a2d23834 --- /dev/null +++ b/test/script/basic/JDK-8026016.js.EXPECTED @@ -0,0 +1,182 @@ +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +TypeError: Cannot call undefined +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such method _,0 +no such method _,1 +no such method _,2 +no such method _,3 +no such method _,4 +no such method _,5 +no such method _,6 +no such method _,7 +no such method _,8 +no such method _,9 +no such method _,10 +no such method _,11 +no such method _,12 +no such method _,13 +no such method _,14 +no such method _,15 +no such method _,16 +no such method _,17 +no such method _,18 +no such method _,19 +no such method _,20 +no such method _,21 +no such method _,22 +no such method _,23 +no such method _,24 +no such method _,25 +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +TypeError: Cannot call undefined +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ +no such property _ diff --git a/test/script/basic/JDK-8026033.js b/test/script/basic/JDK-8026033.js new file mode 100644 index 00000000..4a5d8594 --- /dev/null +++ b/test/script/basic/JDK-8026033.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026033: Switch should load expression even when there are no cases in it + * + * @test + * @run + */ + +try { + (function() { switch(x) {} })(); + fail("Should have thrown ReferenceError"); +} catch (e) { + if (! (e instanceof ReferenceError)) { + fail("ReferenceError expected, got " + e); + } + print(e); +} diff --git a/test/script/basic/JDK-8026033.js.EXPECTED b/test/script/basic/JDK-8026033.js.EXPECTED new file mode 100644 index 00000000..c007ed7b --- /dev/null +++ b/test/script/basic/JDK-8026033.js.EXPECTED @@ -0,0 +1 @@ +ReferenceError: "x" is not defined diff --git a/test/script/basic/JDK-8026042.js b/test/script/basic/JDK-8026042.js new file mode 100644 index 00000000..a853626f --- /dev/null +++ b/test/script/basic/JDK-8026042.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026042: FoldConstants need to guard against ArrayLiteralNode + * + * @test + * @run + */ + +try { + if ([a]) { + print("fail"); + } +} catch (e) { + print(e); +} + +try { + [a] ? print(1) : print(2); +} catch (e) { + print(e); +} diff --git a/test/script/basic/JDK-8026042.js.EXPECTED b/test/script/basic/JDK-8026042.js.EXPECTED new file mode 100644 index 00000000..aae23d09 --- /dev/null +++ b/test/script/basic/JDK-8026042.js.EXPECTED @@ -0,0 +1,2 @@ +ReferenceError: "a" is not defined +ReferenceError: "a" is not defined diff --git a/test/script/basic/JDK-8026048.js b/test/script/basic/JDK-8026048.js new file mode 100644 index 00000000..33789fea --- /dev/null +++ b/test/script/basic/JDK-8026048.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026048: Function constructor should convert arguments to String before performing any syntax checks + * + * @test + * @run + */ + +try { + Function("-", {toString:function(){throw "err"}}) +} catch (e) { + if (e !== "err") { + fail("err xpected, got " + e); + } +} diff --git a/test/script/basic/JDK-8026112.js b/test/script/basic/JDK-8026112.js new file mode 100644 index 00000000..0e869f70 --- /dev/null +++ b/test/script/basic/JDK-8026112.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026112: Function("with(x ? 1e81 : (x2.constructor = 0.1)){}") throws AssertionError: double is not compatible with object + * + * @test + * @run + */ + +Function("with(x ? 1e81 : (x2.constructor = 0.1)){}") diff --git a/test/script/basic/JDK-8026125.js b/test/script/basic/JDK-8026125.js new file mode 100644 index 00000000..bb3a43d8 --- /dev/null +++ b/test/script/basic/JDK-8026125.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026125: Array.prototype.slice.call(Java.type("java.util.HashMap")) throws ClassCastException: jdk.internal.dynalink.beans.StaticClass cannot be cast to jdk.nashorn.internal.runtime.ScriptObject + * + * @test + * @run + */ + +Array.prototype.splice.call(Java.type("java.util.HashMap")) +Array.prototype.slice.call(Java.type("java.util.HashMap")) diff --git a/test/script/basic/JDK-8026137.js b/test/script/basic/JDK-8026137.js new file mode 100644 index 00000000..4a19643f --- /dev/null +++ b/test/script/basic/JDK-8026137.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026137: Binary evaluation order in JavaScript is load load + * convert convert, not load convert load convert. + * + * @test + * @run + */ + +try { + (function f() { Object.defineProperty({},"x",{get: function(){return {valueOf:function(){throw 0}}}}).x - Object.defineProperty({},"x",{get: function(){throw 1}}).x })() +} +catch (e) { + print(e); +} + +try { + ({valueOf: function(){throw 0}}) - ({valueOf: function(){throw 1}} - 1) +} catch (e) { + print(e); +} + diff --git a/test/script/basic/JDK-8026137.js.EXPECTED b/test/script/basic/JDK-8026137.js.EXPECTED new file mode 100644 index 00000000..6ed281c7 --- /dev/null +++ b/test/script/basic/JDK-8026137.js.EXPECTED @@ -0,0 +1,2 @@ +1 +1 diff --git a/test/script/basic/JDK-8026161.js b/test/script/basic/JDK-8026161.js new file mode 100644 index 00000000..49f888b7 --- /dev/null +++ b/test/script/basic/JDK-8026161.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026161: Don't narrow floating-point literals in the lexer + * + * @test + * @run + */ + +print(new java.awt.Color(1, 1, 1)) // creates Color[r=1,g=1,b=1] +print(new java.awt.Color(1.0, 1.0, 1.0)) // Color[r=255,g=255,b=255] diff --git a/test/script/basic/JDK-8026161.js.EXPECTED b/test/script/basic/JDK-8026161.js.EXPECTED new file mode 100644 index 00000000..c7b00b08 --- /dev/null +++ b/test/script/basic/JDK-8026161.js.EXPECTED @@ -0,0 +1,2 @@ +java.awt.Color[r=1,g=1,b=1] +java.awt.Color[r=255,g=255,b=255] diff --git a/test/script/basic/JDK-8026162.js b/test/script/basic/JDK-8026162.js new file mode 100644 index 00000000..ccd6ee71 --- /dev/null +++ b/test/script/basic/JDK-8026162.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026162: "this" in SAM adapter functions is wrong + * + * @test + * @run + */ + +var global = this; + +new java.lang.Runnable( + function func() { + if (this !== global) { + fail("Expected 'this' to be global instance"); + } + } +).run(); + +new java.lang.Runnable( + function func() { + 'use strict'; + if (typeof this != 'undefined') { + fail("Expected 'undefined' to be global instance"); + } + } +).run(); diff --git a/test/script/basic/JDK-8026167.js b/test/script/basic/JDK-8026167.js new file mode 100644 index 00000000..4959e62a --- /dev/null +++ b/test/script/basic/JDK-8026167.js @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026167: Class cache/reuse of 'eval' scripts results in ClassCastException in some cases. + * + * @test + * @run + */ + +var m = new javax.script.ScriptEngineManager(); +var e = m.getEngineByName('js'); + +// leave the whitespace - need both eval("e") at same column for this test! + +e.eval('function f(e) { eval("e") } f()'); +e.eval('function f() { var e = 33; eval("e") } f()'); + +function f() { + Function.call.call(function x() { eval("x") }); eval("x") +} + +try { + f(); + fail("Should have thrown ReferenceError"); +} catch (e) { + if (! (e instanceof ReferenceError)) { + fail("ReferenceError expected but got " + e); + } +} diff --git a/test/script/basic/JDK-8026248.js b/test/script/basic/JDK-8026248.js new file mode 100644 index 00000000..e54d3383 --- /dev/null +++ b/test/script/basic/JDK-8026248.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026248: importClass has to be a varargs function + * + * @test + * @run + */ + +load('nashorn:mozilla_compat.js') + +importClass(java.io.File, java.io.InputStream) + +print(File) +print(InputStream) + +importClass(java.util.Map, java.util.HashMap, java.io.PrintStream) + +print(HashMap) +print(Map) +print(PrintStream) + +importClass.call(this, java.util.Collections, java.util.List); +print(Collections) +print(List) + +importClass.apply(this, [ java.util.Queue, java.math.BigInteger, java.math.BigDecimal ]); +print(Queue) +print(BigInteger) +print(BigDecimal) diff --git a/test/script/basic/JDK-8026248.js.EXPECTED b/test/script/basic/JDK-8026248.js.EXPECTED new file mode 100644 index 00000000..361b8e17 --- /dev/null +++ b/test/script/basic/JDK-8026248.js.EXPECTED @@ -0,0 +1,10 @@ +[JavaClass java.io.File] +[JavaClass java.io.InputStream] +[JavaClass java.util.HashMap] +[JavaClass java.util.Map] +[JavaClass java.io.PrintStream] +[JavaClass java.util.Collections] +[JavaClass java.util.List] +[JavaClass java.util.Queue] +[JavaClass java.math.BigInteger] +[JavaClass java.math.BigDecimal] diff --git a/test/script/basic/JDK-8026264.js b/test/script/basic/JDK-8026264.js new file mode 100644 index 00000000..b4de01d2 --- /dev/null +++ b/test/script/basic/JDK-8026264.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026264: Getter, setter function name mangling issues + * + * @test + * @run + */ + +var obj = { + get ":"(){}, + set ":"(x){}, + get ""(){}, + set ""(x){} +}; + +var desc = Object.getOwnPropertyDescriptor(obj, ":"); +if (desc.get.name != ':') { + fail("getter name is expected to be ':' got " + desc.get.name); +} + +if (desc.set.name != ':') { + fail("setter name is expected to be ':' got " + desc.set.name); +} + +desc = Object.getOwnPropertyDescriptor(obj, ""); +if (desc.get.name != '') { + fail("getter name is expected to be '' got " + desc.get.name); +} + +if (desc.set.name != '') { + fail("setter name is expected to be '' got " + desc.set.name); +} diff --git a/test/script/basic/JDK-8026292.js b/test/script/basic/JDK-8026292.js new file mode 100644 index 00000000..740895f2 --- /dev/null +++ b/test/script/basic/JDK-8026292.js @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026292: Megamorphic setter fails with boolean value + * + * @test + * @run + */ + +function megamorphic(o) { + o.w = true; + if (!o.w) + throw new Error(); +} + +// Calls below must exceed megamorphic callsite threshhold +for (var i = 0; i < 10; i++) { + megamorphic({a: 1}); + megamorphic({b: 1}); + megamorphic({c: 1}); + megamorphic({d: 1}); + megamorphic({e: 1}); + megamorphic({f: 1}); + megamorphic({g: 1}); + megamorphic({h: 1}); + megamorphic({i: 1}); + megamorphic({j: 1}); + megamorphic({k: 1}); + megamorphic({l: 1}); + megamorphic({m: 1}); + megamorphic({n: 1}); + megamorphic({o: 1}); + megamorphic({p: 1}); + megamorphic({q: 1}); + megamorphic({r: 1}); + megamorphic({s: 1}); + megamorphic({t: 1}); + megamorphic({u: 1}); + megamorphic({v: 1}); + megamorphic({w: 1}); + megamorphic({x: 1}); + megamorphic({y: 1}); + megamorphic({z: 1}); +} diff --git a/test/script/basic/JDK-8026302.js b/test/script/basic/JDK-8026302.js new file mode 100644 index 00000000..786cc118 --- /dev/null +++ b/test/script/basic/JDK-8026302.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026302: source representation of getter and setter methods is wrong + * + * @test + * @run + */ + +var obj = { + get "foo"() {}, + set "foo"(x) {}, + get bar() {}, + set bar(x) {}, + get ":"() {}, + set ":"(x) {}, + get 12() {}, + set 12(x) {}, + get 1.8e-8() {}, + set 1.8e-8(x) {} +} + +for (var prop in obj) { + var desc = Object.getOwnPropertyDescriptor(obj, prop); + print(desc.get); + print(desc.set); +} diff --git a/test/script/basic/JDK-8026302.js.EXPECTED b/test/script/basic/JDK-8026302.js.EXPECTED new file mode 100644 index 00000000..232edc00 --- /dev/null +++ b/test/script/basic/JDK-8026302.js.EXPECTED @@ -0,0 +1,10 @@ +get "foo"() {} +set "foo"(x) {} +get bar() {} +set bar(x) {} +get ":"() {} +set ":"(x) {} +get 12() {} +set 12(x) {} +get 1.8e-8() {} +set 1.8e-8(x) {} diff --git a/test/script/basic/JDK-8026317.js b/test/script/basic/JDK-8026317.js new file mode 100644 index 00000000..57bea9cb --- /dev/null +++ b/test/script/basic/JDK-8026317.js @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026317: $ in the function name results in wrong function being invoked + * + * @test + * @run + */ + +function case1() { + function g() { + return 0 + } + function g() { + return 1 + } + function g$1() { + return 2 + } + + return g$1() +} + +print(case1()); + +function case2() { + function g() { + return 0 + } + + var h = function g() { + return 1 + } + + function g$1() { + return 2 + } + + return h() +} + +print(case2()); diff --git a/test/script/basic/JDK-8026317.js.EXPECTED b/test/script/basic/JDK-8026317.js.EXPECTED new file mode 100644 index 00000000..5f1d0ece --- /dev/null +++ b/test/script/basic/JDK-8026317.js.EXPECTED @@ -0,0 +1,2 @@ +2 +1 diff --git a/test/script/basic/JDK-8026367.js b/test/script/basic/JDK-8026367.js new file mode 100644 index 00000000..c5e12358 --- /dev/null +++ b/test/script/basic/JDK-8026367.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026367: Add a sync keyword to mozilla_compat + * + * @test + * @run + */ + +if (typeof sync === "undefined") { + load("nashorn:mozilla_compat.js"); +} + +var obj = { + count: 0, + // Sync called with one argument will synchronize on this-object of invocation + inc: sync(function(d) { + this.count += d; + }), + // Pass explicit object to synchronize on as second argument + dec: sync(function(d) { + this.count -= d; + }, obj) +}; + +var t1 = new java.lang.Thread(function() { + for (var i = 0; i < 100000; i++) obj.inc(1); +}); +var t2 = new java.lang.Thread(function() { + for (var i = 0; i < 100000; i++) obj.dec(1); +}); + +t1.start(); +t2.start(); +t1.join(); +t2.join(); + +if (obj.count !== 0) { + throw new Error("Expected count == 0, got " + obj.count); +} diff --git a/test/script/basic/JDK-8026692.js b/test/script/basic/JDK-8026692.js new file mode 100644 index 00000000..f9f0f38c --- /dev/null +++ b/test/script/basic/JDK-8026692.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026692: eval() throws NullPointerException with --compile-only + * + * @test + * @option --compile-only + * @run + */ + +eval(""); diff --git a/test/script/basic/JDK-8026693.js b/test/script/basic/JDK-8026693.js new file mode 100644 index 00000000..fd1dd5fa --- /dev/null +++ b/test/script/basic/JDK-8026693.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026693: getType() called on DISCARD node + * + * @test + * @run + */ + +function f() { + if(x, y) z; +} diff --git a/test/script/basic/JDK-8026701.js b/test/script/basic/JDK-8026701.js new file mode 100644 index 00000000..90a1739c --- /dev/null +++ b/test/script/basic/JDK-8026701.js @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026701: Array.prototype.splice is slow on dense arrays + * + * @test + * @run + */ + +function testSplice(arr, e1, e2, e3) { + try { + print(arr); + print(arr.splice(3, 0, e1, e2, e3)); + print(arr); + print(arr.splice(2, 3)); + print(arr); + print(arr.splice(2, 3, arr[2], arr[3], arr[4])); + print(arr); + print(arr.splice(20, 10)); + print(arr); + print(arr.splice(arr.length, 0, e1, e2, e3)); + print(arr); + print(arr.splice(0, 2, arr[0], arr[1], arr[2], arr[3])); + print(arr); + } catch (error) { + print(error); + } +} + +function convert(array, type) { + return (typeof Java === "undefined") ? array : Java.from(Java.to(array, type)); +} + +// run some splice tests on all dense array implementations +testSplice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], -1, -2, -3); +testSplice(convert([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "long[]"), -1, -2, -3); +testSplice(convert([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "double[]"), -1, -2, -3); +testSplice(["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], -1, -2, -3); + +// test array conversion during splice +testSplice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], -1, "-2", "-3"); +testSplice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], -1, -2.5, -3.5); +testSplice(convert([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "long[]"), -1, "-2", "-3"); +testSplice(convert([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "long[]"), -1, -2.5, -3.5); +testSplice(convert([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "double[]"), -1, "-2", "-3"); + +// test combination with defined elements +testSplice(Object.defineProperty([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, {value: 13}), -1, -2, -3); +testSplice(Object.defineProperty([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, {value: 13, writable: false}), -1, -2, -3); +testSplice(Object.defineProperty([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, {value: 13, configurable: false}), -1, -2, -3); +testSplice(Object.defineProperty([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, {value: 13, writable: false, configurable: false}), -1, -2, -3); diff --git a/test/script/basic/JDK-8026701.js.EXPECTED b/test/script/basic/JDK-8026701.js.EXPECTED new file mode 100644 index 00000000..b70bbe25 --- /dev/null +++ b/test/script/basic/JDK-8026701.js.EXPECTED @@ -0,0 +1,147 @@ +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,6,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,6,7,8,9,10 +-3,4,5 +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,6,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,6,7,8,9,10 +-3,4,5 +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,6,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,6,7,8,9,10 +-3,4,5 +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,6,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,6,7,8,9,10 +-3,4,5 +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,6,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,6,7,8,9,10 +-3,4,5 +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2.5,-3.5,4,5,6,7,8,9,10 +3,-1,-2.5 +1,2,-3.5,4,5,6,7,8,9,10 +-3.5,4,5 +1,2,-3.5,4,5,6,7,8,9,10 + +1,2,-3.5,4,5,6,7,8,9,10 + +1,2,-3.5,4,5,6,7,8,9,10,-1,-2.5,-3.5 +1,2 +1,2,-3.5,4,-3.5,4,5,6,7,8,9,10,-1,-2.5,-3.5 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,6,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,6,7,8,9,10 +-3,4,5 +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2.5,-3.5,4,5,6,7,8,9,10 +3,-1,-2.5 +1,2,-3.5,4,5,6,7,8,9,10 +-3.5,4,5 +1,2,-3.5,4,5,6,7,8,9,10 + +1,2,-3.5,4,5,6,7,8,9,10 + +1,2,-3.5,4,5,6,7,8,9,10,-1,-2.5,-3.5 +1,2 +1,2,-3.5,4,-3.5,4,5,6,7,8,9,10,-1,-2.5,-3.5 +1,2,3,4,5,6,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,6,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,6,7,8,9,10 +-3,4,5 +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10 + +1,2,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,6,7,8,9,10,-1,-2,-3 +1,2,3,4,5,13,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,13,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,13,7,8,9,10 +-3,4,5 +1,2,-3,4,5,13,7,8,9,10 + +1,2,-3,4,5,13,7,8,9,10 + +1,2,-3,4,5,13,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,13,7,8,9,10,-1,-2,-3 +1,2,3,4,5,13,7,8,9,10 +TypeError: "5" is not a writable property of [object Array] +1,2,3,4,5,13,7,8,9,10 + +1,2,3,-1,-2,-3,4,5,13,7,8,9,10 +3,-1,-2 +1,2,-3,4,5,13,7,8,9,10 +-3,4,5 +1,2,-3,4,5,13,7,8,9,10 + +1,2,-3,4,5,13,7,8,9,10 + +1,2,-3,4,5,13,7,8,9,10,-1,-2,-3 +1,2 +1,2,-3,4,-3,4,5,13,7,8,9,10,-1,-2,-3 +1,2,3,4,5,13,7,8,9,10 +TypeError: "5" is not a writable property of [object Array] diff --git a/test/script/basic/JDK-8026805.js b/test/script/basic/JDK-8026805.js new file mode 100644 index 00000000..0133a0b6 --- /dev/null +++ b/test/script/basic/JDK-8026805.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026805: Array.prototype.length doesn't work as expected + * + * @test + * @run + */ + +if (Array.prototype.length !== 0) { + throw new Error("Initial length not 0"); +} + +Array.prototype[3] = 1; + +if (Array.prototype.length !== 4) { + throw new Error("length not updated to 4"); +} + +Array.prototype.length = 0; + +if (Array.prototype.length !== 0) { + throw new Error("length not reset to 0"); +} + +if (3 in Array.prototype) { + throw new Error("array element not deleted"); +} diff --git a/test/script/basic/JDK-8026858.js b/test/script/basic/JDK-8026858.js new file mode 100644 index 00000000..92562ac1 --- /dev/null +++ b/test/script/basic/JDK-8026858.js @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026858: Array length does not handle defined properties correctly + * + * @test + * @run + */ + +var arr = []; + +Object.defineProperty(arr, "3", {value: 1 /* configurable: false */}); + +if (arr[3] != 1) { + throw new Error("arr[3] not defined"); +} + +if (arr.length !== 4) { + throw new Error("Array length not updated to 4"); +} + +Object.defineProperty(arr, "5", {value: 1, configurable: true}); + +if (arr[5] != 1) { + throw new Error("arr[5] not defined"); +} + +if (arr.length !== 6) { + throw new Error("Array length not updated to 4"); +} + +arr.length = 0; + +if (5 in arr) { + throw new Error("configurable element was not deleted"); +} + +if (arr[3] != 1) { + throw new Error("non-configurable element was deleted"); +} + +if (arr.length !== 4) { + throw new Error("Array length not set"); +} + diff --git a/test/script/basic/JDK-8026955.js b/test/script/basic/JDK-8026955.js new file mode 100644 index 00000000..a54e3d96 --- /dev/null +++ b/test/script/basic/JDK-8026955.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026955: for-in should convert primitive values to object + * + * @test + * @run + */ + +Object.prototype[4] = "world"; +String.prototype[3] = "hello"; +Number.prototype[3] = "hello"; +Boolean.prototype[3] = "hello"; + +function testForIn(x) { + for (var i in x) { + print(i, x[i]); + } + for each (var i in x) { + print(i); + } +} + +testForIn("abc"); +testForIn(false); +testForIn(3); +testForIn(null); +testForIn(); +testForIn(String.prototype); + diff --git a/test/script/basic/JDK-8026955.js.EXPECTED b/test/script/basic/JDK-8026955.js.EXPECTED new file mode 100644 index 00000000..3852d5ab --- /dev/null +++ b/test/script/basic/JDK-8026955.js.EXPECTED @@ -0,0 +1,22 @@ +0 a +1 b +2 c +3 hello +4 world +a +b +c +hello +world +3 hello +4 world +hello +world +3 hello +4 world +hello +world +3 hello +4 world +hello +world diff --git a/test/script/basic/JDK-8027016.js b/test/script/basic/JDK-8027016.js new file mode 100644 index 00000000..f84050fb --- /dev/null +++ b/test/script/basic/JDK-8027016.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027016: Array.prototype.indexOf should return -1 when array is of length zero + * + * @test + * @run + */ + +var res = [].indexOf(null, {valueOf:function(){throw "not reached"}}); +if (res != -1) { + fail("expected -1 on indexOf on empty array"); +} + +// add index beyond length check as well + +res = [].indexOf(null, 1); +if (res != -1) { + fail("expected -1 on indexOf on empty array"); +} + diff --git a/test/script/basic/JDK-8027024.js b/test/script/basic/JDK-8027024.js new file mode 100644 index 00000000..890c6917 --- /dev/null +++ b/test/script/basic/JDK-8027024.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027024: String.prototype.charAt and charCodeAt do not evaluate 'self' and 'pos' arguments in right order + * + * @test + * @run + */ + + +String.prototype.charAt.call( + { + toString: function() { + print("charAt.self.toString"); + } + }, + + { + valueOf: function() { + print("charAt.pos.valueOf"); + } + } +); + +String.prototype.charCodeAt.call( + { + toString: function() { + print("charCodeAt.self.toString"); + } + }, + + { + valueOf: function() { + print("charCodeAt.pos.valueOf"); + } + } +); diff --git a/test/script/basic/JDK-8027024.js.EXPECTED b/test/script/basic/JDK-8027024.js.EXPECTED new file mode 100644 index 00000000..9793e73a --- /dev/null +++ b/test/script/basic/JDK-8027024.js.EXPECTED @@ -0,0 +1,4 @@ +charAt.self.toString +charAt.pos.valueOf +charCodeAt.self.toString +charCodeAt.pos.valueOf diff --git a/test/script/basic/JDK-8027042.js b/test/script/basic/JDK-8027042.js new file mode 100644 index 00000000..eea6373b --- /dev/null +++ b/test/script/basic/JDK-8027042.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027042: Evaluation order for binary operators can be improved + * + * @test + * @run + */ + +// var with getter side effect +Object.defineProperty(this, "a", { get: function() {print("get a"); return 1; }}); + +// var with both getter and conversion side effect +Object.defineProperty(this, "b", { get: function() {print("get b"); return {valueOf: function() { print("conv b"); return 10; }}; }}); + +(function() { + // var with toPrimitive conversion side effect + var c = {valueOf: function() { print("conv c"); return 100; }}; + + print(b + (c + a)); + print(b + (c + b)); + print(b + (a + b)); + print(b + (b + c)); + print(b + (b + c)); + print(b + (c + (a - b))); + print(b + (c + (c - b))); + print(b + (c + (b - c))); + print(b + (b + (a ? 2 : 3))); + print(b + (b + (b ? 2 : 3))); + print(b + (b + (c ? 2 : 3))); + print(b + ((-c) + (-a))); + print(b + ((-c) + (-b))); + print(b + ((-c) + (-c))); + try { print(b + new a); } catch (e) {} + try { print(b + new b); } catch (e) {} + try { print(b + new c); } catch (e) {} +})(); diff --git a/test/script/basic/JDK-8027042.js.EXPECTED b/test/script/basic/JDK-8027042.js.EXPECTED new file mode 100644 index 00000000..25b34e61 --- /dev/null +++ b/test/script/basic/JDK-8027042.js.EXPECTED @@ -0,0 +1,88 @@ +get b +get a +conv c +conv b +111 +get b +get b +conv c +conv b +conv b +120 +get b +get a +get b +conv b +conv b +21 +get b +get b +conv b +conv c +conv b +120 +get b +get b +conv b +conv c +conv b +120 +get b +get a +get b +conv b +conv c +conv b +101 +get b +get b +conv c +conv b +conv c +conv b +200 +get b +get b +conv b +conv c +conv c +conv b +20 +get b +get b +get a +conv b +conv b +22 +get b +get b +get b +conv b +conv b +22 +get b +get b +conv b +conv b +22 +get b +conv c +get a +conv b +-91 +get b +conv c +get b +conv b +conv b +-100 +get b +conv c +conv c +conv b +-190 +get b +get a +get b +get b +get b diff --git a/test/script/basic/JDK-8027236.js b/test/script/basic/JDK-8027236.js new file mode 100644 index 00000000..02f9e8d8 --- /dev/null +++ b/test/script/basic/JDK-8027236.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027236: Ensure ScriptObject and ConsString aren't visible to Java + * + * @test + * @run + */ + +// Check that ConsString is flattened +var m = new java.util.HashMap() +var x = "f" +x += "oo" +m.put(x, "bar") +print(m.get("foo")) +// Note: many more tests are run by the JavaExportImportTest TestNG class. diff --git a/test/script/basic/JDK-8027236.js.EXPECTED b/test/script/basic/JDK-8027236.js.EXPECTED new file mode 100644 index 00000000..5716ca59 --- /dev/null +++ b/test/script/basic/JDK-8027236.js.EXPECTED @@ -0,0 +1 @@ +bar diff --git a/test/script/basic/JDK-8027562.js b/test/script/basic/JDK-8027562.js new file mode 100644 index 00000000..950584b4 --- /dev/null +++ b/test/script/basic/JDK-8027562.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027562: eval should load second and subsequent arguments for side effect + * + * @test + * @run + */ + +try { + eval("", x); + fail("should have thrown ReferenceError for 'x'"); +} catch (e) { + if (! (e instanceof ReferenceError)) { + fail("Expected ReferenceError, got " + e); + } + print(e); +} diff --git a/test/script/basic/JDK-8027562.js.EXPECTED b/test/script/basic/JDK-8027562.js.EXPECTED new file mode 100644 index 00000000..c007ed7b --- /dev/null +++ b/test/script/basic/JDK-8027562.js.EXPECTED @@ -0,0 +1 @@ +ReferenceError: "x" is not defined diff --git a/test/script/basic/JDK-8027700.js b/test/script/basic/JDK-8027700.js new file mode 100644 index 00000000..4c5445d8 --- /dev/null +++ b/test/script/basic/JDK-8027700.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027700: function redeclaration checks missing for declaration binding instantiation + * + * @test + * @run + */ + +Object.defineProperty(this,"x", { + value:0, + writable:true, + enumerable:false +}) + +try { + eval("function x() {}"); + fail("should have thrown TypeError"); +} catch (e) { + if (! (e instanceof TypeError)) { + fail("TypeError expected but got " + e); + } +} + +Object.defineProperty(this, "foo", { value:0 }) +try { + eval("function foo() {}"); + fail("should have thrown TypeError"); +} catch (e) { + if (! (e instanceof TypeError)) { + fail("TypeError expected but got " + e); + } +} diff --git a/test/script/basic/JDK-8027753.js b/test/script/basic/JDK-8027753.js new file mode 100644 index 00000000..2af0baad --- /dev/null +++ b/test/script/basic/JDK-8027753.js @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027753: Support ScriptObject to JSObject, ScriptObjectMirror, Map, Bindings auto-conversion as well as explicit wrap, unwrap + * + * @test + * @run + */ + +var ScriptUtils = Java.type("jdk.nashorn.api.scripting.ScriptUtils"); +var ScriptObjectMirror = Java.type("jdk.nashorn.api.scripting.ScriptObjectMirror"); + +var obj = { foo: 34, bar: 'hello' }; + +var wrapped = ScriptUtils.wrap(obj); +if (! (wrapped instanceof ScriptObjectMirror)) { + fail("ScriptUtils.wrap does not return a ScriptObjectMirror"); +} + +print("wrapped.foo = " + wrapped.foo); +print("wrapped.bar = " + wrapped.bar); + +var unwrapped = ScriptUtils.unwrap(wrapped); +if (! (unwrapped instanceof Object)) { + fail("ScriptUtils.unwrap does not return a ScriptObject"); +} + +// same object unwrapped? +print(unwrapped === obj); diff --git a/test/script/basic/JDK-8027753.js.EXPECTED b/test/script/basic/JDK-8027753.js.EXPECTED new file mode 100644 index 00000000..30a8779e --- /dev/null +++ b/test/script/basic/JDK-8027753.js.EXPECTED @@ -0,0 +1,3 @@ +wrapped.foo = 34 +wrapped.bar = hello +true diff --git a/test/script/basic/JDK-8027828.js b/test/script/basic/JDK-8027828.js new file mode 100644 index 00000000..ab60938b --- /dev/null +++ b/test/script/basic/JDK-8027828.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027828: ClassCastException when converting return value of a Java method to boolean + * + * @test + * @run + */ + +var x = new java.util.HashMap() +x.put('test', new java.io.File('test')) +if (x.get("test")) { + print('Found!') +} diff --git a/test/script/basic/JDK-8027828.js.EXPECTED b/test/script/basic/JDK-8027828.js.EXPECTED new file mode 100644 index 00000000..7a8c9edc --- /dev/null +++ b/test/script/basic/JDK-8027828.js.EXPECTED @@ -0,0 +1 @@ +Found! diff --git a/test/script/basic/JDK-8028020.js b/test/script/basic/JDK-8028020.js new file mode 100644 index 00000000..4dfa0cad --- /dev/null +++ b/test/script/basic/JDK-8028020.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8028020: Function parameter as last expression in comma in return value causes bad type calculation + * + * @test + * @run + */ + +function f(x) { + return 1, x +} + +function g(x, y) { + return x, y +} + +print(f("'1, x' works.")) +print(g(42, "'x, y' works too.")) diff --git a/test/script/basic/JDK-8028020.js.EXPECTED b/test/script/basic/JDK-8028020.js.EXPECTED new file mode 100644 index 00000000..816a21ce --- /dev/null +++ b/test/script/basic/JDK-8028020.js.EXPECTED @@ -0,0 +1,2 @@ +'1, x' works. +'x, y' works too. diff --git a/test/script/basic/NASHORN-100.js b/test/script/basic/NASHORN-100.js index fdea1cf0..df81284d 100644 --- a/test/script/basic/NASHORN-100.js +++ b/test/script/basic/NASHORN-100.js @@ -35,5 +35,5 @@ try { if (! (e instanceof SyntaxError)) { fail("#2 expected SyntaxError got " + e); } - print(e.toString().replace(/\\/g, '/')); + printError(e); } diff --git a/test/script/basic/NASHORN-100.js.EXPECTED b/test/script/basic/NASHORN-100.js.EXPECTED index d183bb33..e9c5a253 100644 --- a/test/script/basic/NASHORN-100.js.EXPECTED +++ b/test/script/basic/NASHORN-100.js.EXPECTED @@ -1,3 +1,3 @@ -SyntaxError: test/script/basic/NASHORN-100.js#32<eval>:1:0 Invalid return statement +SyntaxError:32:Invalid return statement return; ^ diff --git a/test/script/basic/NASHORN-293.js b/test/script/basic/NASHORN-293.js index cd83ec46..013714b8 100644 --- a/test/script/basic/NASHORN-293.js +++ b/test/script/basic/NASHORN-293.js @@ -40,15 +40,13 @@ for (var i = 0; i < 3; i++) { try { eval(src); } catch (e) { - var location = e.fileName ? e.fileName.slice(-9) : "unknown source"; - print(e.name, "@", location); + printError(e); } } for (var i = 0; i < 3; i++) { try { eval(src); } catch (e) { - var location = e.fileName ? e.fileName.slice(-9) : "unknown source"; - print(e.name, "@", location); + printError(e); } } diff --git a/test/script/basic/NASHORN-293.js.EXPECTED b/test/script/basic/NASHORN-293.js.EXPECTED index 68278106..90fe5f08 100644 --- a/test/script/basic/NASHORN-293.js.EXPECTED +++ b/test/script/basic/NASHORN-293.js.EXPECTED @@ -1,9 +1,9 @@ hello hello hello -TypeError @ #41<eval> -TypeError @ #41<eval> -TypeError @ #41<eval> -TypeError @ #49<eval> -TypeError @ #49<eval> -TypeError @ #49<eval> +TypeError:1:read property "foo" from undefined +TypeError:1:read property "foo" from undefined +TypeError:1:read property "foo" from undefined +TypeError:1:read property "foo" from undefined +TypeError:1:read property "foo" from undefined +TypeError:1:read property "foo" from undefined diff --git a/test/script/basic/NASHORN-397.js b/test/script/basic/NASHORN-397.js index e9484bc0..63e7b3ac 100644 --- a/test/script/basic/NASHORN-397.js +++ b/test/script/basic/NASHORN-397.js @@ -35,11 +35,8 @@ if (typeof (5).x !== 'number') { fail("typeof(5).x is not 'number'"); } -// It is function because PrintStream implements Closeable, which is -// marked with @FunctionalInterface. Yes, this means calling a stream -// like "stream()" closes it. -if (typeof (java.lang.System.out) != 'function') { - fail("typeof java.lang.System.out is not 'object'"); +if (typeof (java.net.Proxy.NO_PROXY) != 'object') { + fail("typeof java.net.Proxy.NO_PROXY is not 'object'"); } if (typeof (java.lang.Math.PI) != 'number') { diff --git a/test/script/basic/NASHORN-40.js b/test/script/basic/NASHORN-40.js index f031730b..34331ef2 100644 --- a/test/script/basic/NASHORN-40.js +++ b/test/script/basic/NASHORN-40.js @@ -31,11 +31,11 @@ try { eval("print(.foo)"); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } try { eval(".bar = 3423;"); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } diff --git a/test/script/basic/NASHORN-40.js.EXPECTED b/test/script/basic/NASHORN-40.js.EXPECTED index 0e03cab3..94f15980 100644 --- a/test/script/basic/NASHORN-40.js.EXPECTED +++ b/test/script/basic/NASHORN-40.js.EXPECTED @@ -1,6 +1,6 @@ -SyntaxError: test/script/basic/NASHORN-40.js#32<eval>:1:6 Expected an operand but found . +SyntaxError:32:Expected an operand but found . print(.foo) ^ -SyntaxError: test/script/basic/NASHORN-40.js#38<eval>:1:0 Expected an operand but found . +SyntaxError:38:Expected an operand but found . .bar = 3423; ^ diff --git a/test/script/basic/NASHORN-51.js b/test/script/basic/NASHORN-51.js index bc41d79b..45d00e9e 100644 --- a/test/script/basic/NASHORN-51.js +++ b/test/script/basic/NASHORN-51.js @@ -35,28 +35,28 @@ for (i in literals) { eval(literals[i] + "++"); print("ERROR!! post increment : " + literals[i]); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } try { eval(literals[i] + "--"); print("ERROR!! post decrement : " + literals[i]); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } try { eval("++" + literals[i]); print("ERROR!! pre increment : " + literals[i]); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } try { eval("--" + literals[i]); print("ERROR!! pre decrement : " + literals[i]); } catch (e) { - print(e.toString().replace(/\\/g, '/')); + printError(e); } } diff --git a/test/script/basic/NASHORN-51.js.EXPECTED b/test/script/basic/NASHORN-51.js.EXPECTED index 9479f7e1..52013856 100644 --- a/test/script/basic/NASHORN-51.js.EXPECTED +++ b/test/script/basic/NASHORN-51.js.EXPECTED @@ -1,72 +1,72 @@ -ReferenceError: test/script/basic/NASHORN-51.js#35<eval>:1:0 Invalid left hand side for assignment +ReferenceError:35:Invalid left hand side for assignment 1++ ^ -ReferenceError: test/script/basic/NASHORN-51.js#42<eval>:1:0 Invalid left hand side for assignment +ReferenceError:42:Invalid left hand side for assignment 1-- ^ -ReferenceError: test/script/basic/NASHORN-51.js#49<eval>:1:2 Invalid left hand side for assignment +ReferenceError:49:Invalid left hand side for assignment ++1 ^ -ReferenceError: test/script/basic/NASHORN-51.js#56<eval>:1:2 Invalid left hand side for assignment +ReferenceError:56:Invalid left hand side for assignment --1 ^ -ReferenceError: test/script/basic/NASHORN-51.js#35<eval>:1:0 Invalid left hand side for assignment +ReferenceError:35:Invalid left hand side for assignment 0++ ^ -ReferenceError: test/script/basic/NASHORN-51.js#42<eval>:1:0 Invalid left hand side for assignment +ReferenceError:42:Invalid left hand side for assignment 0-- ^ -ReferenceError: test/script/basic/NASHORN-51.js#49<eval>:1:2 Invalid left hand side for assignment +ReferenceError:49:Invalid left hand side for assignment ++0 ^ -ReferenceError: test/script/basic/NASHORN-51.js#56<eval>:1:2 Invalid left hand side for assignment +ReferenceError:56:Invalid left hand side for assignment --0 ^ -ReferenceError: test/script/basic/NASHORN-51.js#35<eval>:1:0 Invalid left hand side for assignment +ReferenceError:35:Invalid left hand side for assignment 3.14++ ^ -ReferenceError: test/script/basic/NASHORN-51.js#42<eval>:1:0 Invalid left hand side for assignment +ReferenceError:42:Invalid left hand side for assignment 3.14-- ^ -ReferenceError: test/script/basic/NASHORN-51.js#49<eval>:1:2 Invalid left hand side for assignment +ReferenceError:49:Invalid left hand side for assignment ++3.14 ^ -ReferenceError: test/script/basic/NASHORN-51.js#56<eval>:1:2 Invalid left hand side for assignment +ReferenceError:56:Invalid left hand side for assignment --3.14 ^ -ReferenceError: test/script/basic/NASHORN-51.js#35<eval>:1:0 Invalid left hand side for assignment +ReferenceError:35:Invalid left hand side for assignment true++ ^ -ReferenceError: test/script/basic/NASHORN-51.js#42<eval>:1:0 Invalid left hand side for assignment +ReferenceError:42:Invalid left hand side for assignment true-- ^ -ReferenceError: test/script/basic/NASHORN-51.js#49<eval>:1:2 Invalid left hand side for assignment +ReferenceError:49:Invalid left hand side for assignment ++true ^ -ReferenceError: test/script/basic/NASHORN-51.js#56<eval>:1:2 Invalid left hand side for assignment +ReferenceError:56:Invalid left hand side for assignment --true ^ -ReferenceError: test/script/basic/NASHORN-51.js#35<eval>:1:0 Invalid left hand side for assignment +ReferenceError:35:Invalid left hand side for assignment false++ ^ -ReferenceError: test/script/basic/NASHORN-51.js#42<eval>:1:0 Invalid left hand side for assignment +ReferenceError:42:Invalid left hand side for assignment false-- ^ -ReferenceError: test/script/basic/NASHORN-51.js#49<eval>:1:2 Invalid left hand side for assignment +ReferenceError:49:Invalid left hand side for assignment ++false ^ -ReferenceError: test/script/basic/NASHORN-51.js#56<eval>:1:2 Invalid left hand side for assignment +ReferenceError:56:Invalid left hand side for assignment --false ^ -ReferenceError: test/script/basic/NASHORN-51.js#35<eval>:1:0 Invalid left hand side for assignment +ReferenceError:35:Invalid left hand side for assignment null++ ^ -ReferenceError: test/script/basic/NASHORN-51.js#42<eval>:1:0 Invalid left hand side for assignment +ReferenceError:42:Invalid left hand side for assignment null-- ^ -ReferenceError: test/script/basic/NASHORN-51.js#49<eval>:1:2 Invalid left hand side for assignment +ReferenceError:49:Invalid left hand side for assignment ++null ^ -ReferenceError: test/script/basic/NASHORN-51.js#56<eval>:1:2 Invalid left hand side for assignment +ReferenceError:56:Invalid left hand side for assignment --null ^ diff --git a/test/script/basic/NASHORN-98.js b/test/script/basic/NASHORN-98.js index 3e50cf56..9e341e0c 100644 --- a/test/script/basic/NASHORN-98.js +++ b/test/script/basic/NASHORN-98.js @@ -34,7 +34,7 @@ try { if (! (e instanceof SyntaxError)) { fail("syntax error expected here got " + e); } - print(e.toString().replace(/\\/g, '/')); + printError(e); } try { @@ -43,5 +43,5 @@ try { if (! (e instanceof SyntaxError)) { fail("syntax error expected here got " + e); } - print(e.toString().replace(/\\/g, '/')); + printError(e); } diff --git a/test/script/basic/NASHORN-98.js.EXPECTED b/test/script/basic/NASHORN-98.js.EXPECTED index 93a89364..a43935bf 100644 --- a/test/script/basic/NASHORN-98.js.EXPECTED +++ b/test/script/basic/NASHORN-98.js.EXPECTED @@ -1,6 +1,6 @@ -SyntaxError: test/script/basic/NASHORN-98.js#32<eval>:1:13 Expected comma but found decimal +SyntaxError:32:Expected comma but found decimal var x = [ 23 34 ] ^ -SyntaxError: test/script/basic/NASHORN-98.js#41<eval>:1:18 Expected comma but found ident +SyntaxError:41:Expected comma but found ident var x = { foo: 33 bar: 'hello' } ^ diff --git a/test/script/basic/convert.js b/test/script/basic/convert.js new file mode 100644 index 00000000..8d11d122 --- /dev/null +++ b/test/script/basic/convert.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Tests for convert method of ScriptUtils. + * + * @test + * @run + */ + +var ScriptUtils = Java.type("jdk.nashorn.api.scripting.ScriptUtils"); +obj = { valueOf: function() { print("hello"); return 43.3; } }; + +// object to double +print(ScriptUtils.convert(obj, java.lang.Number.class)); + +// array to List +var arr = [3, 44, 23, 33]; +var list = ScriptUtils.convert(arr, java.util.List.class); +print(list instanceof java.util.List) +print(list); + +// object to Map +obj = { foo: 333, bar: 'hello'}; +var map = ScriptUtils.convert(obj, java.util.Map.class); +print(map instanceof java.util.Map); +for (m in map) { + print(m + " " + map[m]); +} + +// object to String +obj = { toString: function() { print("in toString"); return "foo" } }; +print(ScriptUtils.convert(obj, java.lang.String.class)); + +// array to Java array +var jarr = ScriptUtils.convert(arr, Java.type("int[]")); +print(jarr instanceof Java.type("int[]")); +for (i in jarr) { + print(jarr[i]); +} + diff --git a/test/script/basic/convert.js.EXPECTED b/test/script/basic/convert.js.EXPECTED new file mode 100644 index 00000000..e83faebd --- /dev/null +++ b/test/script/basic/convert.js.EXPECTED @@ -0,0 +1,14 @@ +hello +43.3 +true +[3, 44, 23, 33] +true +foo 333 +bar hello +in toString +foo +true +3 +44 +23 +33 diff --git a/test/script/basic/eval.js b/test/script/basic/eval.js index bd63068c..3d66c028 100644 --- a/test/script/basic/eval.js +++ b/test/script/basic/eval.js @@ -69,5 +69,5 @@ try { eval("print('hello)"); } catch (e) { print("is syntax error? " + (e instanceof SyntaxError)); - print(e.toString().replace(/\\/g, '/')); + printError(e); } diff --git a/test/script/basic/eval.js.EXPECTED b/test/script/basic/eval.js.EXPECTED index 3ab2f3da..7fe97ef6 100644 --- a/test/script/basic/eval.js.EXPECTED +++ b/test/script/basic/eval.js.EXPECTED @@ -10,6 +10,6 @@ eval.length 1 100 3300 is syntax error? true -SyntaxError: test/script/basic/eval.js#69<eval>:1:13 Missing close quote +SyntaxError:69:Missing close quote print('hello) ^ diff --git a/test/script/basic/objects.js.EXPECTED b/test/script/basic/objects.js.EXPECTED index a6bedc09..d59daa87 100644 --- a/test/script/basic/objects.js.EXPECTED +++ b/test/script/basic/objects.js.EXPECTED @@ -29,18 +29,18 @@ abc is writable? undefined abc is configurable? true abc is enumerable? true abc's value = undefined -abc's get = abc() { return "abc"; } +abc's get = get abc() { return "abc"; } abc's set = undefined xyz is writable? undefined xyz is configurable? true xyz is enumerable? true xyz's value = undefined xyz's get = undefined -xyz's set = xyz(val) { print(val); } +xyz's set = set xyz(val) { print(val); } hey is writable? undefined hey is configurable? true hey is enumerable? true hey's value = undefined -hey's get = hey() { return "hey"; } -hey's set = hey(val) { print(val); } +hey's get = get hey() { return "hey"; } +hey's set = set hey(val) { print(val); } undefined diff --git a/test/script/error/JDK-8026039.js b/test/script/error/JDK-8026039.js new file mode 100644 index 00000000..f37d9bdd --- /dev/null +++ b/test/script/error/JDK-8026039.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8026039: future strict names are allowed as function name and argument name of a strict function + * + * @test/compile-error + */ + +function public() {"use strict"} + +function f(public) {"use strict"} diff --git a/test/script/error/JDK-8026039.js.EXPECTED b/test/script/error/JDK-8026039.js.EXPECTED new file mode 100644 index 00000000..cde398f3 --- /dev/null +++ b/test/script/error/JDK-8026039.js.EXPECTED @@ -0,0 +1,9 @@ +test/script/error/JDK-8026039.js:30:9 "public" cannot be used as function name in strict mode +function public() {"use strict"} + ^ +test/script/error/JDK-8026039.js:32:11 Expected ident but found public +function f(public) {"use strict"} + ^ +test/script/error/JDK-8026039.js:33:0 Expected } but found eof + +^ diff --git a/test/script/jfx.js b/test/script/jfx.js new file mode 100644 index 00000000..5962b5d0 --- /dev/null +++ b/test/script/jfx.js @@ -0,0 +1,103 @@ +/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Base library for JavaFX canvas run by Nashorn testing.
+ * @subtest
+ *
+ *
+ */
+
+var System = Java.type("java.lang.System");
+var AWTImage = Java.type("org.jemmy.image.AWTImage");
+var PNGDecoder = Java.type("org.jemmy.image.PNGDecoder");
+var JemmyFxRoot = Java.type("org.jemmy.fx.Root");
+var AWTRobotCapturer = Java.type("org.jemmy.image.AWTRobotCapturer");
+var ByWindowType = Java.type("org.jemmy.fx.ByWindowType");
+var Scene = Java.type("javafx.scene.Scene");
+var Stage = Java.type("javafx.stage.Stage");
+var File = Java.type("java.io.File");
+var OSInfo = Java.type("sun.awt.OSInfo");
+var OSType = Java.type("sun.awt.OSInfo.OSType");
+var StringBuffer = Java.type("java.lang.StringBuffer");
+var Paint = Java.type("javafx.scene.paint.Paint");
+var Color = Java.type("javafx.scene.paint.Color");
+var Image = Java.type("javafx.scene.image.Image");
+var Canvas = Java.type("javafx.scene.canvas.Canvas");
+var BorderPane = Java.type("javafx.scene.layout.BorderPane");
+var StackPane = Java.type("javafx.scene.layout.StackPane");
+var StrokeLineCap = Java.type("javafx.scene.shape.StrokeLineCap");
+var Platform = Java.type("javafx.application.Platform");
+var Runnable = Java.type("java.lang.Runnable");
+var RunnableExtend = Java.extend(Runnable);
+var AnimationTimer = Java.type("javafx.animation.AnimationTimer");
+var AnimationTimerExtend = Java.extend(AnimationTimer);
+var Timer = Java.type("java.util.Timer");
+var TimerTask = Java.type("java.util.TimerTask");
+
+var TESTNAME = "test";
+var fsep = System.getProperty("file.separator");
+
+function checkImageAndExit() {
+ var raceTimer = new Timer(true);
+ var timerTask = new TimerTask() {
+ run: function run() {
+ var tmpdir = System.getProperty("java.io.tmpdir");
+ var timenow = (new Date()).getTime();
+ var scrShotTmp = tmpdir + fsep + "screenshot" + timenow +".png";
+ var goldenImageDir = __DIR__ + "jfx" + fsep + TESTNAME + fsep + "golden";
+ makeScreenShot(scrShotTmp);
+ var dupImg = isDuplicateImages(scrShotTmp, goldenImageDir);
+ (new File(scrShotTmp)).delete();
+ if (!dupImg) System.err.println("ERROR: screenshot does not match the golden image");
+ exit(0);
+ }
+ };
+ raceTimer.schedule(timerTask, 100);
+}
+
+function makeScreenShot(shootToImg) {
+ JemmyFxRoot.ROOT.getEnvironment().setImageCapturer(new AWTRobotCapturer());
+ var wrap = JemmyFxRoot.ROOT.lookup(new ByWindowType($STAGE.class)).lookup(Scene.class).wrap(0);
+ var imageJemmy = wrap.getScreenImage();
+ imageJemmy.save(shootToImg);
+}
+
+function isDuplicateImages(screenShot, goldenDir) {
+ var f1 = new File(screenShot);
+ var f2;
+ var sb = new StringBuffer(goldenDir);
+ if (OSInfo.getOSType() == OSType.WINDOWS) {
+ f2 = new File(sb.append(fsep + "windows.png").toString());
+ } else if (OSInfo.getOSType() == OSType.LINUX) {
+ f2 = new File(sb.append(fsep + "linux.png").toString());
+ } else if (OSInfo.getOSType() == OSType.MACOSX) {
+ f2 = new File(sb.append(fsep + "macosx.png").toString());
+ }
+ if (f1.exists() && f2.exists()) {
+ var image1 = new AWTImage(PNGDecoder.decode(f1.getAbsolutePath()));
+ var image2 = new AWTImage(PNGDecoder.decode(f2.getAbsolutePath()));
+ return image1.compareTo(image2) == null ? true : false;
+ }
+ return false;
+}
diff --git a/test/script/jfx/flyingimage.js b/test/script/jfx/flyingimage.js new file mode 100644 index 00000000..844a0fc3 --- /dev/null +++ b/test/script/jfx/flyingimage.js @@ -0,0 +1,84 @@ +/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Testing JavaFX canvas run by Nashorn.
+ *
+ * @test/nocompare
+ * @run
+ * @fork
+ */
+
+TESTNAME = "flyingimage";
+
+var WIDTH = 800;
+var HEIGHT = 600;
+var canvas = new Canvas(WIDTH, HEIGHT);
+function fileToURL(file) {
+ return new File(file).toURI().toURL().toExternalForm();
+}
+var imageUrl = fileToURL(__DIR__ + "flyingimage/flyingimage.png");
+var img = new Image(imageUrl);
+var isFrameRendered = false;
+function renderFrame() {
+ var t = frame;
+ var gc = canvas.graphicsContext2D;
+ gc.setFill(Color.web("#cccccc"));
+ gc.fillRect(0, 0, WIDTH, HEIGHT);
+ gc.setStroke(Color.web("#000000"));
+ gc.setLineWidth(1);
+ gc.strokeRect(5, 5, WIDTH - 10, HEIGHT - 10);
+ var c = 200;
+ var msc= 0.5 * HEIGHT / img.height;
+ var sp0 = 0.003;
+ for (var h = 0; h < c; h++) {
+ gc.setTransform(1, 0, 0, 1, 0, 0);
+ var yh = h / (c - 1);
+ gc.translate((0.5 + Math.sin(t * sp0 + h * 0.1) / 3) * WIDTH, 25 + (HEIGHT * 3 / 4 - 40) * (yh * yh));
+ var sc = 30 / img.height + msc * yh * yh;
+ gc.rotate(90 * Math.sin(t * sp0 + h * 0.1 + Math.PI));
+ gc.scale(sc, sc);
+ gc.drawImage(img, -img.width / 2, -img.height / 2);
+ }
+ gc.setTransform(1, 0, 0, 1, 0, 0);
+ isFrameRendered = true;
+}
+var stack = new StackPane();
+var pane = new BorderPane();
+pane.setCenter(canvas);
+stack.getChildren().add(pane);
+$STAGE.scene = new Scene(stack);
+var frame = 0;
+var timer = new AnimationTimerExtend() {
+ handle: function handle(now) {
+ if (frame < 200) {
+ renderFrame();
+ frame++;
+ } else {
+ checkImageAndExit();
+ timer.stop();
+ }
+ }
+};
+timer.start();
+
diff --git a/test/script/jfx/flyingimage/flyingimage.png b/test/script/jfx/flyingimage/flyingimage.png Binary files differnew file mode 100644 index 00000000..afc44dd3 --- /dev/null +++ b/test/script/jfx/flyingimage/flyingimage.png diff --git a/test/script/jfx/flyingimage/golden/linux.png b/test/script/jfx/flyingimage/golden/linux.png Binary files differnew file mode 100644 index 00000000..4a668a6d --- /dev/null +++ b/test/script/jfx/flyingimage/golden/linux.png diff --git a/test/script/jfx/flyingimage/golden/macosx.png b/test/script/jfx/flyingimage/golden/macosx.png Binary files differnew file mode 100644 index 00000000..ba72fe68 --- /dev/null +++ b/test/script/jfx/flyingimage/golden/macosx.png diff --git a/test/script/jfx/flyingimage/golden/windows.png b/test/script/jfx/flyingimage/golden/windows.png Binary files differnew file mode 100644 index 00000000..e47ae4df --- /dev/null +++ b/test/script/jfx/flyingimage/golden/windows.png diff --git a/test/script/jfx/kaleidoscope.js b/test/script/jfx/kaleidoscope.js new file mode 100644 index 00000000..4430ff01 --- /dev/null +++ b/test/script/jfx/kaleidoscope.js @@ -0,0 +1,167 @@ +/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Testing JavaFX canvas run by Nashorn.
+ *
+ * @test/nocompare
+ * @run
+ * @fork
+ */
+
+TESTNAME = "kaleidoscope";
+
+var WIDTH = 800;
+var HEIGHT = 600;
+var canvas = new Canvas(WIDTH, HEIGHT);
+var context = canvas.graphicsContext2D;
+
+var x,y;
+var p_x,p_y;
+var a=0;
+var b=0;
+var angle=Math.PI/180*8;
+var color=0;
+var limit1=Math.PI*1.5;
+var limit2=Math.PI*1.79;
+var c=new Array(6);
+var d=new Array(6);
+var r,e;
+var fade;
+var prv_x,prv_y,prv_x2,prv_y2;
+var isFrameRendered = false;
+
+function renderFrame() {
+ if (!isFrameRendered) {
+ a=0.2*angle;
+ b=0.7*angle;
+ r=0;
+ fade=32;
+ for(var i=0;i<6;i++)
+ {
+ c[i]=1.0/(i+1)/2;
+ d[i]=1.0/(i+1)/2;
+ }
+ radius=Math.round((WIDTH+HEIGHT)/8);
+ e=radius*0.2;
+ p_x=Math.round(WIDTH/2);
+ p_y=Math.round(HEIGHT/2);
+ x=(radius*c[0])*Math.cos(a*d[1])+(radius*c[2])*Math.sin(a*d[3])+(radius*c[4])*Math.sin(a*d[5]);
+ y=(radius*c[5])*Math.sin(a*d[4])+(radius*c[3])*Math.cos(a*d[2])+(radius*c[1])*Math.cos(a*d[0]);
+ isFrameRendered = true;
+ }
+ anim();
+}
+
+function anim() {
+ var a1=Math.cos(a*2);
+ var a2=Math.cos(a*4);
+ var a3=Math.cos(a);
+ var a4=Math.sin(a);
+ if(b>limit1&&b<limit2) {
+ r+=radius*0.02*a1;
+ prv_x=x;
+ prv_y=y;
+ x=prv_x2+r*a3;
+ y=prv_y2+r*a4;
+ } else {
+ prv_x=x;
+ prv_y=y;
+ prv_x2=x;
+ prv_y2=y;
+ x=(radius*c[0])*Math.cos(a*d[1])+(radius*c[2])*Math.sin(a*d[3])+(radius*c[4])*Math.sin(a*d[5]);
+ y=(radius*c[5])*Math.sin(a*d[4])+(radius*c[3])*Math.cos(a*d[2])+(radius*c[1])*Math.cos(a*d[0]);
+ }
+ var c3=16*Math.cos(a*10);
+ var c1=Math.floor(56*Math.cos(a*angle*4)+c3);
+ var c2=Math.floor(56*Math.sin(a*angle*4)-c3);
+ context.lineCap=StrokeLineCap.ROUND;
+ context.setStroke(Paint.valueOf('rgba('+(192+c1)+','+(192+c2)+','+(192-c1)+','+(0.01-0.005*-a1)+')'));
+ context.lineWidth=e*1.4+e*0.8*a3;
+ draw_line(p_x,p_y,prv_x,prv_y,x,y);
+ context.lineWidth=e+e*0.8*a3;
+ draw_line(p_x,p_y,prv_x,prv_y,x,y);
+ context.setStroke(Paint.valueOf('rgba('+(192+c1)+','+(192+c2)+','+(192-c1)+','+(0.06-0.03*-a1)+')'));
+ context.lineWidth=e*0.6+e*0.35*a3;
+ draw_line(p_x,p_y,prv_x,prv_y,x,y);
+ context.setStroke(Paint.valueOf('rgba(0,0,0,0.06)'));
+ context.lineWidth=e*0.4+e*0.225*a3;
+ draw_line(p_x,p_y,prv_x,prv_y,x,y);
+ context.setStroke(Paint.valueOf('rgba('+(192+c1)+','+(192+c2)+','+(192-c1)+','+(0.1-0.075*-a1)+')'));
+ context.lineWidth=e*0.2+e*0.1*a3;
+ draw_line(p_x,p_y,prv_x,prv_y,x,y);
+ context.setStroke(Paint.valueOf('rgba(255,255,255,0.4)'));
+ context.lineWidth=e*(0.1-0.05*-a2);
+ draw_line(p_x,p_y,prv_x,prv_y,x,y);
+ a+=angle*Math.cos(b);
+ b+=angle*0.1;
+}
+
+function draw_line(x,y,x1,y1,x2,y2) {
+ context.beginPath();
+ context.moveTo(x+x1,y+y1);
+ context.lineTo(x+x2,y+y2);
+ context.moveTo(x-x1,y+y1);
+ context.lineTo(x-x2,y+y2);
+ context.moveTo(x-x1,y-y1);
+ context.lineTo(x-x2,y-y2);
+ context.moveTo(x+x1,y-y1);
+ context.lineTo(x+x2,y-y2);
+ context.moveTo(x+y1,y+x1);
+ context.lineTo(x+y2,y+x2);
+ context.moveTo(x-y1,y+x1);
+ context.lineTo(x-y2,y+x2);
+ context.moveTo(x-y1,y-x1);
+ context.lineTo(x-y2,y-x2);
+ context.moveTo(x+y1,y-x1);
+ context.lineTo(x+y2,y-x2);
+ context.moveTo(x,y+x2);
+ context.lineTo(x,y+x1);
+ context.moveTo(x,y-x2);
+ context.lineTo(x,y-x1);
+ context.moveTo(x+x2,y);
+ context.lineTo(x+x1,y);
+ context.moveTo(x-x2,y);
+ context.lineTo(x-x1,y);
+ context.stroke();
+ context.closePath();
+}
+
+var stack = new StackPane();
+var pane = new BorderPane();
+pane.setCenter(canvas);
+stack.getChildren().add(pane);
+$STAGE.scene = new Scene(stack);
+var frame = 0;
+var timer = new AnimationTimerExtend() {
+ handle: function handle(now) {
+ if (frame < 800) {
+ renderFrame();
+ frame++;
+ } else {
+ checkImageAndExit();
+ timer.stop();
+ }
+ }
+};
+timer.start();
diff --git a/test/script/jfx/kaleidoscope/golden/linux.png b/test/script/jfx/kaleidoscope/golden/linux.png Binary files differnew file mode 100644 index 00000000..4d7e81ad --- /dev/null +++ b/test/script/jfx/kaleidoscope/golden/linux.png diff --git a/test/script/jfx/kaleidoscope/golden/macosx.png b/test/script/jfx/kaleidoscope/golden/macosx.png Binary files differnew file mode 100644 index 00000000..64d9499b --- /dev/null +++ b/test/script/jfx/kaleidoscope/golden/macosx.png diff --git a/test/script/jfx/kaleidoscope/golden/windows.png b/test/script/jfx/kaleidoscope/golden/windows.png Binary files differnew file mode 100644 index 00000000..4d7e81ad --- /dev/null +++ b/test/script/jfx/kaleidoscope/golden/windows.png diff --git a/test/script/jfx/spread.js b/test/script/jfx/spread.js new file mode 100644 index 00000000..4a8f38ce --- /dev/null +++ b/test/script/jfx/spread.js @@ -0,0 +1,222 @@ +/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Testing JavaFX canvas run by Nashorn.
+ *
+ * @test/nocompare
+ * @run
+ * @fork
+ */
+
+TESTNAME = "spread";
+
+var WIDTH = 800;
+var HEIGHT = 600;
+var canvas = new Canvas(WIDTH, HEIGHT);
+var context = canvas.graphicsContext2D;
+
+/* "Spread" tech demo of canvas by Tom Theisen
+ *
+ * This will animate a sequence of branch structures in a canvas element.
+ * Each frame, a new direction is calculated, similar to the last frame.
+ */
+
+var start_width = 20; // starting width of each branch
+var frame_time = 30; // milliseconds per frame
+var straighten_factor = 0.95; // value from 0 to 1, factor applied to direction_offset every frame
+var curviness = 0.2; // amount of random direction change each frame
+
+var color_speed = 0.03; // speed at which colors change when cycling is enabled
+var branch_shrink = 0.95; // factor by which branches shrink every frame
+var min_width = 1; // minimum WIDTH for branch, after which they are discontinued
+var branch_opacity = 0.4; // opacity of lines drawn
+var branch_count = 3; // branch count per tree
+var branch_bud_size = 0.5; // ratio of original branch size at which branch will split
+var branch_bud_angle = 1; // angle offset for split branch;
+
+var paper; // reference to graphics context
+var branches = Object(); // linked list of active branches
+var color_styles = []; // pre-computed list of colors as styles. format: (r,g,b,a)
+var direction_offset = 0; // current direction offset in radians. this is applied to all branches.
+var frame = 0; // frame counter
+var timespent = 0; // total time spent so far, used to calculate average frame render duration
+var frameratespan; // html span element for updating performance number
+
+// preferences object, contains an attribute for each user setting
+var prefs = {
+ wrap: true, // causes branches reaching edge of viewable area to appear on opposite side
+ fade: false, // fade existing graphics on each frame
+ cycle: true, // gradually change colors each frame
+ new_branch_frames: 20 // number of frames elapsed between each auto-generated tree
+};
+
+// create tree at the specified position with number of branches
+function create_tree(branches, start_width, position, branch_count) {
+ var angle_offset = Math.PI * 2 / branch_count;
+ for (var i = 0; i < branch_count; ++i) {
+ branch_add(branches, new Branch(position, angle_offset * i, start_width));
+ }
+}
+
+// add branch to collection
+function branch_add(branches, branch) {
+ branch.next = branches.next;
+ branches.next = branch;
+}
+
+// get the coordinates for the position of a new tree
+// use the center of the canvas
+function get_new_tree_center(width, height) {
+ return {
+ x: 0.5 * width,
+ y: 0.5 * height
+ };
+}
+
+// Branch constructor
+// position has x and y properties
+// direction is in radians
+function Branch(position, direction, width) {
+ this.x = position.x;
+ this.y = position.y;
+ this.width = width;
+ this.original_width = width;
+ this.direction = direction;
+}
+
+// update position, direction and width of a particular branch
+function branch_update(branches, branch, paper) {
+ paper.beginPath();
+ paper.lineWidth = branch.width;
+ paper.moveTo(branch.x, branch.y);
+
+ branch.width *= branch_shrink;
+ branch.direction += direction_offset;
+ branch.x += Math.cos(branch.direction) * branch.width;
+ branch.y += Math.sin(branch.direction) * branch.width;
+
+ paper.lineTo(branch.x, branch.y);
+ paper.stroke();
+
+ if (prefs.wrap) wrap_branch(branch, WIDTH, HEIGHT);
+
+ if (branch.width < branch.original_width * branch_bud_size) {
+ branch.original_width *= branch_bud_size;
+ branch_add(branches, new Branch(branch, branch.direction + 1, branch.original_width));
+ }
+}
+
+function draw_frame() {
+ if (prefs.fade) {
+ paper.fillRect(0, 0, WIDTH, HEIGHT);
+ }
+
+ if (prefs.cycle) {
+ paper.setStroke(Paint.valueOf(color_styles[frame % color_styles.length]));
+ }
+
+ if (frame++ % prefs.new_branch_frames == 0) {
+ create_tree(branches, start_width, get_new_tree_center(WIDTH, HEIGHT), branch_count);
+ }
+
+ direction_offset += (0.35 + (frame % 200) * 0.0015) * curviness - curviness / 2;
+ direction_offset *= straighten_factor;
+
+ var branch = branches;
+ var prev_branch = branches;
+ while (branch = branch.next) {
+ branch_update(branches, branch, paper);
+
+ if (branch.width < min_width) {
+ // remove branch from list
+ prev_branch.next = branch.next;
+ }
+
+ prev_branch = branch;
+ }
+}
+
+// constrain branch position to visible area by "wrapping" from edge to edge
+function wrap_branch(branch, WIDTH, HEIGHT) {
+ branch.x = positive_mod(branch.x, WIDTH);
+ branch.y = positive_mod(branch.y, HEIGHT);
+}
+
+// for a < 0, b > 0, javascript returns a negative number for a % b
+// this is a variant of the % operator that adds b to the result in this case
+function positive_mod(a, b) {
+ // ECMA 262 11.5.3: Applying the % Operator
+ // remainder operator does not convert operands to integers,
+ // although negative results are possible
+
+ return ((a % b) + b) % b;
+}
+
+// pre-compute color styles that will be used for color cycling
+function populate_colors(color_speed, color_styles, branch_opacity) {
+ // used in calculation of RGB values
+ var two_thirds_pi = Math.PI * 2 / 3;
+ var four_thirds_pi = Math.PI * 4 / 3;
+ var two_pi = Math.PI * 2;
+
+ // hue does represent hue, but not in the conventional HSL scheme
+ for(var hue = 0; hue < two_pi; hue += color_speed) {
+ var r = Math.floor(Math.sin(hue) * 128 + 128);
+ var g = Math.floor(Math.sin(hue + two_thirds_pi) * 128 + 128);
+ var b = Math.floor(Math.sin(hue + four_thirds_pi) * 128 + 128);
+ color = "rgba(" + [r, g, b, branch_opacity].join() + ")";
+
+ color_styles.push(color);
+ }
+}
+
+// apply initial settings to canvas object
+function setup_canvas() {
+ paper = canvas.graphicsContext2D;
+ paper.setFill(Paint.valueOf('rgb(0, 0, 0)'));
+ paper.fillRect(0, 0, WIDTH, HEIGHT);
+ paper.setFill(Paint.valueOf("rgba(0, 0, 0, 0.005)"));
+ paper.setStroke(Paint.valueOf("rgba(128, 128, 64, " + String(branch_opacity) + ")"));
+}
+
+populate_colors(color_speed, color_styles, branch_opacity);
+setup_canvas();
+
+var stack = new StackPane();
+var pane = new BorderPane();
+pane.setCenter(canvas);
+stack.getChildren().add(pane);
+$STAGE.scene = new Scene(stack);
+var timer = new AnimationTimerExtend() {
+ handle: function handle(now) {
+ if (frame < 200) {
+ draw_frame();
+ } else {
+ checkImageAndExit();
+ timer.stop();
+ }
+ }
+};
+timer.start();
+
diff --git a/test/script/jfx/spread/golden/linux.png b/test/script/jfx/spread/golden/linux.png Binary files differnew file mode 100644 index 00000000..fc535a47 --- /dev/null +++ b/test/script/jfx/spread/golden/linux.png diff --git a/test/script/jfx/spread/golden/macosx.png b/test/script/jfx/spread/golden/macosx.png Binary files differnew file mode 100644 index 00000000..c2881623 --- /dev/null +++ b/test/script/jfx/spread/golden/macosx.png diff --git a/test/script/jfx/spread/golden/windows.png b/test/script/jfx/spread/golden/windows.png Binary files differnew file mode 100644 index 00000000..fc535a47 --- /dev/null +++ b/test/script/jfx/spread/golden/windows.png diff --git a/test/script/sandbox/arrayclass.js b/test/script/sandbox/arrayclass.js new file mode 100644 index 00000000..c29c86bd --- /dev/null +++ b/test/script/sandbox/arrayclass.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Try to access array class of a sensitive class like Unsafe. + * + * @test + * @security + * @run + */ + +try { + var unsafeArr = Java.type("[Lsun.misc.Unsafe;"); + fail("No Exception for [Lsun.misc.Unsafe;"); +} catch (e) { + print(e); +} diff --git a/test/script/sandbox/arrayclass.js.EXPECTED b/test/script/sandbox/arrayclass.js.EXPECTED new file mode 100644 index 00000000..d66d77f0 --- /dev/null +++ b/test/script/sandbox/arrayclass.js.EXPECTED @@ -0,0 +1 @@ +java.lang.ClassNotFoundException: [Lsun.misc.Unsafe; diff --git a/test/script/sandbox/loadcompat.js b/test/script/sandbox/loadcompat.js index e99f67f2..f0338df7 100644 --- a/test/script/sandbox/loadcompat.js +++ b/test/script/sandbox/loadcompat.js @@ -48,3 +48,7 @@ if (typeof JavaAdapter != 'function') { if (typeof importPackage != 'function') { fail("importPackage function is missing in compatibility script"); } + +if (typeof sync != 'function') { + fail("sync function is missing in compatibility script"); +} diff --git a/test/script/trusted/JDK-8025629.js b/test/script/trusted/JDK-8025629.js new file mode 100644 index 00000000..7dda8465 --- /dev/null +++ b/test/script/trusted/JDK-8025629.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8025629: load function should support a way to load scripts from classpath + * + * @test + * @run + */ + +load("classpath:jdk/nashorn/internal/runtime/resources/load_test.js") + +Assert.assertEquals(loadedFunc("hello"), "HELLO"); diff --git a/test/src/jdk/nashorn/api/javaaccess/ArrayConversionTest.java b/test/src/jdk/nashorn/api/javaaccess/ArrayConversionTest.java new file mode 100644 index 00000000..4a1d8d5d --- /dev/null +++ b/test/src/jdk/nashorn/api/javaaccess/ArrayConversionTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.api.javaaccess; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + +import java.util.Arrays; +import java.util.List; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import org.testng.TestNG; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ArrayConversionTest { + private static ScriptEngine e = null; + + public static void main(final String[] args) { + TestNG.main(args); + } + + @BeforeClass + public static void setUpClass() throws ScriptException { + e = new ScriptEngineManager().getEngineByName("nashorn"); + } + + @AfterClass + public static void tearDownClass() { + e = null; + } + + @Test + public void testIntArrays() throws ScriptException { + runTest("assertNullIntArray", "null"); + runTest("assertEmptyIntArray", "[]"); + runTest("assertSingle42IntArray", "[42]"); + runTest("assertSingle42IntArray", "['42']"); + runTest("assertIntArrayConversions", "[false, true, NaN, Infinity, -Infinity, 0.4, 0.6, null, undefined, [], {}, [1], [1, 2]]"); + } + + @Test + public void testIntIntArrays() throws ScriptException { + runTest("assertNullIntIntArray", "null"); + runTest("assertEmptyIntIntArray", "[]"); + runTest("assertSingleEmptyIntIntArray", "[[]]"); + runTest("assertSingleNullIntIntArray", "[null]"); + runTest("assertLargeIntIntArray", "[[false], [1], [2, 3], [4, 5, 6], ['7', {valueOf: function() { return 8 }}]]"); + } + + @Test + public void testObjectObjectArrays() throws ScriptException { + runTest("assertLargeObjectObjectArray", "[[false], [1], ['foo', 42.3], [{x: 17}]]"); + } + + @Test + public void testBooleanArrays() throws ScriptException { + runTest("assertBooleanArrayConversions", "[false, true, '', 'false', 0, 1, 0.4, 0.6, {}, [], [false], [true], NaN, Infinity, null, undefined]"); + } + + @Test + public void testArrayAmbiguity() throws ScriptException { + runTest("x", "'abc'"); + runTest("x", "['foo', 'bar']"); + } + + @Test + public void testListArrays() throws ScriptException { + runTest("assertListArray", "[['foo', 'bar'], ['apple', 'orange']]"); + } + + @Test + public void testVarArgs() throws ScriptException { + // Sole NativeArray in vararg position becomes vararg array itself + runTest("assertVarArg_42_17", "[42, 17]"); + // NativeArray in vararg position becomes an argument if there are more arguments + runTest("assertVarArg_array_17", "[42], 18"); + // Only NativeArray is converted to vararg array, other objects (e.g. a function) aren't + runTest("assertVarArg_function", "function() { return 'Hello' }"); + } + + private static void runTest(final String testMethodName, final String argument) throws ScriptException { + e.eval("Java.type('" + ArrayConversionTest.class.getName() + "')." + testMethodName + "(" + argument + ")"); + } + + public static void assertNullIntArray(int[] array) { + assertNull(array); + } + + public static void assertNullIntIntArray(int[][] array) { + assertNull(array); + } + + public static void assertEmptyIntArray(int[] array) { + assertEquals(0, array.length); + } + + public static void assertSingle42IntArray(int[] array) { + assertEquals(1, array.length); + assertEquals(42, array[0]); + } + + + public static void assertIntArrayConversions(int[] array) { + assertEquals(13, array.length); + assertEquals(0, array[0]); // false + assertEquals(1, array[1]); // true + assertEquals(0, array[2]); // NaN + assertEquals(0, array[3]); // Infinity + assertEquals(0, array[4]); // -Infinity + assertEquals(0, array[5]); // 0.4 + assertEquals(0, array[6]); // 0.6 - floor, not round + assertEquals(0, array[7]); // null + assertEquals(0, array[8]); // undefined + assertEquals(0, array[9]); // [] + assertEquals(0, array[10]); // {} + assertEquals(1, array[11]); // [1] + assertEquals(0, array[12]); // [1, 2] + } + + public static void assertEmptyIntIntArray(int[][] array) { + assertEquals(0, array.length); + } + + public static void assertSingleEmptyIntIntArray(int[][] array) { + assertEquals(1, array.length); + assertTrue(Arrays.equals(new int[0], array[0])); + } + + public static void assertSingleNullIntIntArray(int[][] array) { + assertEquals(1, array.length); + assertNull(null, array[0]); + } + + public static void assertLargeIntIntArray(int[][] array) { + assertEquals(5, array.length); + assertTrue(Arrays.equals(new int[] { 0 }, array[0])); + assertTrue(Arrays.equals(new int[] { 1 }, array[1])); + assertTrue(Arrays.equals(new int[] { 2, 3 }, array[2])); + assertTrue(Arrays.equals(new int[] { 4, 5, 6 }, array[3])); + assertTrue(Arrays.equals(new int[] { 7, 8 }, array[4])); + } + + public static void assertLargeObjectObjectArray(Object[][] array) throws ScriptException { + assertEquals(4, array.length); + assertTrue(Arrays.equals(new Object[] { Boolean.FALSE }, array[0])); + assertTrue(Arrays.equals(new Object[] { 1 }, array[1])); + assertTrue(Arrays.equals(new Object[] { "foo", 42.3d }, array[2])); + assertEquals(1, array[3].length); + e.getBindings(ScriptContext.ENGINE_SCOPE).put("obj", array[3][0]); + assertEquals(17, e.eval("obj.x")); + } + + public static void assertBooleanArrayConversions(boolean[] array) { + assertEquals(16, array.length); + assertFalse(array[0]); // false + assertTrue(array[1]); // true + assertFalse(array[2]); // '' + assertTrue(array[3]); // 'false' (yep, every non-empty string converts to true) + assertFalse(array[4]); // 0 + assertTrue(array[5]); // 1 + assertTrue(array[6]); // 0.4 + assertTrue(array[7]); // 0.6 + assertTrue(array[8]); // {} + assertTrue(array[9]); // [] + assertTrue(array[10]); // [false] + assertTrue(array[11]); // [true] + assertFalse(array[12]); // NaN + assertTrue(array[13]); // Infinity + assertFalse(array[14]); // null + assertFalse(array[15]); // undefined + } + + public static void assertListArray(List<?>[] array) { + assertEquals(2, array.length); + assertEquals(Arrays.asList("foo", "bar"), array[0]); + assertEquals(Arrays.asList("apple", "orange"), array[1]); + } + + public static void assertVarArg_42_17(Object... args) throws ScriptException { + assertEquals(2, args.length); + assertEquals(42, ((Number)args[0]).intValue()); + assertEquals(17, ((Number)args[1]).intValue()); + } + + public static void assertVarArg_array_17(Object... args) throws ScriptException { + assertEquals(2, args.length); + e.getBindings(ScriptContext.ENGINE_SCOPE).put("arr", args[0]); + assertTrue((Boolean)e.eval("arr instanceof Array && arr.length == 1 && arr[0] == 42")); + assertEquals(18, ((Number)args[1]).intValue()); + } + + public static void assertVarArg_function(Object... args) throws ScriptException { + assertEquals(1, args.length); + e.getBindings(ScriptContext.ENGINE_SCOPE).put("fn", args[0]); + assertEquals("Hello", e.eval("fn()")); + } + + + + public static void x(String y) { + assertEquals("abc", y); + } + public static void x(String[] y) { + assertTrue(Arrays.equals(new String[] { "foo", "bar"}, y)); + } +} diff --git a/test/src/jdk/nashorn/api/javaaccess/ConsStringTest.java b/test/src/jdk/nashorn/api/javaaccess/ConsStringTest.java new file mode 100644 index 00000000..1eadfb77 --- /dev/null +++ b/test/src/jdk/nashorn/api/javaaccess/ConsStringTest.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. + */ + +package jdk.nashorn.api.javaaccess; + +import static org.testng.AssertJUnit.assertEquals; + +import java.util.HashMap; +import java.util.Map; +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import jdk.nashorn.api.scripting.JSObject; +import org.testng.TestNG; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ConsStringTest { + private static ScriptEngine e = null; + + public static void main(final String[] args) { + TestNG.main(args); + } + + @BeforeClass + public static void setUpClass() throws ScriptException { + e = new ScriptEngineManager().getEngineByName("nashorn"); + } + + @AfterClass + public static void tearDownClass() { + e = null; + } + + @Test + public void testConsStringFlattening() throws ScriptException { + final Bindings b = e.getBindings(ScriptContext.ENGINE_SCOPE); + final Map<Object, Object> m = new HashMap<>(); + b.put("m", m); + e.eval("var x = 'f'; x += 'oo'; var y = 'b'; y += 'ar'; m.put(x, y)"); + assertEquals("bar", m.get("foo")); + } + + @Test + public void testConsStringFromMirror() throws ScriptException { + final Bindings b = e.getBindings(ScriptContext.ENGINE_SCOPE); + final Map<Object, Object> m = new HashMap<>(); + e.eval("var x = 'f'; x += 'oo'; var obj = {x: x};"); + assertEquals("foo", ((JSObject)b.get("obj")).getMember("x")); + } + + @Test + public void testArrayConsString() throws ScriptException { + final Bindings b = e.getBindings(ScriptContext.ENGINE_SCOPE); + final ArrayHolder h = new ArrayHolder(); + b.put("h", h); + e.eval("var x = 'f'; x += 'oo'; h.array = [x];"); + assertEquals(1, h.array.length); + assertEquals("foo", h.array[0]); + } + + + public static class ArrayHolder { + private Object[] array; + + public void setArray(Object[] array) { + this.array = array; + } + + public Object[] getArray() { + return array; + } + } +} diff --git a/test/src/jdk/nashorn/api/javaaccess/MethodAccessTest.java b/test/src/jdk/nashorn/api/javaaccess/MethodAccessTest.java index 14207b7d..4be66b81 100644 --- a/test/src/jdk/nashorn/api/javaaccess/MethodAccessTest.java +++ b/test/src/jdk/nashorn/api/javaaccess/MethodAccessTest.java @@ -412,7 +412,7 @@ public class MethodAccessTest { @Test public void accessMethodMixedWithEllipsis() throws ScriptException { - assertArrayEquals(new Object[] { "Hello", 10, true, -100500, 80 }, (Object[])e.eval("o.methodMixedWithEllipsis('Hello', 10, true, -100500,80.0);")); + assertArrayEquals(new Object[] { "Hello", 10, true, -100500, 80d }, (Object[])e.eval("o.methodMixedWithEllipsis('Hello', 10, true, -100500,80.0);")); assertArrayEquals(new Object[] { "Nashorn", 15 }, (Object[])e.eval("o.methodMixedWithEllipsis('Nashorn',15);")); } @@ -431,8 +431,8 @@ public class MethodAccessTest { @Test public void accessMethodDoubleVSintOverloaded() throws ScriptException { - assertEquals("int", e.eval("o.overloadedMethodDoubleVSint(0.0);")); - assertEquals("int", e.eval("o.overloadedMethodDoubleVSint(1000.0);")); + assertEquals("double", e.eval("o.overloadedMethodDoubleVSint(0.0);")); + assertEquals("double", e.eval("o.overloadedMethodDoubleVSint(1000.0);")); assertEquals("double", e.eval("o.overloadedMethodDoubleVSint(0.01);")); assertEquals("double", e.eval("o.overloadedMethodDoubleVSint(100.02);")); assertEquals("int", e.eval("o.overloadedMethodDoubleVSint(0);")); diff --git a/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java b/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java index acb57164..a574e6f6 100644 --- a/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java +++ b/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java @@ -46,7 +46,7 @@ import org.testng.annotations.Test; * JSObject implementations. */ public class PluggableJSObjectTest { - public static class MapWrapperObject extends JSObject { + public static class MapWrapperObject extends AbstractJSObject { private final HashMap<String, Object> map = new LinkedHashMap<>(); public HashMap<String, Object> getMap() { @@ -109,7 +109,7 @@ public class PluggableJSObjectTest { } } - public static class BufferObject extends JSObject { + public static class BufferObject extends AbstractJSObject { private final IntBuffer buf; public BufferObject(int size) { @@ -170,7 +170,7 @@ public class PluggableJSObjectTest { } } - public static class Adder extends JSObject { + public static class Adder extends AbstractJSObject { @Override public Object call(Object thiz, Object... args) { double res = 0.0; @@ -202,7 +202,7 @@ public class PluggableJSObjectTest { } } - public static class Factory extends JSObject { + public static class Factory extends AbstractJSObject { @Override public Object newObject(Object... args) { return new HashMap<Object, Object>(); diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java index 99207de0..55aacb34 100644 --- a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java +++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java @@ -523,6 +523,18 @@ public class ScriptEngineTest { assertEquals(sw.toString(), println("34 true hello")); } + @Test + public void scriptObjectAutoConversionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + e.eval("obj = { foo: 'hello' }"); + e.put("Window", e.eval("Packages.jdk.nashorn.api.scripting.Window")); + assertEquals(e.eval("Window.funcJSObject(obj)"), "hello"); + assertEquals(e.eval("Window.funcScriptObjectMirror(obj)"), "hello"); + assertEquals(e.eval("Window.funcMap(obj)"), "hello"); + assertEquals(e.eval("Window.funcJSObject(obj)"), "hello"); + } + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); // Returns String that would be the result of calling PrintWriter.println diff --git a/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java b/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java index c7b40c63..544f4ea7 100644 --- a/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java +++ b/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java @@ -26,6 +26,7 @@ package jdk.nashorn.api.scripting; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -129,7 +130,7 @@ public class ScriptObjectMirrorTest { final ScriptEngine e = m.getEngineByName("nashorn"); try { e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }"); - JSObject obj = (JSObject) e.get("obj"); + ScriptObjectMirror obj = (ScriptObjectMirror) e.get("obj"); // try basic get on existing properties if (!obj.getMember("bar").equals("hello")) { @@ -227,4 +228,28 @@ public class ScriptObjectMirrorTest { final Object newObj = ((ScriptObjectMirror)e2obj.getMember("foo")).newObject(); assertTrue(newObj instanceof ScriptObjectMirror); } + + @Test + public void conversionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final ScriptObjectMirror arr = (ScriptObjectMirror)e.eval("[33, 45, 23]"); + final int[] intArr = arr.to(int[].class); + assertEquals(intArr[0], 33); + assertEquals(intArr[1], 45); + assertEquals(intArr[2], 23); + + final List<?> list = arr.to(List.class); + assertEquals(list.get(0), 33); + assertEquals(list.get(1), 45); + assertEquals(list.get(2), 23); + + ScriptObjectMirror obj = (ScriptObjectMirror)e.eval( + "({ valueOf: function() { return 42 } })"); + assertEquals(Double.valueOf(42.0), obj.to(Double.class)); + + obj = (ScriptObjectMirror)e.eval( + "({ toString: function() { return 'foo' } })"); + assertEquals("foo", obj.to(String.class)); + } } diff --git a/test/src/jdk/nashorn/api/scripting/Window.java b/test/src/jdk/nashorn/api/scripting/Window.java index bde23941..510c5dae 100644 --- a/test/src/jdk/nashorn/api/scripting/Window.java +++ b/test/src/jdk/nashorn/api/scripting/Window.java @@ -25,6 +25,9 @@ package jdk.nashorn.api.scripting; +import java.util.Map; +import javax.script.Bindings; + public class Window { private String location = "http://localhost:8080/window"; @@ -63,4 +66,20 @@ public class Window { System.out.println("window.setTimeout: " + delay + ", code: " + code); return 0; } + + public static Object funcJSObject(final JSObject jsobj) { + return jsobj.getMember("foo"); + } + + public static Object funcScriptObjectMirror(final ScriptObjectMirror sobj) { + return sobj.get("foo"); + } + + public static Object funcMap(final Map<?,?> map) { + return map.get("foo"); + } + + public static Object funcBindings(final Bindings bindings) { + return bindings.get("foo"); + } } diff --git a/src/jdk/nashorn/internal/runtime/ScriptObjectListAdapter.java b/test/src/jdk/nashorn/internal/runtime/resources/load_test.js index 4fe2dc40..21672d61 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptObjectListAdapter.java +++ b/test/src/jdk/nashorn/internal/runtime/resources/load_test.js @@ -23,32 +23,6 @@ * questions. */ -package jdk.nashorn.internal.runtime; - -/** - * A ListAdapter that can wrap a ScriptObject. - */ -public final class ScriptObjectListAdapter extends ListAdapter { - /** - * Creates a new list wrapper for the specified ScriptObject. - * @param obj script the object to wrap - */ - public ScriptObjectListAdapter(final ScriptObject obj) { - super(obj); - } - - @Override - public int size() { - return JSType.toInt32(((ScriptObject)obj).getLength()); - } - - @Override - protected Object getAt(int index) { - return ((ScriptObject)obj).get(index); - } - - @Override - protected void setAt(int index, Object element) { - ((ScriptObject)obj).set(index, element, false); - } +function loadedFunc(arg) { + return arg.toUpperCase(); } |