Mercurial > hg > release > icedtea8-forest-3.0 > nashorn
changeset 1733:33ee43fc8148
Merge
author | asaha |
---|---|
date | Wed, 02 Dec 2015 23:08:29 -0800 |
parents | f76d7971db2b (diff) dbdadc4378e8 (current diff) |
children | 74ab3a7f55dd 540d87751301 |
files | .hgtags |
diffstat | 239 files changed, 7247 insertions(+), 3261 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Tue Dec 01 22:55:46 2015 -0800 +++ b/.hgtags Wed Dec 02 23:08:29 2015 -0800 @@ -480,7 +480,32 @@ 9a3b86240761e602469c41bd720c7791997253e6 jdk8u66-b15 c0ce5c308f5e2c42ac0d2e7367355663312a3128 jdk8u66-b16 3cc16ff2735c6818b68fdf161ddbcc89a5b4db1a jdk8u66-b17 +39bfb9eb75dcb2176a87ac3b025a665c41244e54 jdk8u66-b18 3e08bc604b2166b251833e522892ffcfd22b4b88 jdk8u66-b31 52d1be12498e2390cf0581040ce6f4ab7258b498 jdk8u66-b32 3629a9cd6627e0b9e1a0735be214b20fd2f9a743 jdk8u66-b33 a33c509181cef86d6799d1e6ec389de2990bc16c jdk8u66-b34 +a7ac4116ee88aa86fec5ac66901302e11f578172 jdk8u71-b00 +b8987f466586180cb4dc387f5fa290bb4cf34983 jdk8u71-b01 +a8fd49cb76fafc704b746a98bd18647802674d2d jdk8u71-b02 +12745568f9fb73a452d85b2388fef29909dafc24 jdk8u71-b03 +92b196168ef13e159e311e864c08d70e2c23dec6 jdk8u71-b04 +c870d760ca23b0f2c21813946eeb36563eeea8d4 jdk8u71-b05 +f76ef9364e15663fe29cda34f7d95cbab224ff36 jdk8u71-b06 +c3c1c032ff27f5967edcd595eeca23a005c8a7fe jdk8u71-b07 +0eb2b5f656d5d738957259d5f3f7982dc5b2a864 jdk8u71-b08 +79a56b0e79aa18f18c930236157458d7893537db jdk8u71-b09 +dcb78b4ac30e7ace9db4c53450b54226cc753505 jdk8u71-b10 +8d7818ff955da951165530e2c76154145a9f6098 jdk8u71-b11 +667e020da337e453eac8ecb9285c9b34a47e25fd jdk8u72-b00 +a105e7b0eff93895b82e3d372a63df4311d79821 jdk8u72-b01 +f7c3d65076a0c0bf1bb4ea633b4ea0af9ffb12fe jdk8u72-b02 +c47a6341ae31e394f6748acbeebf68de3ae285ff jdk8u72-b03 +4592976d8655d9ec0ad364901ae07c79813c8cd4 jdk8u72-b04 +0c87ce7cc7d01462476bce07945dc1646b4f08d1 jdk8u72-b05 +a2591c7bcb92f5bb2bd53b46d5a2c97be367f7fe jdk8u72-b06 +619a1ac46fc46394f524f7aa587f6c70d3d7ab52 jdk8u72-b07 +bcc30e9810fe0b3f19556dcfce0cdd801dbfe463 jdk8u72-b08 +1f69895e259bb6cbb4a5b2aead1c7bb341a58ec2 jdk8u72-b09 +390714be5c40a14a43f136a6dc2d15f38251a2d1 jdk8u72-b10 +e7a962a13695d244f31721ca322968e70c41f623 jdk8u72-b11
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java Wed Dec 02 23:08:29 2015 -0800 @@ -54,10 +54,9 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_DESC; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE; @@ -282,9 +281,9 @@ assert specs != null; if (!specs.isEmpty()) { mi.memberInfoArray(className, specs); - mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC); + mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC); } else { - mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC); + mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_DESC); } if (arityFound) {
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Wed Dec 02 23:08:29 2015 -0800 @@ -38,9 +38,8 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC3; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC4; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_INIT_DESC3; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_INIT_DESC4; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE; @@ -55,7 +54,7 @@ import jdk.internal.org.objectweb.asm.Handle; /** - * This class generates constructor class for a @ClassInfo annotated class. + * This class generates constructor class for a @ScriptClass annotated class. * */ public class ConstructorGenerator extends ClassGenerator { @@ -75,8 +74,8 @@ } byte[] getClassBytes() { - // new class extensing from ScriptObject - final String superClass = (constructor != null)? SCRIPTFUNCTIONIMPL_TYPE : SCRIPTOBJECT_TYPE; + // new class extending from ScriptObject + final String superClass = (constructor != null)? SCRIPTFUNCTION_TYPE : SCRIPTOBJECT_TYPE; cw.visit(V1_7, ACC_FINAL, className, null, superClass, null); if (memberCount > 0) { // add fields @@ -182,8 +181,8 @@ loadMap(mi); } else { // call Function.<init> - superClass = SCRIPTFUNCTIONIMPL_TYPE; - superDesc = (memberCount > 0) ? SCRIPTFUNCTIONIMPL_INIT_DESC4 : SCRIPTFUNCTIONIMPL_INIT_DESC3; + superClass = SCRIPTFUNCTION_TYPE; + superDesc = (memberCount > 0) ? SCRIPTFUNCTION_INIT_DESC4 : SCRIPTFUNCTION_INIT_DESC3; mi.loadLiteral(constructor.getName()); mi.visitLdcInsn(new Handle(H_INVOKESTATIC, scriptClassInfo.getJavaName(), constructor.getJavaName(), constructor.getJavaDesc())); loadMap(mi);
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java Wed Dec 02 23:08:29 2015 -0800 @@ -161,7 +161,7 @@ } /** - * Tag something as optimitic builtin or not + * Tag something as optimistic builtin or not * @param isOptimistic boolean, true if builtin constructor */ public void setIsOptimistic(final boolean isOptimistic) { @@ -178,7 +178,7 @@ } /** - * Set thre SpecializedFunction link logic class for specializations, i.e. optimistic + * Set the SpecializedFunction link logic class for specializations, i.e. optimistic * builtins * @param linkLogicClass link logic class */
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Wed Dec 02 23:08:29 2015 -0800 @@ -42,7 +42,7 @@ import java.io.IOException; /** - * This class generates prototype class for a @ClassInfo annotated class. + * This class generates prototype class for a @ScriptClass annotated class. * */ public class PrototypeGenerator extends ClassGenerator { @@ -57,7 +57,7 @@ } byte[] getClassBytes() { - // new class extensing from ScriptObject + // new class extending from ScriptObject cw.visit(V1_7, ACC_FINAL | ACC_SUPER, className, null, PROTOTYPEOBJECT_TYPE, null); if (memberCount > 0) { // add fields @@ -155,7 +155,7 @@ */ public static void main(final String[] args) throws IOException { if (args.length != 1) { - System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>"); + System.err.println("Usage: " + PrototypeGenerator.class.getName() + " <class>"); System.exit(1); }
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java Wed Dec 02 23:08:29 2015 -0800 @@ -48,7 +48,7 @@ * */ public final class ScriptClassInfo { - // descriptots for various annotations + // descriptors for various annotations static final String SCRIPT_CLASS_ANNO_DESC = Type.getDescriptor(ScriptClass.class); static final String CONSTRUCTOR_ANNO_DESC = Type.getDescriptor(Constructor.class); static final String FUNCTION_ANNO_DESC = Type.getDescriptor(Function.class); @@ -140,7 +140,7 @@ } boolean isPrototypeNeeded() { - // Prototype class generation is needed if we have atleast one + // Prototype class generation is needed if we have at least one // prototype property or @Constructor defined in the class. for (final MemberInfo memInfo : members) { if (memInfo.getWhere() == Where.PROTOTYPE || memInfo.isConstructor()) {
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java Wed Dec 02 23:08:29 2015 -0800 @@ -118,7 +118,7 @@ addScriptMember(memInfo); return new AnnotationVisitor(Opcodes.ASM4, delegateAV) { - // These could be "null" if values are not suppiled, + // These could be "null" if values are not supplied, // in which case we have to use the default values. private String name; private Integer attributes; @@ -194,7 +194,7 @@ final MemberInfo memInfo = new MemberInfo(); - //annokind == e.g. GETTER or SPECIALIZED_FUNCTION + // annoKind == GETTER or SPECIALIZED_FUNCTION memInfo.setKind(annoKind); memInfo.setJavaName(methodName); memInfo.setJavaDesc(methodDesc); @@ -203,7 +203,7 @@ addScriptMember(memInfo); return new AnnotationVisitor(Opcodes.ASM4, delegateAV) { - // These could be "null" if values are not suppiled, + // These could be "null" if values are not supplied, // in which case we have to use the default values. private String name; private Integer attributes;
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java Wed Dec 02 23:08:29 2015 -0800 @@ -65,7 +65,6 @@ * 2) add "Map" type static field named "$map". * 3) add static initializer block to initialize map. */ - public class ScriptClassInstrumentor extends ClassVisitor { private final ScriptClassInfo scriptClassInfo; private final int memberCount; @@ -267,7 +266,7 @@ */ public static void main(final String[] args) throws IOException { if (args.length != 1) { - System.err.println("Usage: " + ScriptClassInfoCollector.class.getName() + " <class>"); + System.err.println("Usage: " + ScriptClassInstrumentor.class.getName() + " <class>"); System.exit(1); }
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Tue Dec 01 22:55:46 2015 -0800 +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Wed Dec 02 23:08:29 2015 -0800 @@ -31,10 +31,9 @@ import java.util.Collections; import java.util.List; import jdk.internal.org.objectweb.asm.Type; -import jdk.nashorn.internal.objects.PrototypeObject; -import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.runtime.AccessorProperty; import jdk.nashorn.internal.runtime.PropertyMap; +import jdk.nashorn.internal.runtime.PrototypeObject; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.Specialization; @@ -88,7 +87,6 @@ static final Type TYPE_PROPERTYMAP = Type.getType(PropertyMap.class); static final Type TYPE_PROTOTYPEOBJECT = Type.getType(PrototypeObject.class); static final Type TYPE_SCRIPTFUNCTION = Type.getType(ScriptFunction.class); - static final Type TYPE_SCRIPTFUNCTIONIMPL = Type.getType(ScriptFunctionImpl.class); static final Type TYPE_SCRIPTOBJECT = Type.getType(ScriptObject.class); static final String PROTOTYPE_SUFFIX = "$Prototype"; @@ -122,17 +120,14 @@ static final String SCRIPTFUNCTION_SETARITY_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE); static final String SCRIPTFUNCTION_SETPROTOTYPE = "setPrototype"; static final String SCRIPTFUNCTION_SETPROTOTYPE_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT); - - // ScriptFunctionImpl - static final String SCRIPTFUNCTIONIMPL_TYPE = TYPE_SCRIPTFUNCTIONIMPL.getInternalName(); - static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION = "makeFunction"; - static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC = + static final String SCRIPTFUNCTION_CREATEBUILTIN = "createBuiltin"; + static final String SCRIPTFUNCTION_CREATEBUILTIN_DESC = Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE); - static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC = + static final String SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC = Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY); - static final String SCRIPTFUNCTIONIMPL_INIT_DESC3 = + static final String SCRIPTFUNCTION_INIT_DESC3 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY); - static final String SCRIPTFUNCTIONIMPL_INIT_DESC4 = + static final String SCRIPTFUNCTION_INIT_DESC4 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_SPECIALIZATION_ARRAY); // ScriptObject
--- a/make/build.xml Tue Dec 01 22:55:46 2015 -0800 +++ b/make/build.xml Wed Dec 02 23:08:29 2015 -0800 @@ -209,10 +209,11 @@ </jar> </target> + <!-- generate javadoc for all Nashorn and ASM classes --> <target name="javadoc" depends="jar"> <javadoc destdir="${dist.javadoc.dir}" use="yes" overview="${src.dir}/overview.html" extdirs="${nashorn.ext.path}" windowtitle="${nashorn.product.name} ${nashorn.version}" - additionalparam="-quiet" failonerror="true"> + additionalparam="-quiet" failonerror="true" useexternalfile="true"> <classpath> <pathelement location="${build.classes.dir}"/> </classpath> @@ -226,10 +227,23 @@ </javadoc> </target> + <!-- generate javadoc for Nashorn classes --> + <target name="javadocnh" depends="jar"> + <javadoc destdir="${dist.javadoc.dir}" use="yes" overview="${src.dir}/overview.html" + extdirs="${nashorn.ext.path}" windowtitle="${nashorn.product.name} ${nashorn.version}" + additionalparam="-quiet" failonerror="true" useexternalfile="true"> + <classpath> + <pathelement location="${build.classes.dir}"/> + </classpath> + <fileset dir="${src.dir}" includes="**/*.java"/> + <link href="http://docs.oracle.com/javase/8/docs/api/"/> + </javadoc> + </target> + <!-- generate javadoc only for nashorn extension api classes --> <target name="javadocapi" depends="jar"> <javadoc destdir="${dist.javadoc.dir}" use="yes" extdirs="${nashorn.ext.path}" - windowtitle="${nashorn.product.name}" additionalparam="-quiet" failonerror="true"> + windowtitle="${nashorn.product.name}" additionalparam="-quiet" failonerror="true" useexternalfile="true"> <classpath> <pathelement location="${build.classes.dir}"/> </classpath> @@ -238,7 +252,6 @@ </javadoc> </target> - <!-- generate shell.html for shell tool documentation --> <target name="shelldoc" depends="jar"> <java classname="${nashorn.shell.tool}" dir="${basedir}" output="${dist.dir}/shell.html" failonerror="true" fork="true"> @@ -267,8 +280,8 @@ <javac srcdir="${test.src.dir}" destdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" - source="${javac.source}" - target="${javac.target}" + source="${test.javac.source}" + target="${test.javac.target}" debug="${javac.debug}" encoding="${javac.encoding}" includeantruntime="false" fork="true"> @@ -460,7 +473,7 @@ </testng> </target> - <target name="test" depends="get-testng, javadoc, test-pessimistic, test-optimistic"/> + <target name="test" depends="get-testng, javadocnh, test-pessimistic, test-optimistic"/> <target name="test-optimistic" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available"> <echo message="Running test suite in OPTIMISTIC mode..."/>
--- a/make/project.properties Tue Dec 01 22:55:46 2015 -0800 +++ b/make/project.properties Wed Dec 02 23:08:29 2015 -0800 @@ -30,6 +30,8 @@ build.compiler=modern javac.source=1.7 javac.target=1.7 +test.javac.source=1.8 +test.javac.target=1.8 # nashorn version information nashorn.version=0.1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/EvalWithArbitraryThis.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import javax.script.*; +import jdk.nashorn.api.scripting.*; + +// Simple nashorn demo that evals a script with arbitrary script +// object bound as "this" for the evaluated script. + +public class EvalWithArbitraryThis { + public static void main(String[] args) throws Exception { + ScriptEngineManager m = new ScriptEngineManager(); + ScriptEngine e = m.getEngineByName("nashorn"); + Object sobj = e.eval("( { foo: 343, bar: 'hello' } )"); + + // "this" bound to sobj in this eval. + // so it prints sobj.foo and sobj.bar. + ((ScriptObjectMirror)sobj).eval("print(this.foo); print(this.bar)"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/LambdaAsFunc.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import javax.script.*; +import java.util.function.*; + +// example demonstrating that arbitrary Lambda can be called as function from script + +public class LambdaAsFunc { + public static void main(String[] args) throws Exception { + ScriptEngineManager m = new ScriptEngineManager(); + ScriptEngine e = m.getEngineByName("nashorn"); + + // expose a lambda as top-level script function + e.put("upper", (Function<String, String>) String::toUpperCase); + + // call lambda as though it is a normal script function + System.out.println(e.eval("upper('hello')")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/Main.asm Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Simple sample to demonstrate openjdk asmtools assembler with +// nashorn dynalink linker in a invokedynamic instruction. +// +// To assemble this file, use the following command: +// +// java -cp <asmtools.jar> org.openjdk.asmtools.Main jasm Main.asm +// +// See also: https://wiki.openjdk.java.net/display/CodeTools/asmtools +// +// NOTE: Uses nashorn internals and so *may* break with later nashorn! + +super public class Main + version 52:0 +{ + + +public Method "<init>":"()V" + stack 1 locals 1 +{ + aload_0; + invokespecial Method java/lang/Object."<init>":"()V"; + return; +} + +public static Method main:"([Ljava/lang/String;)V" + stack 2 locals 2 +{ + // List l = new ArrayList(); + new class java/util/ArrayList; + dup; + invokespecial Method java/util/ArrayList."<init>":"()V"; + astore_1; + aload_1; + + // l.add("hello"); + ldc String "hello"; + invokeinterface InterfaceMethod java/util/List.add:"(Ljava/lang/Object;)Z", 2; + pop; + + // l.add("world"); + aload_1; + ldc String "world"; + invokeinterface InterfaceMethod java/util/List.add:"(Ljava/lang/Object;)Z", 2; + pop; + + // printLength(l); + aload_1; + invokestatic Method printLength:"(Ljava/lang/Object;)V"; + + // printLength(args); // args is argument of main method + aload_0; + invokestatic Method printLength:"(Ljava/lang/Object;)V"; + return; +} + +private static Method printLength:"(Ljava/lang/Object;)V" + stack 2 locals 1 +{ + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + aload_0; + + // Using nashorn embedded dynalink linker with the following invokedynamic + // 'length' property on a bean - arrays, lists supported + + invokedynamic InvokeDynamic REF_invokeStatic:jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;":"dyn:getProp|getElem|getMethod:length":"(Ljava/lang/Object;)Ljava/lang/Object;" int 0; + + // print 'length' value + invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/Object;)V"; + return; +} + +} // end Class Main
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/PrintToString.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import javax.script.*; +import java.io.StringWriter; + +// simple example demonstrating capturing of "print" output +// from script execution as a String via script engine API. + +public class PrintToString { + public static void main(String[] args) throws ScriptException { + ScriptEngineManager m = new ScriptEngineManager(); + ScriptEngine e = m.getEngineByName("nashorn"); + StringWriter sw = new StringWriter(); + e.getContext().setWriter(sw); + e.eval("print('hello world')"); + System.out.println(sw.toString()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/array_removeif.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Sample for Collection.removeIf and Java.to +// See http://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate- + +var langs = [ + "java", "javascript", + "C", "C#", "C++", + "Erlang", "Haskell" +]; + +// convert script array to a Java List +function asList(array) { + return Java.to(array, java.util.List); +} + +// remove elements that satisfy a predicate +// using Collection.removeIf(Predicate) +asList(langs).removeIf( + function(s) s.startsWith("C")); + +// print modified array +print(langs);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/bind_on_java.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// sample to demonstrate calling Function.prototype.bind on Java methods. + +var DoubleStream = java.util.stream.DoubleStream; +var r = new java.util.Random(); + +// bind "this" for nextGaussian method of Random class +var next = Function.bind.call(r.nextGaussian, r); + +// next is used as no argument "function" +DoubleStream + .generate(function() next()) + .limit(100) + .forEach(print);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/call_bind_java.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Sample demonstrating calling Java methods like normal +// functions. Bound instance methods, static methods can be +// called like normal script functions. + +// Java types used +var Files = Java.type("java.nio.file.Files"); +var FileSystems = Java.type("java.nio.file.FileSystems"); +var System = Java.type("java.lang.System"); + +var fs = FileSystems.default; +var bind = Function.prototype.bind; + +// Java methods as "functions" + +// Java method with bound "this" +var Path = bind.call(fs.getPath, fs); +// Java static method +var Property = System.getProperty; + +// call Java static methods and bound instance methods +// like normal script functions. + +var root = Path("/"); +// print URI for root dir +print(root.toUri()); +var home = Path(Property("user.home")); +// list home directory +Files.walk(home).forEach(print);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/check_nashorn.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// check if script is being run using nashorn script engine +function isNashorn() { + try { + // "engine" variable is of type javax.script.ScriptEngine is defined + // by nashorn jsr-223 engine. Check the name of the engine from + // javax.script.ScriptEngineFactory associated + + return engine.factory.engineName.contains("Nashorn"); + } catch (e) { + // if engine or any of the properties are undefined + // then script is not being run using nashorn. + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/datetime.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// converting b/w JavaScript Date and Java LocalDateTime + +// JavaScript Date with current time +var d = new Date(); +print(d); + +// Java 8 java.time classes used +var Instant = java.time.Instant; +var LocalDateTime = java.time.LocalDateTime; +var ZoneId = java.time.ZoneId; + +// Date.prototype.getTime + +// getTime() method returns the numeric value corresponding to the time +// for the specified date according to universal time. The value returned +// by the getTime() method is the number of milliseconds since 1 January 1970 00:00:00 UTC. +// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime + +// Java Instant.ofEpochMilli to convert time in milliseconds to Instant object +// https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html#ofEpochMilli-long- + +var instant = Instant.ofEpochMilli(d.getTime()); + +// Instant to LocalDateTime using LocalDateTime.ofInstant +// https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#ofInstant-java.time.Instant-java.time.ZoneId- + +var ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); +print(ldt); + +// converting a LocalDateTime to JavaScript Date +// convert LocalDateTime to Instant first +// https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#atZone-java.time.ZoneId- + +var instant = ldt.atZone(ZoneId.systemDefault()).toInstant(); + +// instant to to epoch milliseconds +// https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html#toEpochMilli-- +// and then to JavaScript Date from time in milliseconds +// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date + +var d1 = new Date(instant.toEpochMilli()); +print(d1);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/defaults.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// print default methods of a Java class + +if (arguments.length == 0) { + print("Usage: jjs defaults.js -- <java class name>"); + exit(1); +} + +var jclass = Java.type(arguments[0]).class; +for each (m in jclass.methods) { + // http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#isDefault-- + if (m.default) print(m); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/exceptionswallow.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,136 @@ +#// Usage: jjs exceptionswallow.js -- <directory> + +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This example demonstrates Java subclassing by Java.extend +// and javac Compiler and Tree API. This example looks for +// empty catch blocks ("exception swallow") and reports those. + +if (arguments.length == 0) { + print("Usage: jjs exceptionswallow.js -- <directory>"); + exit(1); +} + +// Java types used +var File = Java.type("java.io.File"); +var Files = Java.type("java.nio.file.Files"); +var StringArray = Java.type("java.lang.String[]"); +var ToolProvider = Java.type("javax.tools.ToolProvider"); +var Tree = Java.type("com.sun.source.tree.Tree"); +var EmptyStatementTree = Java.type("com.sun.source.tree.EmptyStatementTree"); +var Trees = Java.type("com.sun.source.util.Trees"); +var TreeScanner = Java.type("com.sun.source.util.TreeScanner"); + +// printEmptyCatch + +function printEmptyCatch() { + // get the system compiler tool + var compiler = ToolProvider.systemJavaCompiler; + // get standard file manager + var fileMgr = compiler.getStandardFileManager(null, null, null); + // Using Java.to convert script array (arguments) to a Java String[] + var compUnits = fileMgr.getJavaFileObjects( + Java.to(arguments, StringArray)); + // create a new compilation task + var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); + + // SourcePositions object to get positions of AST nodes + var sourcePositions = Trees.instance(task).sourcePositions; + + // subclass SimpleTreeVisitor - to print empty catch + var EmptyCatchFinder = Java.extend(TreeScanner); + + function hasOnlyEmptyStats(stats) { + var itr = stats.iterator(); + while (itr.hasNext()) { + if (! (itr.next() instanceof EmptyStatementTree)) { + return false; + } + } + + return true; + } + + var visitor = new EmptyCatchFinder() { + // current CompilationUnitTree + compUnit: null, + // current LineMap (pos -> line, column) + lineMap: null, + // current compilation unit's file name + fileName: null, + + // overrides of TreeScanner methods + + visitCompilationUnit: function(node, p) { + // capture info about current Compilation unit + this.compUnit = node; + this.lineMap = node.lineMap; + this.fileName = node.sourceFile.name; + + // Using Java.super API to call super class method here + return Java.super(visitor).visitCompilationUnit(node, p); + }, + + visitCatch: function (node, p) { + var stats = node.block.statements; + if (stats.empty || hasOnlyEmptyStats(stats)) { + // print information on this empty catch + var pos = sourcePositions.getStartPosition(this.compUnit, node); + var line = this.lineMap.getLineNumber(pos); + var col = this.lineMap.getColumnNumber(pos); + print("Exception swallow" + " @ " + this.fileName + ":" + line + ":" + col); + // print(node); + } + } + } + + for each (var cu in task.parse()) { + cu.accept(visitor, null); + } +} + +// for each ".java" file in directory (recursively) and check it! +function main(dir) { + Files.walk(dir.toPath()). + forEach(function(p) { + var name = p.toFile().absolutePath; + if (name.endsWith(".java")) { + try { + printEmptyCatch(p.toFile().getAbsolutePath()); + } catch (e) { + print(e); + } + } + }); +} + +main(new File(arguments[0]));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/find_max_lines.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Find the file with maximum number of lines + +var Files = Java.type("java.nio.file.Files"); +var Integer = Java.type("java.lang.Integer"); +var Paths = Java.type("java.nio.file.Paths"); + +var file = arguments.length == 0? "." : arguments[0]; +var ext = arguments.length > 1? arguments[1] : ".java"; +var path = Paths.get(file); + +// return number of lines in given Path (that represents a file) +function lines(p) { + var strm = Files.lines(p); + try { + return strm.count(); + } finally { + strm.close(); + } +} + +// walk files, map to file and lines, find the max +var obj = Files.find(path, Integer.MAX_VALUE, + function(p, a) !p.toFile().isDirectory() && p.toString().endsWith(ext)). +map(function(p) ({ path : p, lines: lines(p) })). +max(function(x, y) x.lines - y.lines).get(); + +// print path and lines of the max line file +print(obj.path, obj.lines);
--- a/samples/find_nonfinals2.js Tue Dec 01 22:55:46 2015 -0800 +++ b/samples/find_nonfinals2.js Wed Dec 02 23:08:29 2015 -0800 @@ -43,7 +43,6 @@ // Java types used var File = Java.type("java.io.File"); var Files = Java.type("java.nio.file.Files"); -var FileVisitOption = Java.type("java.nio.file.FileVisitOption"); var StringArray = Java.type("java.lang.String[]"); var ToolProvider = Java.type("javax.tools.ToolProvider"); var Tree = Java.type("com.sun.source.tree.Tree"); @@ -106,7 +105,7 @@ // for each ".java" file in directory (recursively). function main(dir) { var totalCount = 0; - Files.walk(dir.toPath(), FileVisitOption.FOLLOW_LINKS). + Files.walk(dir.toPath()). forEach(function(p) { var name = p.toFile().absolutePath; if (name.endsWith(".java")) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/fixed_point.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Simple sample demonstrating "fixed point" computation with Streams + +// See also https://mitpress.mit.edu/sicp/chapter1/node21.html#secprocgeneralmethods +var Stream = Java.type("java.util.stream.Stream"); + +// generic fixed point procedure +function fixed_point(f, init_guess) { + var tolerance = 0.00001; + function close_enough(v1, v2) Math.abs(v1 - v2) < tolerance; + + var prev; + return Stream.iterate(init_guess, f) + .filter(function(x) { + try { + return prev == undefined? false : close_enough(prev, x); + } finally { + prev = x; + } + }) + .findFirst() + .get(); +} + +// solution to x = cos(x) +print(fixed_point(Math.cos, 1.0)) + +// solution to x = sin(x) + cos(x) +print(fixed_point(function(x) Math.sin(x) + Math.cos(x), 1.0)); + +// square root by Newton's method +// http://en.wikipedia.org/wiki/Newton's_method +function sqrt(n) + fixed_point(function(x) (x + n/x) / 2, 2.0); + +print(sqrt(2)) +print(sqrt(3)) + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/importstatic.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Java like "import static class_name.*" for nashorn + +function importStatic(clazz) { + // make sure the argument is a Java type + if (! Java.isType(clazz)) { + throw new TypeError(clazz + " is not a Java type"); + } + + // bind properties of clazz to an object + var obj = Object.bindProperties({}, clazz); + + // copy properties to global to "import" those + for (var i in obj) { + this[i] = obj[i]; + } +} + +importStatic(java.time.Instant); +print(now()); +print(ofEpochSecond(1)); +print(parse("2007-12-03T10:15:30.00Z"));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/javabind.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// bind on a Java method + +// #javascript "bind" function +var bind = Function.prototype.bind; + +// Java console object +var console = java.lang.System.console(); + +// arguments "this" and prompt string of Console.readLine method are bound +var readName = bind.call(console.readLine, console, "Your name: "); + +// Now call it like a function that takes no arguments! +print("Hello,", readName());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/javaconstructorbind.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// bind on a Java constructor + +// See Function.prototype.bind: +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind +var bind = Function.prototype.bind; + +var URL = Java.type("java.net.URL"); + +// get the constructor that accepts URL, String parameters. +// constructor signatures are properties of type object. +var newURL = URL["(URL, String)"]; + +// bind "context" URL parameter. +var TwitterURL = bind.call(newURL, null, new URL('https://www.twitter.com')); + +// now you can create context relative URLs using the bound constructor +print(new TwitterURL("sundararajan_a")); + +// read the URL content and print (optional part) + +var BufferedReader = Java.type("java.io.BufferedReader"); +var InputStreamReader = Java.type("java.io.InputStreamReader"); + +// function to retrieve text content of the given URL +function readTextFromURL(url) { + var str = ''; + var u = new URL(url); + var reader = new BufferedReader( + new InputStreamReader(u.openStream())); + try { + reader.lines().forEach(function(x) str += x); + return str; + } finally { + reader.close(); + } +} + +print(readTextFromURL(new TwitterURL("sundararajan_a")));
--- a/samples/javafoovars.js Tue Dec 01 22:55:46 2015 -0800 +++ b/samples/javafoovars.js Wed Dec 02 23:08:29 2015 -0800 @@ -42,7 +42,6 @@ // Java types used var File = Java.type("java.io.File"); var Files = Java.type("java.nio.file.Files"); -var FileVisitOption = Java.type("java.nio.file.FileVisitOption"); var StringArray = Java.type("java.lang.String[]"); var ToolProvider = Java.type("javax.tools.ToolProvider"); var Tree = Java.type("com.sun.source.tree.Tree"); @@ -81,7 +80,7 @@ // for each ".java" file in directory (recursively) count "foo". function main(dir) { var totalCount = 0; - Files.walk(dir.toPath(), FileVisitOption.FOLLOW_LINKS). + Files.walk(dir.toPath()). forEach(function(p) { var name = p.toFile().absolutePath; if (name.endsWith(".java")) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/mapwith.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Using a Java map with Javascript "with" statement + +var map = new java.util.HashMap(); +map.put("foo", 34); +map.put("bar", "hello"); + +var obj = { + __noSuchProperty__: function(name) { + return map.get(name); + } +}; + +with(obj) { + print(foo); + print(bar); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/mothers_day.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,56 @@ +# compute Mothers day of the given the year + +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// print "Mother's day" of the given year using Java Time API + +if (arguments.length == 0) { + print("Usage: jjs mothers_day.js -- year"); + exit(1); +} + +// java classes used +var DayOfWeek = java.time.DayOfWeek; +var LocalDate = java.time.LocalDate; +var TemporalAdjusters = java.time.temporal.TemporalAdjusters; + +var year = parseInt(arguments[0]); + +// See: https://en.wikipedia.org/?title=Mother%27s_Day +// We need second Sunday of May. Make April 30 of the given +// year adjust and adjust to next Sunday from there twice. To adjust a Date +// we use a common TemporalAdjuster provided in JDK8. +// https://docs.oracle.com/javase/8/docs/api/java/time/temporal/TemporalAdjusters.html + +print(LocalDate.of(year, 4, 30). + with(TemporalAdjusters.next(DayOfWeek.SUNDAY)). + with(TemporalAdjusters.next(DayOfWeek.SUNDAY)));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/passwordgen.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Simple password generator using SecureRandom + stream API + +// accept optional password length from command line +var len = arguments.length? parseInt(arguments[0]) : 8; + +// Java types used +var Collectors = Java.type("java.util.stream.Collectors"); +var SecureRandom = Java.type("java.security.SecureRandom"); + +// allowed password chars +var chars = + "!@#$%ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +// generated and print password +print(new SecureRandom(). + ints(len, 0, chars.length). + mapToObj(function(i) chars[i]). + collect(Collectors.joining("")));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/print_symlinks.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Print all symbolic links in user's home directory + +// JavaImporter and "with" to simulate "import" statements in Java. +// See https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions + +with (new JavaImporter(java.nio.file, java.lang)) { + + var userDir = System.getProperty("user.dir") + var home = FileSystems.default.getPath(userDir); + + // JS functions can be passed where Java lambdas are required. + // Also, using "Expression closure" extension here. + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Expression_Closures + + Files.walk(home). + filter(function(p) Files.isSymbolicLink(p)). + forEach(function(p) + print(p, '->', Files.readSymbolicLink(p))); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/resourcetrysuggester.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,156 @@ +#// Usage: jjs resourcetrysuggester.js -- <directory> + +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This example demonstrates Java subclassing by Java.extend +// and javac Compiler and Tree API. This example looks for +// finally clauses with "close" call and suggests "resource try"! + +if (arguments.length == 0) { + print("Usage: jjs resourcetrysuggester.js -- <directory>"); + exit(1); +} + +// Java types used +var ExpressionStatementTree = Java.type("com.sun.source.tree.ExpressionStatementTree"); +var File = Java.type("java.io.File"); +var Files = Java.type("java.nio.file.Files"); +var MemberSelectTree = Java.type("com.sun.source.tree.MemberSelectTree"); +var MethodInvocationTree = Java.type("com.sun.source.tree.MethodInvocationTree"); +var StringArray = Java.type("java.lang.String[]"); +var ToolProvider = Java.type("javax.tools.ToolProvider"); +var Tree = Java.type("com.sun.source.tree.Tree"); +var Trees = Java.type("com.sun.source.util.Trees"); +var TreeScanner = Java.type("com.sun.source.util.TreeScanner"); + +// resourceTrySuggestions + +function resourceTrySuggestions() { + // get the system compiler tool + var compiler = ToolProvider.systemJavaCompiler; + // get standard file manager + var fileMgr = compiler.getStandardFileManager(null, null, null); + // Using Java.to convert script array (arguments) to a Java String[] + var compUnits = fileMgr.getJavaFileObjects( + Java.to(arguments, StringArray)); + // create a new compilation task + var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); + + // SourcePositions object to get positions of AST nodes + var sourcePositions = Trees.instance(task).sourcePositions; + + // subclass SimpleTreeVisitor - to print resource try suggestions + var ResourceTrySuggester = Java.extend(TreeScanner); + + function hasOnlyEmptyStats(stats) { + var itr = stats.iterator(); + while (itr.hasNext()) { + if (! (itr.next() instanceof EmptyStatementTree)) { + return false; + } + } + + return true; + } + + // does the given statement list has an expression statement which + // calls "close" method (don't worry about types - just crude one will do) + function hasCloseCall(stats) { + var itr = stats.iterator(); + while (itr.hasNext()) { + var stat = itr.next(); + if (stat instanceof ExpressionStatementTree) { + var expr = stat.expression; + if (expr instanceof MethodInvocationTree) { + var method = expr.methodSelect; + if (method instanceof MemberSelectTree) { + return method.identifier.toString().equals("close"); + } + } + } + } + return false; + } + + var visitor = new ResourceTrySuggester() { + // current CompilationUnitTree + compUnit: null, + // current LineMap (pos -> line, column) + lineMap: null, + // current compilation unit's file name + fileName: null, + + // overrides of TreeScanner methods + + visitCompilationUnit: function(node, p) { + // capture info about current Compilation unit + this.compUnit = node; + this.lineMap = node.lineMap; + this.fileName = node.sourceFile.name; + + // Using Java.super API to call super class method here + return Java.super(visitor).visitCompilationUnit(node, p); + }, + + visitTry: function (node, p) { + var finallyBlk = node.finallyBlock; + if (finallyBlk != null && hasCloseCall(finallyBlk.statements)) { + var pos = sourcePositions.getStartPosition(this.compUnit, node); + var line = this.lineMap.getLineNumber(pos); + var col = this.lineMap.getColumnNumber(pos); + print("Consider resource try statement " + " @ " + this.fileName + ":" + line + ":" + col); + // print(node); + } + } + } + + for each (var cu in task.parse()) { + cu.accept(visitor, null); + } +} + +// for each ".java" file in directory (recursively) and check it! +function main(dir) { + Files.walk(dir.toPath()). + forEach(function(p) { + var name = p.toFile().absolutePath; + if (name.endsWith(".java")) { + try { + resourceTrySuggestions(p.toFile().getAbsolutePath()); + } catch (e) { + print(e); + } + } + }); +} + +main(new File(arguments[0]));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/sort_by_java8.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Simple sorting with Java8 APIs + +// Separation key-extraction from key ordering + +// Simple demo for Comparator.comparing +// http://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#comparing-java.util.function.Function- + +// data to be sorted +var cards = [ + { name: "hello", email: "foo@hello.com" }, + { name: "world", email: "bar@world.com" }, + { name: "see", email: "see@dontsee.com" }, +]; + +var Collections = java.util.Collections; +var Comparator = java.util.Comparator; + +// sort records name in reverse order of names +Collections.sort(cards, + Comparator.comparing(function(a) a.name).reversed()); + +print(JSON.stringify(cards)) + +// sort records by email +Collections.sort(cards, + Comparator.comparing(function(a) a.email)); + +print(JSON.stringify(cards))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/this_for_eval.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// how to pass arbitrary "this" object for eval'ed script? + +var obj = { foo: 44 }; + +// the following won't work. eval.apply is indirect eval call +// and so 'this' will be global object always! So, this +// line will print 'undefined' +eval.apply(obj, [ " print(this.foo)" ]); + +obj.myEval = eval; + +// still won't work - still indirect 'eval' call! +// still undefined is printed! +obj.myEval("print(this.foo)"); + +function func(code) { + eval(code); +} + +// eval called inside func and so get's func's "this" as it's "this"! +// So, 44 is printed + +func.apply(obj, [ "print(this.foo)" ]);
--- a/samples/zipfs.js Tue Dec 01 22:55:46 2015 -0800 +++ b/samples/zipfs.js Wed Dec 02 23:08:29 2015 -0800 @@ -36,13 +36,12 @@ var Files = Java.type("java.nio.file.Files") var FileSystems = Java.type("java.nio.file.FileSystems") -var FileVisitOption = Java.type("java.nio.file.FileVisitOption") var Paths = Java.type("java.nio.file.Paths") var zipfile = Paths.get(arguments[0]) var fs = FileSystems.newFileSystem(zipfile, null) var root = fs.rootDirectories[0] -Files.walk(root, FileVisitOption.FOLLOW_LINKS).forEach( +Files.walk(root).forEach( function(p) (print(p), print(Files.readAttributes(p, "zip:*"))) ) fs.close()
--- a/src/jdk/internal/dynalink/DynamicLinker.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/DynamicLinker.java Wed Dec 02 23:08:29 2015 -0800 @@ -117,7 +117,7 @@ * return factory.createLinker(); * } * - * public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) { + * public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) { * return dynamicLinker.link(new MonomorphicCallSite(CallSiteDescriptorFactory.create(lookup, name, type))); * } * }
--- a/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Wed Dec 02 23:08:29 2015 -0800 @@ -107,7 +107,7 @@ private final AccessibleObject target; private final MethodType type; - public CallerSensitiveDynamicMethod(final AccessibleObject target) { + CallerSensitiveDynamicMethod(final AccessibleObject target) { super(getName(target)); this.target = target; this.type = getMethodType(target); @@ -115,8 +115,9 @@ private static String getName(final AccessibleObject target) { final Member m = (Member)target; - return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(), - m.getName())); + final boolean constructor = m instanceof Constructor; + return getMethodNameWithSignature(getMethodType(target), constructor ? m.getName() : + getClassAndMethodName(m.getDeclaringClass(), m.getName()), !constructor); } @Override
--- a/src/jdk/internal/dynalink/beans/FacetIntrospector.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/beans/FacetIntrospector.java Wed Dec 02 23:08:29 2015 -0800 @@ -152,7 +152,7 @@ boolean isAccessible(final Member m) { final Class<?> declaring = m.getDeclaringClass(); // (declaring == clazz) is just an optimization - we're calling this only from code that operates on a - // non-restriced class, so if the declaring class is identical to the class being inspected, then forego + // non-restricted class, so if the declaring class is identical to the class being inspected, then forego // a potentially expensive restricted-package check. return declaring == clazz || !CheckRestrictedPackage.isRestrictedClass(declaring); }
--- a/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Wed Dec 02 23:08:29 2015 -0800 @@ -86,7 +86,9 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.text.Collator; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -242,6 +244,35 @@ return methods.getFirst().isConstructor(); } + @Override + public String toString() { + // First gather the names and sort them. This makes it consistent and easier to read. + final List<String> names = new ArrayList<>(methods.size()); + int len = 0; + for (final SingleDynamicMethod m: methods) { + final String name = m.getName(); + len += name.length(); + names.add(name); + } + // Case insensitive sorting, so e.g. "Object" doesn't come before "boolean". + final Collator collator = Collator.getInstance(); + collator.setStrength(Collator.SECONDARY); + Collections.sort(names, collator); + + final String className = getClass().getName(); + // Class name length + length of signatures + 2 chars/per signature for indentation and newline + + // 3 for brackets and initial newline + final int totalLength = className.length() + len + 2 * names.size() + 3; + final StringBuilder b = new StringBuilder(totalLength); + b.append('[').append(className).append('\n'); + for(final String name: names) { + b.append(' ').append(name).append('\n'); + } + b.append(']'); + assert b.length() == totalLength; + return b.toString(); + }; + ClassLoader getClassLoader() { return classLoader; }
--- a/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Wed Dec 02 23:08:29 2015 -0800 @@ -122,13 +122,13 @@ * @param constructor does this represent a constructor? */ SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) { - super(getName(target, clazz, name)); + super(getName(target, clazz, name, constructor)); this.target = target; this.constructor = constructor; } - private static String getName(final MethodHandle target, final Class<?> clazz, final String name) { - return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name)); + private static String getName(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) { + return getMethodNameWithSignature(target.type(), constructor ? name : getClassAndMethodName(clazz, name), !constructor); } @Override
--- a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java Wed Dec 02 23:08:29 2015 -0800 @@ -98,7 +98,6 @@ * target method to a call site type (including mapping variable arity methods to a call site signature with different * arity). * @author Attila Szegedi - * @version $Id: $ */ abstract class SingleDynamicMethod extends DynamicMethod { @@ -143,14 +142,18 @@ return getMethodType().parameterList().equals(method.getMethodType().parameterList()); } - static String getMethodNameWithSignature(final MethodType type, final String methodName) { + static String getMethodNameWithSignature(final MethodType type, final String methodName, final boolean withReturnType) { final String typeStr = type.toString(); final int retTypeIndex = typeStr.lastIndexOf(')') + 1; int secondParamIndex = typeStr.indexOf(',') + 1; if(secondParamIndex == 0) { secondParamIndex = retTypeIndex - 1; } - return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex); + final StringBuilder b = new StringBuilder(); + if (withReturnType) { + b.append(typeStr, retTypeIndex, typeStr.length()).append(' '); + } + return b.append(methodName).append('(').append(typeStr, secondParamIndex, retTypeIndex).toString(); } /**
--- a/src/jdk/internal/dynalink/linker/GuardedInvocation.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/linker/GuardedInvocation.java Wed Dec 02 23:08:29 2015 -0800 @@ -353,7 +353,7 @@ /** * Applies argument filters to both the invocation and the guard (if there is one). - * @param pos the position of the first argumen being filtered + * @param pos the position of the first argument being filtered * @param filters the argument filters * @return a filtered invocation */
--- a/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java Wed Dec 02 23:08:29 2015 -0800 @@ -110,7 +110,7 @@ /** * Check if invocation is cacheable - * @return true if cachable, false otherwise + * @return true if cacheable, false otherwise */ public boolean isCacheable() { return cacheable;
--- a/src/jdk/nashorn/api/scripting/AbstractJSObject.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/api/scripting/AbstractJSObject.java Wed Dec 02 23:08:29 2015 -0800 @@ -182,7 +182,7 @@ /** * Checking whether the given object is an instance of 'this' object. * - * @param instance instace to check + * @param instance instance to check * @return true if the given 'instance' is an instance of this 'function' object */ @Override
--- a/src/jdk/nashorn/api/scripting/JSObject.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/api/scripting/JSObject.java Wed Dec 02 23:08:29 2015 -0800 @@ -141,7 +141,7 @@ /** * Checking whether the given object is an instance of 'this' object. * - * @param instance instace to check + * @param instance instance to check * @return true if the given 'instance' is an instance of this 'function' object */ public boolean isInstance(final Object instance);
--- a/src/jdk/nashorn/api/scripting/NashornException.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/api/scripting/NashornException.java Wed Dec 02 23:08:29 2015 -0800 @@ -51,6 +51,8 @@ private String fileName; // script line number private int line; + // are the line and fileName unknown? + private boolean lineAndFileNameUnknown; // script column number private int column; // underlying ECMA error object - lazily initialized @@ -92,27 +94,10 @@ */ protected NashornException(final String msg, final Throwable cause) { super(msg, cause == null ? null : cause); - // This is not so pretty - but it gets the job done. Note that the stack - // trace has been already filled by "fillInStackTrace" call from - // Throwable - // constructor and so we don't pay additional cost for it. - // Hard luck - no column number info this.column = -1; - - // Find the first JavaScript frame by walking and set file, line from it - // Usually, we should be able to find it in just few frames depth. - for (final StackTraceElement ste : getStackTrace()) { - if (ECMAErrors.isScriptFrame(ste)) { - // Whatever here is compiled from JavaScript code - this.fileName = ste.getFileName(); - this.line = ste.getLineNumber(); - return; - } - } - - this.fileName = null; - this.line = 0; + // We can retrieve the line number and file name from the stack trace if needed + this.lineAndFileNameUnknown = true; } /** @@ -121,6 +106,7 @@ * @return the file name */ public final String getFileName() { + ensureLineAndFileName(); return fileName; } @@ -131,6 +117,7 @@ */ public final void setFileName(final String fileName) { this.fileName = fileName; + lineAndFileNameUnknown = false; } /** @@ -139,6 +126,7 @@ * @return the line number */ public final int getLineNumber() { + ensureLineAndFileName(); return line; } @@ -148,6 +136,7 @@ * @param line the line number */ public final void setLineNumber(final int line) { + lineAndFileNameUnknown = false; this.line = line; } @@ -274,4 +263,19 @@ public void setEcmaError(final Object ecmaError) { this.ecmaError = ecmaError; } + + private void ensureLineAndFileName() { + if (lineAndFileNameUnknown) { + for (final StackTraceElement ste : getStackTrace()) { + if (ECMAErrors.isScriptFrame(ste)) { + // Whatever here is compiled from JavaScript code + fileName = ste.getFileName(); + line = ste.getLineNumber(); + return; + } + } + + lineAndFileNameUnknown = false; + } + } }
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed Dec 02 23:08:29 2015 -0800 @@ -140,7 +140,7 @@ this._global_per_engine = nashornContext.getEnv()._global_per_engine; // create new global object - this.global = createNashornGlobal(context); + this.global = createNashornGlobal(); // set the default ENGINE_SCOPE object for the default context context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE); } @@ -167,7 +167,7 @@ // We use same 'global' for all Bindings. return new SimpleBindings(); } - return createGlobalMirror(null); + return createGlobalMirror(); } // Compilable methods @@ -317,7 +317,7 @@ // We didn't find associated nashorn global mirror in the Bindings given! // Create new global instance mirror and associate with the Bindings. - final ScriptObjectMirror mirror = createGlobalMirror(ctxt); + final ScriptObjectMirror mirror = createGlobalMirror(); bindings.put(NASHORN_GLOBAL, mirror); return mirror.getHomeGlobal(); } @@ -333,13 +333,13 @@ } // Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object - private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) { - final Global newGlobal = createNashornGlobal(ctxt); + private ScriptObjectMirror createGlobalMirror() { + final Global newGlobal = createNashornGlobal(); return new ScriptObjectMirror(newGlobal, newGlobal); } // Create a new Nashorn Global object - private Global createNashornGlobal(final ScriptContext ctxt) { + private Global createNashornGlobal() { final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { @Override public Global run() { @@ -354,7 +354,7 @@ } }, CREATE_GLOBAL_ACC_CTXT); - nashornContext.initGlobal(newGlobal, this, ctxt); + nashornContext.initGlobal(newGlobal, this); return newGlobal; }
--- a/src/jdk/nashorn/api/scripting/ScriptUtils.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java Wed Dec 02 23:08:29 2015 -0800 @@ -79,7 +79,7 @@ * @return a synchronizing wrapper function */ public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) { - return func.makeSynchronizedFunction(unwrap(sync)); + return func.createSynchronized(unwrap(sync)); } /**
--- a/src/jdk/nashorn/api/scripting/URLReader.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/api/scripting/URLReader.java Wed Dec 02 23:08:29 2015 -0800 @@ -103,7 +103,7 @@ /** * Charset used by this reader * - * @return the Chartset used to convert bytes to chars + * @return the Charset used to convert bytes to chars */ public Charset getCharset() { return cs;
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Wed Dec 02 23:08:29 2015 -0800 @@ -40,11 +40,9 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.logging.DebugLogger; @@ -82,7 +80,7 @@ */ @Logger(name="apply2call") -public final class ApplySpecialization extends NodeVisitor<LexicalContext> implements Loggable { +public final class ApplySpecialization extends SimpleNodeVisitor implements Loggable { private static final boolean USE_APPLY2CALL = Options.getBooleanProperty("nashorn.apply2call", true); @@ -106,7 +104,6 @@ * @param compiler compiler */ public ApplySpecialization(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); } @@ -139,7 +136,7 @@ private boolean hasApplies(final FunctionNode functionNode) { try { - functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + functionNode.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode fn) { return fn == functionNode; @@ -173,7 +170,7 @@ final Deque<Set<Expression>> stack = new ArrayDeque<>(); //ensure that arguments is only passed as arg to apply - functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + functionNode.accept(new SimpleNodeVisitor() { private boolean isCurrentArg(final Expression expr) { return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call @@ -283,17 +280,13 @@ start++; } - start++; //we always uses this + start++; // we always use this - final List<IdentNode> params = functionNode.getParameters(); + assert functionNode.getNumOfParams() == 0 : "apply2call on function with named paramaters!"; final List<IdentNode> newParams = new ArrayList<>(); - final long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start); + final long to = actualCallSiteType.parameterCount() - start; for (int i = 0; i < to; i++) { - if (i >= params.size()) { - newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i))); - } else { - newParams.add(params.get(i)); - } + newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i))); } callSiteTypes.push(actualCallSiteType); @@ -302,7 +295,28 @@ @Override public boolean enterFunctionNode(final FunctionNode functionNode) { - if (!USE_APPLY2CALL) { + // Cheap tests first + if (!( + // is the transform globally enabled? + USE_APPLY2CALL + + // Are we compiling lazily? We can't known the number and types of the actual parameters at + // the caller when compiling eagerly, so this only works with on-demand compilation. + && compiler.isOnDemandCompilation() + + // Does the function even reference the "arguments" identifier (without redefining it)? If not, + // it trivially can't have an expression of form "f.apply(self, arguments)" that this transform + // is targeting. + && functionNode.needsArguments() + + // Does the function have eval? If so, it can arbitrarily modify arguments so we can't touch it. + && !functionNode.hasEval() + + // Finally, does the function declare any parameters explicitly? We don't support that. It could + // be done, but has some complications. Therefore only a function with no explicit parameters + // is considered. + && functionNode.getNumOfParams() == 0)) + { return false; } @@ -312,14 +326,6 @@ return false; } - if (!compiler.isOnDemandCompilation()) { - return false; - } - - if (functionNode.hasEval()) { - return false; - } - if (!hasApplies(functionNode)) { return false; } @@ -375,7 +381,7 @@ callSiteTypes.pop(); explodedArguments.pop(); - return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED); + return newFunctionNode; } private static boolean isApply(final CallNode callNode) {
--- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Dec 02 23:08:29 2015 -0800 @@ -65,17 +65,14 @@ import jdk.nashorn.internal.ir.Expression; 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.IndexNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -83,7 +80,7 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ECMAErrors; @@ -104,7 +101,7 @@ * visitor. */ @Logger(name="symbols") -final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable { +final class AssignSymbols extends SimpleNodeVisitor implements Loggable { private final DebugLogger log; private final boolean debug; @@ -149,12 +146,13 @@ private final Deque<Set<String>> thisProperties = new ArrayDeque<>(); private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol private final Compiler compiler; + private final boolean isOnDemand; public AssignSymbols(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); this.debug = log.isEnabled(); + this.isOnDemand = compiler.isOnDemandCompilation(); } @Override @@ -187,7 +185,7 @@ */ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { // This visitor will assign symbol to all declared variables. - body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + body.accept(new SimpleNodeVisitor() { @Override protected boolean enterDefault(final Node node) { // Don't bother visiting expressions; var is a statement, it can't be inside an expression. @@ -242,7 +240,7 @@ /** * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically - * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function + * used to create assignment of {@code :callee} to the function name symbol in self-referential function * expressions as well as for assignment of {@code :arguments} to {@code arguments}. * * @param name the ident node identifying the variable to initialize @@ -390,7 +388,7 @@ // Create and add to appropriate block. symbol = createSymbol(name, flags); - symbolBlock.putSymbol(lc, symbol); + symbolBlock.putSymbol(symbol); if ((flags & IS_SCOPE) == 0) { // Initial assumption; symbol can lose its slot later @@ -440,7 +438,7 @@ start(block); if (lc.isFunctionBody()) { - block.clearSymbols(); + assert !block.hasSymbols(); final FunctionNode fn = lc.getCurrentFunction(); if (isUnparsedFunction(fn)) { // It's a skipped nested function. Just mark the symbols being used by it as being in use. @@ -459,7 +457,7 @@ } private boolean isUnparsedFunction(final FunctionNode fn) { - return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); + return isOnDemand && fn != lc.getOutermostFunction(); } @Override @@ -551,9 +549,7 @@ private void defineVarIdent(final VarNode varNode) { final IdentNode ident = varNode.getName(); final int flags; - if (varNode.isAnonymousFunctionDeclaration()) { - flags = IS_INTERNAL; - } else if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) { + if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) { flags = IS_SCOPE; } else { flags = 0; @@ -749,28 +745,6 @@ } } - @Override - public Node leaveBlock(final Block block) { - // It's not necessary to guard the marking of symbols as locals with this "if" condition for - // correctness, it's just an optimization -- runtime type calculation is not used when the compilation - // is not an on-demand optimistic compilation, so we can skip locals marking then. - if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { - // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand - // compilation, and we're skipping parsing the function bodies for nested functions, this - // basically only means their parameters. It'd be enough to mistakenly declare to be a local a - // symbol in the outer function named the same as one of the parameters, though. - if (lc.getFunction(block) == lc.getOutermostFunction()) { - for (final Symbol symbol: block.getSymbols()) { - if (!symbol.isScope()) { - assert symbol.isVar() || symbol.isParam(); - compiler.declareLocalSymbol(symbol.getName()); - } - } - } - } - return block; - } - private Node leaveDELETE(final UnaryNode unaryNode) { final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final boolean strictMode = currentFunctionNode.isStrict(); @@ -785,13 +759,14 @@ // If this is a declared variable or a function parameter, delete always fails (except for globals). final String name = ident.getName(); final Symbol symbol = ident.getSymbol(); + + if (symbol.isThis()) { + // Can't delete "this", ignore and return true + return LiteralNode.newInstance(unaryNode, true); + } + final Expression literalNode = LiteralNode.newInstance(unaryNode, name); final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))); - if (failDelete && symbol.isThis()) { - return LiteralNode.newInstance(unaryNode, true).accept(this); - } - final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); - if (!failDelete) { args.add(compilerConstantIdentifier(SCOPE)); } @@ -800,13 +775,15 @@ if (failDelete) { request = Request.FAIL_DELETE; + } else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) { + request = Request.SLOW_DELETE; } } else if (rhs instanceof AccessNode) { final Expression base = ((AccessNode)rhs).getBase(); final String property = ((AccessNode)rhs).getProperty(); args.add(base); - args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this)); + args.add(LiteralNode.newInstance(unaryNode, property)); args.add(strictFlagNode); } else if (rhs instanceof IndexNode) { @@ -819,15 +796,15 @@ args.add(strictFlagNode); } else { - return LiteralNode.newInstance(unaryNode, true).accept(this); + return LiteralNode.newInstance(unaryNode, true); } - return new RuntimeNode(unaryNode, request, args).accept(this); + return new RuntimeNode(unaryNode, request, args); } @Override public Node leaveForNode(final ForNode forNode) { if (forNode.isForIn()) { - forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 + return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 } return end(forNode); @@ -847,7 +824,7 @@ lc.applyTopFlags(functionNode)))) .setThisProperties(lc, thisProperties.pop().size())); } - return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED); + return finalizedFunction; } @Override @@ -903,19 +880,18 @@ public Node leaveSwitchNode(final SwitchNode switchNode) { // We only need a symbol for the tag if it's not an integer switch node if(!switchNode.isUniqueInteger()) { - switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); + return switchNode.setTag(lc, newObjectInternal(SWITCH_TAG_PREFIX)); } return switchNode; } @Override public Node leaveTryNode(final TryNode tryNode) { - tryNode.setException(exceptionSymbol()); assert tryNode.getFinallyBody() == null; end(tryNode); - return tryNode; + return tryNode.setException(lc, exceptionSymbol()); } private Node leaveTYPEOF(final UnaryNode unaryNode) { @@ -924,13 +900,13 @@ final List<Expression> args = new ArrayList<>(); if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { args.add(compilerConstantIdentifier(SCOPE)); - args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null + args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName())); //null } else { args.add(rhs); - args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' + args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } - final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); + final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); end(unaryNode); @@ -938,7 +914,7 @@ } private FunctionNode markProgramBlock(final FunctionNode functionNode) { - if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) { + if (isOnDemand || !functionNode.isProgram()) { return functionNode; } @@ -1005,7 +981,7 @@ boolean previousWasBlock = false; for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { final LexicalContextNode node = it.next(); - if (node instanceof FunctionNode || isSplitArray(node)) { + if (node instanceof FunctionNode || isSplitLiteral(node)) { // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. // It needs to be in scope. return true; @@ -1031,12 +1007,8 @@ throw new AssertionError(); } - private static boolean isSplitArray(final LexicalContextNode expr) { - if(!(expr instanceof ArrayLiteralNode)) { - return false; - } - final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); - return !(units == null || units.isEmpty()); + private static boolean isSplitLiteral(final LexicalContextNode expr) { + return expr instanceof Splittable && ((Splittable) expr).getSplitRanges() != null; } private void throwUnprotectedSwitchError(final VarNode varNode) {
--- a/src/jdk/nashorn/internal/codegen/AstSerializer.java Tue Dec 01 22:55:46 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.nashorn.internal.codegen; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.util.Collections; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; -import jdk.nashorn.internal.ir.Block; -import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.LexicalContext; -import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.Statement; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; -import jdk.nashorn.internal.runtime.options.Options; - -/** - * This static utility class performs serialization of FunctionNode ASTs to a byte array. - * The format is a standard Java serialization stream, deflated. - */ -final class AstSerializer { - // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed - // and size. - private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); - static byte[] serialize(final FunctionNode fn) { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final Deflater deflater = new Deflater(COMPRESSION_LEVEL); - try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { - oout.writeObject(removeInnerFunctionBodies(fn)); - } catch (final IOException e) { - throw new AssertionError("Unexpected exception serializing function", e); - } finally { - deflater.end(); - } - return out.toByteArray(); - } - - private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) { - return (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public Node leaveBlock(final Block block) { - if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { - return block.setStatements(lc, Collections.<Statement>emptyList()); - } - return super.leaveBlock(block); - } - }); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/CacheAst.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.codegen; + +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; + +class CacheAst extends SimpleNodeVisitor { + private final Deque<RecompilableScriptFunctionData> dataStack = new ArrayDeque<>(); + + private final Compiler compiler; + + CacheAst(final Compiler compiler) { + this.compiler = compiler; + assert !compiler.isOnDemandCompilation(); + } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + final int id = functionNode.getId(); + // It isn't necessary to keep a stack of RecompilableScriptFunctionData, but then we'd need to do a + // potentially transitive lookup with compiler.getScriptFunctionData(id) for deeper functions; this way + // we keep it constant time. + dataStack.push(dataStack.isEmpty() ? compiler.getScriptFunctionData(id) : dataStack.peek().getScriptFunctionData(id)); + return true; + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + final RecompilableScriptFunctionData data = dataStack.pop(); + if (functionNode.isSplit()) { + // NOTE: cache only split function ASTs from eager pass. Caching non-split functions would require + // some additional work, namely creating the concept of "uncacheable" function and reworking + // ApplySpecialization to ensure that functions undergoing apply-to-call transformations are not + // cacheable as well as recomputing Symbol.useCount when caching the eagerly parsed AST. + // Recomputing Symbol.useCount would be needed so it will only reflect uses from within the + // function being cached (and not reflect uses from its own nested functions or functions it is + // nested in). This is consistent with the count an on-demand recompilation of the function would + // produce. This is important as the decision to emit shared scope calls is based on this count, + // and if it is not matched between a previous version of the code and its deoptimizing rest-of + // compilation, it can result in rest-of not emitting a shared scope call where a previous version + // of the code (compiled from a cached eager pre-pass seeing higher (global) useCount) would emit + // it, causing a mismatch in stack shapes between previous code and its rest-of. + data.setCachedAst(functionNode); + } + + if (!dataStack.isEmpty() && ((dataStack.peek().getFunctionFlags() & FunctionNode.IS_SPLIT) != 0)) { + // Return a function node with no body so that caching outer functions doesn't hold on to nested + // functions' bodies. Note we're doing this only for functions directly nested inside split + // functions, since we're only caching the split ones. It is not necessary to limit body removal + // to just these functions, but it's a cheap way to prevent unnecessary AST mutations. + return functionNode.setBody(lc, functionNode.getBody().setStatements(null, Collections.<Statement>emptyList())); + } + return functionNode; + } +}
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Dec 02 23:08:29 2015 -0800 @@ -93,7 +93,6 @@ 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.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; @@ -106,7 +105,6 @@ import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode; import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.LoopNode; @@ -119,6 +117,7 @@ import jdk.nashorn.internal.ir.RuntimeNode.Request; import jdk.nashorn.internal.ir.SetSplitState; import jdk.nashorn.internal.ir.SplitReturn; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -130,9 +129,8 @@ 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.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.objects.Global; -import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; @@ -195,9 +193,9 @@ private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class, "ensureNumber", double.class, Object.class, int.class); - private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class, + private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunction.class, "create", ScriptFunction.class, Object[].class, int.class, ScriptObject.class); - private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class, + private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunction.class, "create", ScriptFunction.class, Object[].class, int.class); private static final Call TO_NUMBER_FOR_EQ = CompilerConstants.staticCallNoLookup(JSType.class, @@ -244,12 +242,12 @@ private final DebugLogger log; /** From what size should we use spill instead of fields for JavaScript objects? */ - private static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256); + static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256); private final Set<String> emittedMethods = new HashSet<>(); // Function Id -> ContinuationInfo. Used by compilation of rest-of function only. - private final Map<Integer, ContinuationInfo> fnIdToContinuationInfo = new HashMap<>(); + private ContinuationInfo continuationInfo; private final Deque<Label> scopeEntryLabels = new ArrayDeque<>(); @@ -349,11 +347,20 @@ final int flags = getScopeCallSiteFlags(symbol); 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 && !isOptimisticOrRestOf()) { - method.loadCompilerConstant(SCOPE); - // As shared scope vars are only used in non-optimistic compilation, we switch from using TypeBounds to + if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !identNode.isOptimistic()) { + // As shared scope vars are only used with non-optimistic identifiers, we switch from using TypeBounds to // just a single definitive type, resultBounds.widest. - loadSharedScopeVar(resultBounds.widest, symbol, flags); + new OptimisticOperation(identNode, TypeBounds.OBJECT) { + @Override + void loadStack() { + method.loadCompilerConstant(SCOPE); + } + + @Override + void consumeStack() { + loadSharedScopeVar(resultBounds.widest, symbol, flags); + } + }.emit(); } else { new LoadFastScopeVar(identNode, resultBounds, flags).emit(); } @@ -384,10 +391,6 @@ return continuationEntryPoints != null; } - private boolean isOptimisticOrRestOf() { - return useOptimisticTypes() || isRestOf(); - } - private boolean isCurrentContinuationEntryPoint(final int programPoint) { return isRestOf() && getCurrentContinuationEntryPoint() == programPoint; } @@ -464,12 +467,8 @@ } private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) { - assert !isOptimisticOrRestOf(); - if (isFastScope(symbol)) { - method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol)); - } else { - method.load(-1); - } + assert isFastScope(symbol); + method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol)); return lc.getScopeGet(unit, symbol, valueType, flags).generateInvoke(method); } @@ -1434,8 +1433,7 @@ final Block currentBlock = lc.getCurrentBlock(); final CodeGeneratorLexicalContext codegenLexicalContext = lc; - function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - + function.accept(new SimpleNodeVisitor() { private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) { final Symbol symbol = identNode.getSymbol(); final boolean isFastScope = isFastScope(symbol); @@ -1480,7 +1478,7 @@ } @Override void consumeStack() { - dynamicCall(2 + argsCount, flags); + dynamicCall(2 + argsCount, flags, ident.getName()); } }.emit(); } @@ -1494,7 +1492,7 @@ int argsCount; @Override void loadStack() { - /** + /* * We want to load 'eval' to check if it is indeed global builtin eval. * If this eval call is inside a 'with' statement, dyn:getMethod|getProp|getElem * would be generated if ident is a "isFunction". But, that would result in a @@ -1538,7 +1536,7 @@ @Override void consumeStack() { // Ordinary call - dynamicCall(2 + argsCount, flags); + dynamicCall(2 + argsCount, flags, "eval"); method._goto(eval_done); method.label(invoke_direct_eval); @@ -1573,7 +1571,7 @@ } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD || !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD || CodeGenerator.this.lc.inDynamicScope() - || isOptimisticOrRestOf()) { + || callNode.isOptimistic()) { scopeCall(node, flags); } else { sharedScopeCall(node, flags); @@ -1610,7 +1608,7 @@ } @Override void consumeStack() { - dynamicCall(2 + argCount, flags); + dynamicCall(2 + argCount, flags, node.toString(false)); } }.emit(); @@ -1635,9 +1633,7 @@ @Override void consumeStack() { - final int flags = getCallSiteFlags(); - //assert callNodeType.equals(callee.getReturnType()) : callNodeType + " != " + callee.getReturnType(); - dynamicCall(2 + argsCount, flags); + dynamicCall(2 + argsCount, getCallSiteFlags(), null); } }.emit(); return false; @@ -1666,8 +1662,7 @@ } @Override void consumeStack() { - final int flags = getCallSiteFlags(); - dynamicCall(2 + argsCount, flags); + dynamicCall(2 + argsCount, getCallSiteFlags(), node.toString(false)); } }.emit(); return false; @@ -1687,7 +1682,7 @@ @Override void consumeStack() { final int flags = getCallSiteFlags() | CALLSITE_SCOPE; - dynamicCall(2 + argsCount, flags); + dynamicCall(2 + argsCount, flags, node.toString(false)); } }.emit(); return false; @@ -2073,8 +2068,6 @@ @Override public boolean enterFunctionNode(final FunctionNode functionNode) { - final int fnId = functionNode.getId(); - if (skipFunction(functionNode)) { // In case we are not generating code for the function, we must create or retrieve the function object and // load it on the stack here. @@ -2112,9 +2105,9 @@ method.begin(); if (isRestOf()) { - final ContinuationInfo ci = new ContinuationInfo(); - fnIdToContinuationInfo.put(fnId, ci); - method.gotoLoopStart(ci.getHandlerLabel()); + assert continuationInfo == null; + continuationInfo = new ContinuationInfo(); + method.gotoLoopStart(continuationInfo.getHandlerLabel()); } } @@ -2147,7 +2140,7 @@ markOptimistic = false; } - FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.BYTECODE_GENERATED); + FunctionNode newFunctionNode = functionNode; if (markOptimistic) { newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_DEOPTIMIZABLE); } @@ -2240,73 +2233,33 @@ * * @param arrayLiteralNode the array of contents * @param arrayType the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT - * - * @return the method generator that was used */ - private MethodEmitter loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) { + private void loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) { assert arrayType == Type.INT_ARRAY || arrayType == Type.LONG_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY; - final Expression[] nodes = arrayLiteralNode.getValue(); - final Object presets = arrayLiteralNode.getPresets(); - final int[] postsets = arrayLiteralNode.getPostsets(); - final Class<?> type = arrayType.getTypeClass(); - final List<ArrayUnit> units = arrayLiteralNode.getUnits(); + final Expression[] nodes = arrayLiteralNode.getValue(); + final Object presets = arrayLiteralNode.getPresets(); + final int[] postsets = arrayLiteralNode.getPostsets(); + final List<Splittable.SplitRange> ranges = arrayLiteralNode.getSplitRanges(); loadConstant(presets); final Type elementType = arrayType.getElementType(); - if (units != null) { - final MethodEmitter savedMethod = method; - final FunctionNode currentFunction = lc.getCurrentFunction(); - - for (final ArrayUnit arrayUnit : units) { - unit = lc.pushCompileUnit(arrayUnit.getCompileUnit()); - - final String className = unit.getUnitClassName(); - assert unit != null; - final String name = currentFunction.uniqueName(SPLIT_PREFIX.symbolName()); - final String signature = methodDescriptor(type, ScriptFunction.class, Object.class, ScriptObject.class, type); - - pushMethodEmitter(unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature)); - - method.setFunctionNode(currentFunction); - method.begin(); - - defineCommonSplitMethodParameters(); - defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType); - - // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT - // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). - final int arraySlot = fixScopeSlot(currentFunction, 3); - - lc.enterSplitNode(); - - for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) { - method.load(arrayType, arraySlot); - storeElement(nodes, elementType, postsets[i]); + if (ranges != null) { + + loadSplitLiteral(new SplitLiteralCreator() { + @Override + public void populateRange(final MethodEmitter method, final Type type, final int slot, final int start, final int end) { + for (int i = start; i < end; i++) { + method.load(type, slot); + storeElement(nodes, elementType, postsets[i]); + } + method.load(type, slot); } - - method.load(arrayType, arraySlot); - method._return(); - lc.exitSplitNode(); - method.end(); - lc.releaseSlots(); - popMethodEmitter(); - - assert method == savedMethod; - method.loadCompilerConstant(CALLEE); - method.swap(); - method.loadCompilerConstant(THIS); - method.swap(); - method.loadCompilerConstant(SCOPE); - method.swap(); - method.invokestatic(className, name, signature); - - unit = lc.popCompileUnit(unit); - } - - return method; + }, ranges, arrayType); + + return; } if(postsets.length > 0) { @@ -2318,7 +2271,6 @@ } method.load(arrayType, arraySlot); } - return method; } private void storeElement(final Expression[] nodes, final Type elementType, final int index) { @@ -2508,7 +2460,7 @@ @Override public Boolean get() { - value.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + value.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { return false; @@ -2543,6 +2495,7 @@ final List<MapTuple<Expression>> tuples = new ArrayList<>(); final List<PropertyNode> gettersSetters = new ArrayList<>(); final int ccp = getCurrentContinuationEntryPoint(); + final List<Splittable.SplitRange> ranges = objectNode.getSplitRanges(); Expression protoNode = null; boolean restOfProperty = false; @@ -2589,7 +2542,13 @@ loadExpressionAsType(node, type); }}; } - oc.makeObject(method); + + if (ranges != null) { + oc.createObject(method); + loadSplitLiteral(oc, ranges, Type.typeFor(oc.getAllocatorClass())); + } else { + oc.makeObject(method); + } //if this is a rest of method and our continuation point was found as one of the values //in the properties above, we need to reset the map to oc.getMap() in the continuation @@ -2839,7 +2798,7 @@ boolean contains; @Override public Boolean get() { - rootExpr.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + rootExpr.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { return false; @@ -2905,6 +2864,54 @@ method.onLocalStore(type, slot); } + private void loadSplitLiteral(final SplitLiteralCreator creator, final List<Splittable.SplitRange> ranges, final Type literalType) { + assert ranges != null; + + // final Type literalType = Type.typeFor(literalClass); + final MethodEmitter savedMethod = method; + final FunctionNode currentFunction = lc.getCurrentFunction(); + + for (final Splittable.SplitRange splitRange : ranges) { + unit = lc.pushCompileUnit(splitRange.getCompileUnit()); + + assert unit != null; + final String className = unit.getUnitClassName(); + final String name = currentFunction.uniqueName(SPLIT_PREFIX.symbolName()); + final Class<?> clazz = literalType.getTypeClass(); + final String signature = methodDescriptor(clazz, ScriptFunction.class, Object.class, ScriptObject.class, clazz); + + pushMethodEmitter(unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature)); + + method.setFunctionNode(currentFunction); + method.begin(); + + defineCommonSplitMethodParameters(); + defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), literalType); + + // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT + // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). + final int literalSlot = fixScopeSlot(currentFunction, 3); + + lc.enterSplitNode(); + + creator.populateRange(method, literalType, literalSlot, splitRange.getLow(), splitRange.getHigh()); + + method._return(); + lc.exitSplitNode(); + method.end(); + lc.releaseSlots(); + popMethodEmitter(); + + assert method == savedMethod; + method.loadCompilerConstant(CALLEE).swap(); + method.loadCompilerConstant(THIS).swap(); + method.loadCompilerConstant(SCOPE).swap(); + method.invokestatic(className, name, signature); + + unit = lc.popCompileUnit(unit); + } + } + private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) { // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method) final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE); @@ -3707,10 +3714,11 @@ final CallNode callNode = (CallNode)unaryNode.getExpression(); final List<Expression> args = callNode.getArgs(); + final Expression func = callNode.getFunction(); // Load function reference. - loadExpressionAsObject(callNode.getFunction()); // must detect type error - - method.dynamicNew(1 + loadArgs(args), getCallSiteFlags()); + loadExpressionAsObject(func); // must detect type error + + method.dynamicNew(1 + loadArgs(args), getCallSiteFlags(), func.toString(false)); } private void loadNOT(final UnaryNode unaryNode) { @@ -4333,12 +4341,12 @@ } private void prologue() { - /** + /* * This loads the parts of the target, e.g base and index. they are kept * on the stack throughout the store and used at the end to execute it */ - target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + target.accept(new SimpleNodeVisitor() { @Override public boolean enterIdentNode(final IdentNode node) { if (node.getSymbol().isScope()) { @@ -4437,7 +4445,7 @@ * need to do a conversion on non-equivalent types exists, but is * very rare. See for example test/script/basic/access-specializer.js */ - target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + target.accept(new SimpleNodeVisitor() { @Override protected boolean enterDefault(final Node node) { throw new AssertionError("Unexpected node " + node + " in store epilogue"); @@ -4801,7 +4809,7 @@ * conversion has no side effects. * @param name the name of the property being get * @param flags call site flags - * @param isMethod whether we're preferrably retrieving a function + * @param isMethod whether we're preferably retrieving a function * @return the current method emitter */ MethodEmitter dynamicGet(final String name, final int flags, final boolean isMethod, final boolean isIndex) { @@ -4818,11 +4826,11 @@ return method.dynamicGetIndex(resultBounds.within(expression.getType()), nonOptimisticFlags(flags), isMethod); } - MethodEmitter dynamicCall(final int argCount, final int flags) { + MethodEmitter dynamicCall(final int argCount, final int flags, final String msg) { if (isOptimistic) { - return method.dynamicCall(getOptimisticCoercedType(), argCount, getOptimisticFlags(flags)); - } - return method.dynamicCall(resultBounds.within(expression.getType()), argCount, nonOptimisticFlags(flags)); + return method.dynamicCall(getOptimisticCoercedType(), argCount, getOptimisticFlags(flags), msg); + } + return method.dynamicCall(resultBounds.within(expression.getType()), argCount, nonOptimisticFlags(flags), msg); } int getOptimisticFlags(final int flags) { @@ -5233,7 +5241,7 @@ private Type returnValueType; // If we are in the middle of an object literal initialization, we need to update the map private PropertyMap objectLiteralMap; - // Object literal stack depth for object literal - not necessarly top if property is a tree + // Object literal stack depth for object literal - not necessarily top if property is a tree private int objectLiteralStackDepth = -1; // The line number at the continuation point private int lineNumber; @@ -5310,7 +5318,7 @@ } private ContinuationInfo getContinuationInfo() { - return fnIdToContinuationInfo.get(lc.getCurrentFunction().getId()); + return continuationInfo; } private void generateContinuationHandler() { @@ -5398,7 +5406,7 @@ method.load(lvarTypes.get(slot), slot); method.convert(stackTypes[i]); // stack: s0=object literal being initialized - // change map of s0 so that the property we are initilizing when we failed + // change map of s0 so that the property we are initializing when we failed // is now ci.returnValueType if (i == objectLiteralStackDepth) { method.dup(); @@ -5466,4 +5474,21 @@ method.uncheckedGoto(targetCatchLabel); } } + + /** + * Interface implemented by object creators that support splitting over multiple methods. + */ + interface SplitLiteralCreator { + /** + * Generate code to populate a range of the literal object. A reference to the object + * should be left on the stack when the method terminates. + * + * @param method the method emitter + * @param type the type of the literal object + * @param slot the local slot containing the literal object + * @param start the start index (inclusive) + * @param end the end index (exclusive) + */ + void populateRange(MethodEmitter method, Type type, int slot, int start, int end); + } }
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Dec 02 23:08:29 2015 -0800 @@ -25,37 +25,24 @@ package jdk.nashorn.internal.codegen; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.OPTIMISTIC_TYPES_ASSIGNED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; -import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; import java.io.PrintWriter; -import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; +import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.CodeInstaller; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptEnvironment; @@ -65,15 +52,9 @@ * A compilation phase is a step in the processes of turning a JavaScript * FunctionNode into bytecode. It has an optional return value. */ -enum CompilationPhase { - /** - * Constant folding pass Simple constant folding that will make elementary - * constructs go away - */ - CONSTANT_FOLDING_PHASE( - EnumSet.of( - INITIALIZED, - PARSED)) { +abstract class CompilationPhase { + + private static final class ConstantFoldingPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new FoldConstants(compiler)); @@ -83,20 +64,15 @@ public String toString() { return "'Constant Folding'"; } - }, + } /** - * Lower (Control flow pass) Finalizes the control flow. Clones blocks for - * finally constructs and similar things. Establishes termination criteria - * for nodes Guarantee return instructions to method making sure control - * flow cannot fall off the end. Replacing high level nodes with lower such - * as runtime nodes where applicable. + * Constant folding pass Simple constant folding that will make elementary + * constructs go away */ - LOWERING_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED)) { + static final CompilationPhase CONSTANT_FOLDING_PHASE = new ConstantFoldingPhase(); + + private static final class LoweringPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new Lower(compiler)); @@ -106,42 +82,35 @@ public String toString() { return "'Control Flow Lowering'"; } - }, + } /** - * Phase used only when doing optimistic code generation. It assigns all potentially - * optimistic ops a program point so that an UnwarrantedException knows from where - * a guess went wrong when creating the continuation to roll back this execution + * Lower (Control flow pass) Finalizes the control flow. Clones blocks for + * finally constructs and similar things. Establishes termination criteria + * for nodes Guarantee return instructions to method making sure control + * flow cannot fall off the end. Replacing high level nodes with lower such + * as runtime nodes where applicable. */ - TRANSFORM_BUILTINS_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED)) { - //we only do this if we have a param type map, otherwise this is not a specialized recompile + static final CompilationPhase LOWERING_PHASE = new LoweringPhase(); + + private static final class ApplySpecializationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return setStates(transformFunction(fn, new ApplySpecialization(compiler)), BUILTINS_TRANSFORMED); + return transformFunction(fn, new ApplySpecialization(compiler)); } @Override public String toString() { return "'Builtin Replacement'"; } - }, + }; /** - * Splitter Split the AST into several compile units based on a heuristic size calculation. - * Split IR can lead to scope information being changed. + * Phase used to transform Function.prototype.apply. */ - SPLITTING_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED)) { + static final CompilationPhase APPLY_SPECIALIZATION_PHASE = new ApplySpecializationPhase(); + + private static final class SplittingPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L); @@ -149,7 +118,7 @@ FunctionNode newFunctionNode; //ensure elementTypes, postsets and presets exist for splitter and arraynodes - newFunctionNode = transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { + newFunctionNode = transformFunction(fn, new SimpleNodeVisitor() { @Override public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) { return literalNode.initialize(lc); @@ -168,16 +137,15 @@ public String toString() { return "'Code Splitting'"; } - }, + }; - PROGRAM_POINT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + /** + * Splitter Split the AST into several compile units based on a heuristic size calculation. + * Split IR can lead to scope information being changed. + */ + static final CompilationPhase SPLITTING_PHASE = new SplittingPhase(); + + private static final class ProgramPointPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new ProgramPoints()); @@ -187,43 +155,34 @@ public String toString() { return "'Program Point Calculation'"; } - }, + }; - SERIALIZE_SPLIT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + static final CompilationPhase PROGRAM_POINT_PHASE = new ProgramPointPhase(); + + private static final class CacheAstPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public boolean enterFunctionNode(final FunctionNode functionNode) { - if (functionNode.isSplit()) { - compiler.serializeAst(functionNode); - } - return true; - } - }); + if (!compiler.isOnDemandCompilation()) { + // Only do this on initial preprocessing of the source code. For on-demand compilations from + // source, FindScopeDepths#leaveFunctionNode() calls data.setCachedAst() for the sole function + // being compiled. + transformFunction(fn, new CacheAst(compiler)); + } + // NOTE: we're returning the original fn as we have destructively modified the cached functions by + // removing their bodies. This step is associating FunctionNode objects with + // RecompilableScriptFunctionData; it's not really modifying the AST. + return fn; } @Override public String toString() { - return "'Serialize Split Functions'"; + return "'Cache ASTs'"; } - }, + }; - SYMBOL_ASSIGNMENT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + static final CompilationPhase CACHE_AST_PHASE = new CacheAstPhase(); + + private static final class SymbolAssignmentPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new AssignSymbols(compiler)); @@ -233,17 +192,11 @@ public String toString() { return "'Symbol Assignment'"; } - }, + }; - SCOPE_DEPTH_COMPUTATION_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED)) { + static final CompilationPhase SYMBOL_ASSIGNMENT_PHASE = new SymbolAssignmentPhase(); + + private static final class ScopeDepthComputationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { return transformFunction(fn, new FindScopeDepths(compiler)); @@ -253,43 +206,66 @@ public String toString() { return "'Scope Depth Computation'"; } - }, + }; + + static final CompilationPhase SCOPE_DEPTH_COMPUTATION_PHASE = new ScopeDepthComputationPhase(); - OPTIMISTIC_TYPE_ASSIGNMENT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED)) { + private static final class DeclareLocalSymbolsPhase extends CompilationPhase { + @Override + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { + // It's not necessary to guard the marking of symbols as locals with this "if" condition for + // correctness, it's just an optimization -- runtime type calculation is not used when the compilation + // is not an on-demand optimistic compilation, so we can skip locals marking then. + if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { + fn.getBody().accept(new SimpleNodeVisitor() { + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand + // compilation, and we're skipping parsing the function bodies for nested functions, this + // basically only means their parameters. It'd be enough to mistakenly declare to be a local a + // symbol in the outer function named the same as one of the parameters, though. + return false; + }; + @Override + public boolean enterBlock(final Block block) { + for (final Symbol symbol: block.getSymbols()) { + if (!symbol.isScope()) { + compiler.declareLocalSymbol(symbol.getName()); + } + } + return true; + }; + }); + } + return fn; + } + + @Override + public String toString() { + return "'Local Symbols Declaration'"; + } + }; + + static final CompilationPhase DECLARE_LOCAL_SYMBOLS_PHASE = new DeclareLocalSymbolsPhase(); + + private static final class OptimisticTypeAssignmentPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { if (compiler.useOptimisticTypes()) { return transformFunction(fn, new OptimisticTypesCalculator(compiler)); } - return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED); + return fn; } @Override public String toString() { return "'Optimistic Type Assignment'"; } - }, + } - LOCAL_VARIABLE_TYPE_CALCULATION_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED)) { + static final CompilationPhase OPTIMISTIC_TYPE_ASSIGNMENT_PHASE = new OptimisticTypeAssignmentPhase(); + + private static final class LocalVariableTypeCalculationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler)); @@ -314,25 +290,11 @@ public String toString() { return "'Local Variable Type Calculation'"; } - }, - + }; - /** - * Reuse compile units, if they are already present. We are using the same compiler - * to recompile stuff - */ - REUSE_COMPILE_UNITS_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED, - LOCAL_VARIABLE_TYPES_CALCULATED)) { + static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase(); + + private static final class ReuseCompileUnitsPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods"; @@ -380,16 +342,15 @@ public String toString() { return "'Reuse Compile Units'"; } - }, + } - REINITIALIZE_SERIALIZED( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT)) { + /** + * Reuse compile units, if they are already present. We are using the same compiler + * to recompile stuff + */ + static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase(); + + private static final class ReinitializeCachedPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet(); @@ -430,28 +391,13 @@ @Override public String toString() { - return "'Deserialize'"; + return "'Reinitialize cached'"; } - }, + } - /** - * Bytecode generation: - * - * Generate the byte code class(es) resulting from the compiled FunctionNode - */ - BYTECODE_GENERATION_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED, - LOCAL_VARIABLE_TYPES_CALCULATED)) { + static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase(); + private static final class BytecodeGenerationPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final ScriptEnvironment senv = compiler.getScriptEnvironment(); @@ -469,7 +415,7 @@ try { // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program // in the lazy + optimistic world. See CodeGenerator.skipFunction(). - newFunctionNode = transformFunction(newFunctionNode, codegen).setState(null, BYTECODE_GENERATED); + newFunctionNode = transformFunction(newFunctionNode, codegen); codegen.generateScopeCalls(); } catch (final VerifyError e) { if (senv._verify_code || senv._print_code) { @@ -517,22 +463,16 @@ public String toString() { return "'Bytecode Generation'"; } - }, + } - INSTALL_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED, - BUILTINS_TRANSFORMED, - SPLIT, - SYMBOLS_ASSIGNED, - SCOPE_DEPTHS_COMPUTED, - OPTIMISTIC_TYPES_ASSIGNED, - LOCAL_VARIABLE_TYPES_CALCULATED, - BYTECODE_GENERATED)) { + /** + * Bytecode generation: + * + * Generate the byte code class(es) resulting from the compiled FunctionNode + */ + static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase(); + private static final class InstallPhase extends CompilationPhase { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { final DebugLogger log = compiler.getLogger(); @@ -543,8 +483,8 @@ Class<?> rootClass = null; long length = 0L; - final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller(); - final Map<String, byte[]> bytecode = compiler.getBytecode(); + final CodeInstaller codeInstaller = compiler.getCodeInstaller(); + final Map<String, byte[]> bytecode = compiler.getBytecode(); for (final Entry<String, byte[]> entry : bytecode.entrySet()) { final String className = entry.getKey(); @@ -600,18 +540,16 @@ log.fine(sb.toString()); } - return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED); + return fn.setRootClass(null, rootClass); } @Override public String toString() { return "'Class Installation'"; } - - }; + } - /** pre conditions required for function node to which this transform is to be applied */ - private final EnumSet<CompilationState> pre; + static final CompilationPhase INSTALL_PHASE = new InstallPhase(); /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ private long startTime; @@ -622,21 +560,7 @@ /** boolean that is true upon transform completion */ private boolean isFinished; - private CompilationPhase(final EnumSet<CompilationState> pre) { - this.pre = pre; - } - - private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) { - if (!AssertsEnabled.assertsEnabled()) { - return functionNode; - } - return transformFunction(functionNode, new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public Node leaveFunctionNode(final FunctionNode fn) { - return fn.setState(lc, state); - } - }); - } + private CompilationPhase() {} /** * Start a compilation phase @@ -646,23 +570,7 @@ */ protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) { compiler.getLogger().indent(); - - assert pre != null; - - if (!functionNode.hasState(pre)) { - final StringBuilder sb = new StringBuilder("Compilation phase "); - sb.append(this). - append(" is not applicable to "). - append(quote(functionNode.getName())). - append("\n\tFunctionNode state = "). - append(functionNode.getState()). - append("\n\tRequired state = "). - append(this.pre); - - throw new CompilationException(sb.toString()); - } - - startTime = System.nanoTime(); + startTime = System.nanoTime(); return functionNode; } @@ -697,7 +605,7 @@ abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException; /** - * Apply a transform to a function node, returning the transfored function node. If the transform is not + * Apply a transform to a function node, returning the transformed function node. If the transform is not * applicable, an exception is thrown. Every transform requires the function to have a certain number of * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states.
--- a/src/jdk/nashorn/internal/codegen/Compiler.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Wed Dec 02 23:08:29 2015 -0800 @@ -101,7 +101,7 @@ private final ConstantData constantData; - private final CodeInstaller<ScriptEnvironment> installer; + private final CodeInstaller installer; /** logger for compiler, trampolines and related code generation events * that affect classes */ @@ -160,42 +160,40 @@ */ private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; - private final Map<Integer, byte[]> serializedAsts = new HashMap<>(); - /** * Compilation phases that a compilation goes through */ public static class CompilationPhases implements Iterable<CompilationPhase> { /** - * Singleton that describes compilation up to the phase where a function can be serialized. + * Singleton that describes compilation up to the phase where a function can be cached. */ - private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases( + private final static CompilationPhases COMPILE_UPTO_CACHED = new CompilationPhases( "Common initial phases", CompilationPhase.CONSTANT_FOLDING_PHASE, CompilationPhase.LOWERING_PHASE, - CompilationPhase.TRANSFORM_BUILTINS_PHASE, + CompilationPhase.APPLY_SPECIALIZATION_PHASE, CompilationPhase.SPLITTING_PHASE, CompilationPhase.PROGRAM_POINT_PHASE, - CompilationPhase.SERIALIZE_SPLIT_PHASE + CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, + CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, + CompilationPhase.CACHE_AST_PHASE ); - private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases( + private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( "After common phases, before bytecode generator", - CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, - CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE ); /** - * Singleton that describes additional steps to be taken after deserializing, all the way up to (but not - * including) generating and installing code. + * Singleton that describes additional steps to be taken after retrieving a cached function, all the + * way up to (but not including) generating and installing code. */ - public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases( - "Recompile serialized function up to bytecode", - CompilationPhase.REINITIALIZE_SERIALIZED, - COMPILE_SERIALIZABLE_UPTO_BYTECODE + public final static CompilationPhases RECOMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases( + "Recompile cached function up to bytecode", + CompilationPhase.REINITIALIZE_CACHED, + COMPILE_CACHED_UPTO_BYTECODE ); /** @@ -211,8 +209,8 @@ /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( "Compile upto bytecode", - COMPILE_UPTO_SERIALIZABLE, - COMPILE_SERIALIZABLE_UPTO_BYTECODE); + COMPILE_UPTO_CACHED, + COMPILE_CACHED_UPTO_BYTECODE); /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( @@ -227,9 +225,9 @@ GENERATE_BYTECODE_AND_INSTALL); /** Singleton that describes a full compilation - this includes code installation - from serialized state*/ - public final static CompilationPhases COMPILE_ALL_SERIALIZED = new CompilationPhases( + public final static CompilationPhases COMPILE_ALL_CACHED = new CompilationPhases( "Eager compilation from serializaed state", - RECOMPILE_SERIALIZED_UPTO_BYTECODE, + RECOMPILE_CACHED_UPTO_BYTECODE, GENERATE_BYTECODE_AND_INSTALL); /** @@ -248,9 +246,9 @@ GENERATE_BYTECODE_AND_INSTALL_RESTOF); /** Compile from serialized for a rest of method */ - public final static CompilationPhases COMPILE_SERIALIZED_RESTOF = new CompilationPhases( + public final static CompilationPhases COMPILE_CACHED_RESTOF = new CompilationPhases( "Compile serialized, rest of", - RECOMPILE_SERIALIZED_UPTO_BYTECODE, + RECOMPILE_CACHED_UPTO_BYTECODE, GENERATE_BYTECODE_AND_INSTALL_RESTOF); private final List<CompilationPhase> phases; @@ -313,7 +311,7 @@ } boolean isRestOfCompilation() { - return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_SERIALIZED_RESTOF; + return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_CACHED_RESTOF; } String getDesc() { @@ -353,47 +351,83 @@ private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0); /** - * Constructor + * Creates a new compiler instance for initial compilation of a script. * - * @param context context - * @param env script environment * @param installer code installer * @param source source to compile * @param errors error manager * @param isStrict is this a strict compilation + * @return a new compiler */ - public Compiler( - final Context context, - final ScriptEnvironment env, - final CodeInstaller<ScriptEnvironment> installer, + public static Compiler forInitialCompilation( + final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict) { - this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null); + return new Compiler(installer.getContext(), installer, source, errors, isStrict); } /** - * Constructor + * Creates a compiler without a code installer. It can only be used to compile code, not install the + * generated classes and as such it is useful only for implementation of {@code --compile-only} command + * line option. + * @param context the current context + * @param source source to compile + * @param isStrict is this a strict compilation + * @return a new compiler + */ + public static Compiler forNoInstallerCompilation( + final Context context, + final Source source, + final boolean isStrict) { + return new Compiler(context, null, source, context.getErrorManager(), isStrict); + } + + /** + * Creates a compiler for an on-demand compilation job. * - * @param context context - * @param env script environment * @param installer code installer * @param source source to compile - * @param errors error manager * @param isStrict is this a strict compilation - * @param isOnDemand is this an on demand compilation * @param compiledFunction compiled function, if any * @param types parameter and return value type information, if any is known * @param invalidatedProgramPoints invalidated program points for recompilation * @param typeInformationFile descriptor of the location where type information is persisted * @param continuationEntryPoints continuation entry points for restof method * @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator} + * @return a new compiler */ - @SuppressWarnings("unused") - public Compiler( + public static Compiler forOnDemandCompilation( + final CodeInstaller installer, + final Source source, + final boolean isStrict, + final RecompilableScriptFunctionData compiledFunction, + final TypeMap types, + final Map<Integer, Type> invalidatedProgramPoints, + final Object typeInformationFile, + final int[] continuationEntryPoints, + final ScriptObject runtimeScope) { + final Context context = installer.getContext(); + return new Compiler(context, installer, source, context.getErrorManager(), isStrict, true, + compiledFunction, types, invalidatedProgramPoints, typeInformationFile, + continuationEntryPoints, runtimeScope); + } + + /** + * Convenience constructor for non on-demand compiler instances. + */ + private Compiler( final Context context, - final ScriptEnvironment env, - final CodeInstaller<ScriptEnvironment> installer, + final CodeInstaller installer, + final Source source, + final ErrorManager errors, + final boolean isStrict) { + this(context, installer, source, errors, isStrict, false, null, null, null, null, null, null); + } + + private Compiler( + final Context context, + final CodeInstaller installer, final Source source, final ErrorManager errors, final boolean isStrict, @@ -405,7 +439,7 @@ final int[] continuationEntryPoints, final ScriptObject runtimeScope) { this.context = context; - this.env = env; + this.env = context.getEnv(); this.installer = installer; this.constantData = new ConstantData(); this.compileUnits = CompileUnit.createCompileUnitSet(); @@ -427,7 +461,7 @@ this.optimistic = env._optimistic_types; } - private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) { + private String safeSourceName() { String baseName = new File(source.getName()).getName(); final int index = baseName.lastIndexOf(".js"); @@ -486,7 +520,7 @@ sb.append('$'); } - sb.append(Compiler.safeSourceName(env, installer, source)); + sb.append(safeSourceName()); return sb.toString(); } @@ -607,7 +641,7 @@ newFunctionNode.uniqueName(reservedName); } - final boolean info = log.levelFinerThanOrEqual(Level.INFO); + final boolean info = log.isLoggable(Level.INFO); final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null; @@ -644,8 +678,8 @@ if (info) { final StringBuilder sb = new StringBuilder("<< Finished compile job for "); sb.append(newFunctionNode.getSource()). - append(':'). - append(quote(newFunctionNode.getName())); + append(':'). + append(quote(newFunctionNode.getName())); if (time > 0L && timeLogger != null) { assert env.isTimingEnabled(); @@ -685,7 +719,7 @@ return constantData; } - CodeInstaller<ScriptEnvironment> getCodeInstaller() { + CodeInstaller getCodeInstaller() { return installer; } @@ -713,7 +747,7 @@ if (cacheKey != null && env._persistent_cache) { // If this is an on-demand compilation create a function initializer for the function being compiled. // Otherwise use function initializer map generated by codegen. - Map<Integer, FunctionInitializer> initializers = new HashMap<>(); + final Map<Integer, FunctionInitializer> initializers = new HashMap<>(); if (isOnDemandCompilation()) { initializers.put(functionNode.getId(), new FunctionInitializer(functionNode, getInvalidatedProgramPoints())); } else { @@ -766,14 +800,6 @@ compileUnits.addAll(newUnits); } - void serializeAst(final FunctionNode fn) { - serializedAsts.put(fn.getId(), AstSerializer.serialize(fn)); - } - - byte[] removeSerializedAst(final int fnId) { - return serializedAsts.remove(fnId); - } - CompileUnit findUnit(final long weight) { for (final CompileUnit unit : compileUnits) { if (unit.canHold(weight)) { @@ -829,9 +855,9 @@ final long totalSize = osc.calculateObjectSize(functionNode); sb.append(phaseName). - append(" Total size = "). - append(totalSize / 1024 / 1024). - append("MB"); + append(" Total size = "). + append(totalSize / 1024 / 1024). + append("MB"); log.info(sb); Collections.sort(list, new Comparator<ClassHistogramElement>() {
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java Wed Dec 02 23:08:29 2015 -0800 @@ -192,7 +192,7 @@ private static Set<String> symbolNames; /** - * Prefix used for internal methods generated in script clases. + * Prefix used for internal methods generated in script classes. */ private static final String INTERNAL_METHOD_PREFIX = ":"; @@ -225,7 +225,7 @@ } /** - * Check whether a name is that of a reserved compiler constnat + * Check whether a name is that of a reserved compiler constant * @param name name * @return true if compiler constant name */
--- a/src/jdk/nashorn/internal/codegen/ConstantData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/ConstantData.java Wed Dec 02 23:08:29 2015 -0800 @@ -30,6 +30,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; + import jdk.nashorn.internal.runtime.PropertyMap; /** @@ -120,7 +122,7 @@ private final int hashCode; public PropertyMapWrapper(final PropertyMap map) { - this.hashCode = Arrays.hashCode(map.getProperties()); + this.hashCode = Arrays.hashCode(map.getProperties()) + 31 * Objects.hashCode(map.getClassName()); this.propertyMap = map; } @@ -131,8 +133,13 @@ @Override public boolean equals(final Object other) { - return other instanceof PropertyMapWrapper && - Arrays.equals(propertyMap.getProperties(), ((PropertyMapWrapper) other).propertyMap.getProperties()); + if (!(other instanceof PropertyMapWrapper)) { + return false; + } + final PropertyMap otherMap = ((PropertyMapWrapper) other).propertyMap; + return propertyMap == otherMap + || (Arrays.equals(propertyMap.getProperties(), otherMap.getProperties()) + && Objects.equals(propertyMap.getClassName(), otherMap.getClassName())); } }
--- a/src/jdk/nashorn/internal/codegen/DumpBytecode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/DumpBytecode.java Wed Dec 02 23:08:29 2015 -0800 @@ -88,7 +88,7 @@ } - // should code be dumped to disk - only valid in compile_only mode? + // should code be dumped to disk if (env._dest_dir != null) { final String fileName = className.replace('.', File.separatorChar) + ".class"; final int index = fileName.lastIndexOf(File.separatorChar);
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Wed Dec 02 23:08:29 2015 -0800 @@ -34,7 +34,6 @@ import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; -import java.util.Iterator; import java.util.List; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Symbol; @@ -91,27 +90,20 @@ findClass(); } - /** - * Construct an object. - * - * @param method the method emitter - */ @Override - protected void makeObject(final MethodEmitter method) { + public void createObject(final MethodEmitter method) { makeMap(); final String className = getClassName(); - try { - // NOTE: we must load the actual structure class here, because the API operates with Nashorn Type objects, - // and Type objects need a loaded class, for better or worse. We also have to be specific and use the type - // of the actual structure class, we can't generalize it to e.g. Type.typeFor(ScriptObject.class) as the - // exact type information is needed for generating continuations in rest-of methods. If we didn't do this, - // object initializers like { x: arr[i] } would fail during deoptimizing compilation on arr[i], as the - // values restored from the RewriteException would be cast to "ScriptObject" instead of to e.g. "JO4", and - // subsequently the "PUTFIELD J04.L0" instruction in the continuation code would fail bytecode verification. - method._new(Context.forStructureClass(className.replace('/', '.'))).dup(); - } catch (final ClassNotFoundException e) { - throw new AssertionError(e); - } + // NOTE: we must load the actual structure class here, because the API operates with Nashorn Type objects, + // and Type objects need a loaded class, for better or worse. We also have to be specific and use the type + // of the actual structure class, we can't generalize it to e.g. Type.typeFor(ScriptObject.class) as the + // exact type information is needed for generating continuations in rest-of methods. If we didn't do this, + // object initializers like { x: arr[i] } would fail during deoptimizing compilation on arr[i], as the + // values restored from the RewriteException would be cast to "ScriptObject" instead of to e.g. "JO4", and + // subsequently the "PUTFIELD J04.L0" instruction in the continuation code would fail bytecode verification. + assert fieldObjectClass != null; + method._new(fieldObjectClass).dup(); + loadMap(method); //load the map if (isScope()) { @@ -126,14 +118,14 @@ } else { method.invoke(constructorNoLookup(className, PropertyMap.class)); } + } - helpOptimisticRecognizeDuplicateIdentity(method); - + @Override + public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) { + method.load(objectType, objectSlot); // Set values. - final Iterator<MapTuple<T>> iter = tuples.iterator(); - - while (iter.hasNext()) { - final MapTuple<T> tuple = iter.next(); + for (int i = start; i < end; i++) { + final MapTuple<T> tuple = tuples.get(i); //we only load when we have both symbols and values (which can be == the symbol) //if we didn't load, we need an array property if (tuple.symbol != null && tuple.value != null) { @@ -212,6 +204,11 @@ } } + @Override + protected Class<? extends ScriptObject> getAllocatorClass() { + return fieldObjectClass; + } + /** * Get the class name for the object class, * e.g. {@code com.nashorn.oracle.scripts.JO2P0}
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Wed Dec 02 23:08:29 2015 -0800 @@ -34,13 +34,12 @@ import java.util.Set; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.logging.DebugLogger; @@ -54,7 +53,7 @@ * FunctionNode being compiled */ @Logger(name="scopedepths") -final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Loggable { +final class FindScopeDepths extends SimpleNodeVisitor implements Loggable { private final Compiler compiler; private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>(); @@ -67,7 +66,6 @@ private int dynamicScopeCount; FindScopeDepths(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; this.log = initLogger(compiler.getContext()); } @@ -182,14 +180,16 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { final String name = functionNode.getName(); - FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.SCOPE_DEPTHS_COMPUTED); - + FunctionNode newFunctionNode = functionNode; if (compiler.isOnDemandCompilation()) { final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId()); if (data.inDynamicContext()) { log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope."); newFunctionNode = newFunctionNode.setInDynamicContext(lc); } + if (newFunctionNode == lc.getOutermostFunction() && !newFunctionNode.hasApplyToCallSpecialization()) { + data.setCachedAst(newFunctionNode); + } return newFunctionNode; } @@ -210,8 +210,7 @@ ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()), nestedFunctions, externalSymbolDepths.get(fnId), - internalSymbols.get(fnId), - compiler.removeSerializedAst(fnId)); + internalSymbols.get(fnId)); if (lc.getOutermostFunction() != newFunctionNode) { final FunctionNode parentFn = lc.getParentFunction(newFunctionNode); @@ -275,17 +274,13 @@ //get all symbols that are referenced inside this function body final Set<Symbol> symbols = new HashSet<>(); - block.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + block.accept(new SimpleNodeVisitor() { @Override - public final boolean enterDefault(final Node node) { - if (!compiler.isOnDemandCompilation()) { - if (node instanceof IdentNode) { - final Symbol symbol = ((IdentNode)node).getSymbol(); - if (symbol != null && symbol.isScope()) { - //if this is an internal symbol, skip it. - symbols.add(symbol); - } - } + public boolean enterIdentNode(final IdentNode identNode) { + final Symbol symbol = identNode.getSymbol(); + if (symbol != null && symbol.isScope()) { + //if this is an internal symbol, skip it. + symbols.add(symbol); } return true; }
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java Wed Dec 02 23:08:29 2015 -0800 @@ -37,9 +37,7 @@ import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IfNode; -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; @@ -48,7 +46,7 @@ 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.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -60,12 +58,11 @@ * Simple constant folding pass, executed before IR is starting to be lowered. */ @Logger(name="fold") -final class FoldConstants extends NodeVisitor<LexicalContext> implements Loggable { +final class FoldConstants extends SimpleNodeVisitor implements Loggable { private final DebugLogger log; FoldConstants(final Compiler compiler) { - super(new LexicalContext()); this.log = initLogger(compiler.getContext()); } @@ -101,7 +98,7 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { - return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED); + return functionNode; } @Override @@ -117,7 +114,7 @@ statements.addAll(executed.getStatements()); // Get statements form executed branch } if (dropped != null) { - extractVarNodes(dropped, statements); // Get var-nodes from non-executed branch + extractVarNodesFromDeadCode(dropped, statements); // Get var-nodes from non-executed branch } if (statements.isEmpty()) { return new EmptyNode(ifNode); @@ -186,14 +183,27 @@ 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) { + /** + * When we eliminate dead code, we must preserve var declarations as they are scoped to the whole + * function. This method gathers var nodes from code passed to it, removing their initializers. + * + * @param deadCodeRoot the root node of eliminated dead code + * @param statements a list that will be receiving the var nodes from the dead code, with their + * initializers removed. + */ + static void extractVarNodesFromDeadCode(final Node deadCodeRoot, final List<Statement> statements) { + deadCodeRoot.accept(new SimpleNodeVisitor() { @Override public boolean enterVarNode(final VarNode varNode) { statements.add(varNode.setInit(null)); return false; } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + // Don't descend into nested functions + return false; + } }); } @@ -353,8 +363,8 @@ return null; } - isInteger &= JSType.isRepresentableAsInt(value) && !JSType.isNegativeZero(value); - isLong &= JSType.isRepresentableAsLong(value) && !JSType.isNegativeZero(value); + isInteger &= JSType.isStrictlyRepresentableAsInt(value); + isLong &= JSType.isStrictlyRepresentableAsLong(value); if (isInteger) { return LiteralNode.newInstance(token, finish, (int)value);
--- a/src/jdk/nashorn/internal/codegen/Label.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/Label.java Wed Dec 02 23:08:29 2015 -0800 @@ -447,7 +447,7 @@ undefineLocalVariables(liveLocalCount, true); // Temporaries are promoted firstTemp = liveLocalCount; - // No trailing undefineds + // No trailing undefined values localVariableTypes.subList(firstTemp, localVariableTypes.size()).clear(); assert symbolBoundary.length() == firstTemp; // Generalize all reference types to Object, and promote boolean to int @@ -497,7 +497,7 @@ private transient Label.Stack stack; /** ASM representation of this label */ - private jdk.internal.org.objectweb.asm.Label label; + private transient jdk.internal.org.objectweb.asm.Label label; /** Id for debugging purposes, remove if footprint becomes unmanageable */ private final int id;
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Dec 02 23:08:29 2015 -0800 @@ -54,7 +54,6 @@ 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.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; @@ -88,6 +87,7 @@ import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.TokenType; /** @@ -106,7 +106,7 @@ * instances of the calculator to be run on nested functions (when not lazy compiling). * */ -final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{ +final class LocalVariableTypesCalculator extends SimpleNodeVisitor { private static class JumpOrigin { final JoinPredecessor node; @@ -426,7 +426,6 @@ private final Deque<Label> catchLabels = new ArrayDeque<>(); LocalVariableTypesCalculator(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; } @@ -1331,7 +1330,7 @@ // Sets the return type of the function and also performs the bottom-up pass of applying type and conversion // information to nodes as well as doing the calculation on nested functions as required. FunctionNode newFunction = functionNode; - final NodeVisitor<LexicalContext> applyChangesVisitor = new NodeVisitor<LexicalContext>(new LexicalContext()) { + final SimpleNodeVisitor applyChangesVisitor = new SimpleNodeVisitor() { private boolean inOuterFunction = true; private final Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>(); @@ -1478,7 +1477,6 @@ newFunction = newFunction.setReturnType(lc, returnType); - newFunction = newFunction.setState(lc, CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED); newFunction = newFunction.setParameters(lc, newFunction.visitParameters(applyChangesVisitor)); return newFunction; }
--- a/src/jdk/nashorn/internal/codegen/Lower.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Wed Dec 02 23:08:29 2015 -0800 @@ -51,7 +51,6 @@ 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; @@ -74,7 +73,7 @@ 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.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; @@ -121,13 +120,7 @@ terminated = true; } } else { - statement.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public boolean enterVarNode(final VarNode varNode) { - newStatements.add(varNode.setInit(null)); - return false; - } - }); + FoldConstants.extractVarNodesFromDeadCode(statement, newStatements); } } return newStatements; @@ -266,7 +259,7 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { log.info("END FunctionNode: ", functionNode.getName()); - return functionNode.setState(lc, CompilationState.LOWERED); + return functionNode; } @Override @@ -328,7 +321,7 @@ @SuppressWarnings("unchecked") private static <T extends Node> T ensureUniqueNamesIn(final T node) { - return (T)node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + return (T)node.accept(new SimpleNodeVisitor() { @Override public Node leaveFunctionNode(final FunctionNode functionNode) { final String name = functionNode.getName(); @@ -393,7 +386,7 @@ final Block finallyBlock = createFinallyBlock(finallyBody); final ArrayList<Block> inlinedFinallies = new ArrayList<>(); final FunctionNode fn = lc.getCurrentFunction(); - final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + final TryNode newTryNode = (TryNode)tryNode.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode functionNode) { @@ -511,7 +504,7 @@ } /* - * create a new trynode + * create a new try node * if we have catches: * * try try @@ -522,7 +515,7 @@ * catchall * rethrow * - * otheriwse + * otherwise * * try try * x x @@ -536,7 +529,7 @@ final Block catchAll = catchAllBlock(tryNode); final List<ThrowNode> rethrows = new ArrayList<>(1); - catchAll.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + catchAll.accept(new SimpleNodeVisitor() { @Override public boolean enterThrowNode(final ThrowNode throwNode) { rethrows.add(throwNode); @@ -681,7 +674,7 @@ private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) { final List<Node> escapes = new ArrayList<>(); - loopBody.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + loopBody.accept(new SimpleNodeVisitor() { @Override public Node leaveBreakNode(final BreakNode node) { escapes.add(node);
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Dec 02 23:08:29 2015 -0800 @@ -258,8 +258,7 @@ */ private Type popType(final Type expected) { final Type type = popType(); - assert type.isObject() && expected.isObject() || - type.isEquivalentTo(expected) : type + " is not compatible with " + expected; + assert type.isEquivalentTo(expected) : type + " is not compatible with " + expected; return type; } @@ -1159,7 +1158,7 @@ /** * Pop a value from the stack and store it in a variable denoted by the given symbol. The variable should be either * a local variable, or a function parameter (and not a scoped variable). For local variables, this method will also - * do the bookeeping of the local variable table as well as mark values in all alternative slots for the symbol as + * do the bookkeeping of the local variable table as well as mark values in all alternative slots for the symbol as * dead. In this regard it differs from {@link #storeHidden(Type, int)}. * * @param symbol the symbol to store into. @@ -2133,10 +2132,25 @@ * @return the method emitter */ MethodEmitter dynamicNew(final int argCount, final int flags) { + return dynamicNew(argCount, flags, null); + } + + /** + * Generate a dynamic new + * + * @param argCount number of arguments + * @param flags callsite flags + * @param msg additional message to be used when reporting error + * + * @return the method emitter + */ + MethodEmitter dynamicNew(final int argCount, final int flags, final String msg) { assert !isOptimistic(flags); debug("dynamic_new", "argcount=", argCount); final String signature = getDynamicSignature(Type.OBJECT, argCount); - method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn( + msg != null && msg.length() < LARGE_STRING_THRESHOLD? "dyn:new:" + NameCodec.encode(msg) : "dyn:new", + signature, LINKERBOOTSTRAP, flags); pushType(Type.OBJECT); //TODO fix result type return this; } @@ -2151,10 +2165,26 @@ * @return the method emitter */ MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) { + return dynamicCall(returnType, argCount, flags, null); + } + + /** + * Generate a dynamic call + * + * @param returnType return type + * @param argCount number of arguments + * @param flags callsite flags + * @param msg additional message to be used when reporting error + * + * @return the method emitter + */ + MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags, final String msg) { debug("dynamic_call", "args=", argCount, "returnType=", returnType); final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target) debug(" signature", signature); - method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn( + msg != null && msg.length() < LARGE_STRING_THRESHOLD? "dyn:call:" + NameCodec.encode(msg) : "dyn:call", + signature, LINKERBOOTSTRAP, flags); pushType(returnType); return this;
--- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Wed Dec 02 23:08:29 2015 -0800 @@ -69,7 +69,6 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; -import jdk.nashorn.internal.runtime.options.Options; /** * Generates the ScriptObject subclass structure with fields for a user objects. @@ -787,7 +786,7 @@ * @param primitiveSetter primitive setter for the current type with an element of the current type * @param objectSetter the object setter * - * @return method handle that checks if the element to be set is of the currenttype, even though it's boxed + * @return method handle that checks if the element to be set is of the current type, even though it's boxed * and instead of using the generic object setter, that would blow up the type and invalidate the map, * unbox it and call the primitive setter instead */
--- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java Wed Dec 02 23:08:29 2015 -0800 @@ -36,7 +36,7 @@ * Base class for object creation code generation. * @param <T> value type */ -public abstract class ObjectCreator<T> { +public abstract class ObjectCreator<T> implements CodeGenerator.SplitLiteralCreator { /** List of keys & symbols to initiate in this ObjectCreator */ final List<MapTuple<T>> tuples; @@ -69,7 +69,23 @@ * Generate code for making the object. * @param method Script method. */ - protected abstract void makeObject(final MethodEmitter method); + public void makeObject(final MethodEmitter method) { + createObject(method); + // We need to store the object in a temporary slot as populateRange expects to load the + // object from a slot (as it is also invoked within split methods). Note that this also + // helps optimistic continuations to handle the stack in case an optimistic assumption + // fails during initialization (see JDK-8079269). + final int objectSlot = method.getUsedSlotsWithLiveTemporaries(); + final Type objectType = method.peekType(); + method.storeTemp(objectType, objectSlot); + populateRange(method, objectType, objectSlot, 0, tuples.size()); + } + + /** + * Generate code for creating and initializing the object. + * @param method the method emitter + */ + protected abstract void createObject(final MethodEmitter method); /** * Construct the property map appropriate for the object. @@ -125,6 +141,12 @@ } /** + * Get the class of objects created by this ObjectCreator + * @return class of created object + */ + abstract protected Class<? extends ScriptObject> getAllocatorClass(); + + /** * Technique for loading an initial value. Defined by anonymous subclasses in code gen. * * @param value Value to load. @@ -145,29 +167,4 @@ MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple) { return loadTuple(method, tuple, true); } - - /** - * If using optimistic typing, let the code generator realize that the newly created object on the stack - * when DUP-ed will be the same value. Basically: {NEW, DUP, INVOKESPECIAL init, DUP} will leave a stack - * load specification {unknown, unknown} on stack (that is "there's two values on the stack, but neither - * comes from a known local load"). If there's an optimistic operation in the literal initializer, - * OptimisticOperation.storeStack will allocate two temporary locals for it and store them as - * {ASTORE 4, ASTORE 3}. If we instead do {NEW, DUP, INVOKESPECIAL init, ASTORE 3, ALOAD 3, DUP} we end up - * with stack load specification {ALOAD 3, ALOAD 3} (as DUP can track that the value it duplicated came - * from a local load), so if/when a continuation needs to be recreated from it, it'll be - * able to emit ALOAD 3, ALOAD 3 to recreate the stack. If we didn't do this, deoptimization within an - * object literal initialization could in rare cases cause an incompatible change in the shape of the - * local variable table for the temporaries, e.g. in the following snippet where a variable is reassigned - * to a wider type in an object initializer: - * <code>var m = 1; var obj = {p0: m, p1: m = "foo", p2: m}</code> - * @param method the current method emitter. - */ - void helpOptimisticRecognizeDuplicateIdentity(final MethodEmitter method) { - if (codegen.useOptimisticTypes()) { - final Type objectType = method.peekType(); - final int tempSlot = method.defineTemporaryLocalVariable(objectType.getSlots()); - method.storeHidden(objectType, tempSlot); - method.load(objectType, tempSlot); - } - } }
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Wed Dec 02 23:08:29 2015 -0800 @@ -38,12 +38,10 @@ 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.JoinPredecessorExpression; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Optimistic; @@ -53,7 +51,7 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.ScriptObject; @@ -62,7 +60,7 @@ * must not ever be marked as optimistic, assigning narrowest non-invalidated types to program points from the * compilation environment, as well as initializing optimistic types of global properties for scripts. */ -final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> { +final class OptimisticTypesCalculator extends SimpleNodeVisitor { final Compiler compiler; @@ -70,7 +68,6 @@ final Deque<BitSet> neverOptimistic = new ArrayDeque<>(); OptimisticTypesCalculator(final Compiler compiler) { - super(new LexicalContext()); this.compiler = compiler; } @@ -208,7 +205,7 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { neverOptimistic.pop(); - return functionNode.setState(lc, CompilationState.OPTIMISTIC_TYPES_ASSIGNED); + return functionNode; } @Override
--- a/src/jdk/nashorn/internal/codegen/ProgramPoints.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/ProgramPoints.java Wed Dec 02 23:08:29 2015 -0800 @@ -37,25 +37,20 @@ import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Optimistic; import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; /** * Find program points in the code that are needed for optimistic assumptions */ -class ProgramPoints extends NodeVisitor<LexicalContext> { +class ProgramPoints extends SimpleNodeVisitor { private final IntDeque nextProgramPoint = new IntDeque(); private final Set<Node> noProgramPoint = new HashSet<>(); - ProgramPoints() { - super(new LexicalContext()); - } - private int next() { final int next = nextProgramPoint.getAndIncrement(); if(next > MAX_PROGRAM_POINT_VALUE) {
--- a/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Wed Dec 02 23:08:29 2015 -0800 @@ -29,21 +29,17 @@ import java.util.List; import jdk.nashorn.internal.ir.CompileUnitHolder; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.ObjectNode; +import jdk.nashorn.internal.ir.Splittable; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; /** * Base class for a node visitor that replaces {@link CompileUnit}s in {@link CompileUnitHolder}s. */ -abstract class ReplaceCompileUnits extends NodeVisitor<LexicalContext> { - ReplaceCompileUnits() { - super(new LexicalContext()); - } +abstract class ReplaceCompileUnits extends SimpleNodeVisitor { /** * Override to provide a replacement for an old compile unit. @@ -64,22 +60,35 @@ @Override public Node leaveFunctionNode(final FunctionNode node) { - return node.setCompileUnit(lc, getExistingReplacement(node)).setState(lc, CompilationState.COMPILE_UNITS_REUSED); + return node.setCompileUnit(lc, getExistingReplacement(node)); } @Override public Node leaveLiteralNode(final LiteralNode<?> node) { if (node instanceof ArrayLiteralNode) { final ArrayLiteralNode aln = (ArrayLiteralNode)node; - if (aln.getUnits() == null) { + if (aln.getSplitRanges() == null) { return node; } - final List<ArrayUnit> newArrayUnits = new ArrayList<>(); - for (final ArrayUnit au : aln.getUnits()) { - newArrayUnits.add(new ArrayUnit(getExistingReplacement(au), au.getLo(), au.getHi())); + final List<Splittable.SplitRange> newArrayUnits = new ArrayList<>(); + for (final Splittable.SplitRange au : aln.getSplitRanges()) { + newArrayUnits.add(new Splittable.SplitRange(getExistingReplacement(au), au.getLow(), au.getHigh())); } - return aln.setUnits(lc, newArrayUnits); + return aln.setSplitRanges(lc, newArrayUnits); } return node; } + + @Override + public Node leaveObjectNode(final ObjectNode objectNode) { + final List<Splittable.SplitRange> ranges = objectNode.getSplitRanges(); + if (ranges != null) { + final List<Splittable.SplitRange> newRanges = new ArrayList<>(); + for (final Splittable.SplitRange range : ranges) { + newRanges.add(new Splittable.SplitRange(getExistingReplacement(range), range.getLow(), range.getHigh())); + } + return objectNode.setSplitRanges(lc, newRanges); + } + return super.leaveObjectNode(objectNode); + } }
--- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Wed Dec 02 23:08:29 2015 -0800 @@ -169,7 +169,7 @@ slot += type.getSlots(); } // Shared scope calls disabled in optimistic world. TODO is this right? - method.dynamicCall(returnType, 2 + paramTypes.length, flags); + method.dynamicCall(returnType, 2 + paramTypes.length, flags, symbol.getName()); } method._return(returnType);
--- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Wed Dec 02 23:08:29 2015 -0800 @@ -61,7 +61,7 @@ } @Override - protected void makeObject(final MethodEmitter method) { + public void createObject(final MethodEmitter method) { assert !isScope() : "spill scope objects are not currently supported"; final int length = tuples.size(); @@ -69,9 +69,7 @@ final int spillLength = ScriptObject.spillAllocationLength(length); final long[] jpresetValues = dualFields ? new long[spillLength] : null; final Object[] opresetValues = new Object[spillLength]; - final Set<Integer> postsetValues = new LinkedHashSet<>(); - final int callSiteFlags = codegen.getCallSiteFlags(); - final Class<?> objectClass = dualFields ? JD.class : JO.class; + final Class<?> objectClass = getAllocatorClass(); ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY); // Compute constant property values @@ -85,9 +83,7 @@ if (value != null) { final Object constantValue = LiteralNode.objectAsConstant(value); - if (constantValue == LiteralNode.POSTSET_MARKER) { - postsetValues.add(pos); - } else { + if (constantValue != LiteralNode.POSTSET_MARKER) { final Property property = propertyMap.findProperty(key); if (property != null) { // normal property key @@ -146,25 +142,34 @@ // instantiate the script object with spill objects method.invoke(constructorNoLookup(objectClass, PropertyMap.class, long[].class, Object[].class)); - helpOptimisticRecognizeDuplicateIdentity(method); - // Set prefix array data if any if (arrayData.length() > 0) { method.dup(); codegen.loadConstant(arrayData); method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray", void.class, ArrayData.class)); } + } + + @Override + public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) { + final int callSiteFlags = codegen.getCallSiteFlags(); + method.load(objectType, objectSlot); // set postfix values - for (final int i : postsetValues) { + for (int i = start; i < end; i++) { final MapTuple<Expression> tuple = tuples.get(i); + + if (LiteralNode.isConstant(tuple.value)) { + continue; + } + final Property property = propertyMap.findProperty(tuple.key); + if (property == null) { final int index = ArrayIndex.getArrayIndex(tuple.key); assert ArrayIndex.isValidArrayIndex(index); method.dup(); method.load(ArrayIndex.toLongIndex(index)); - //method.println("putting " + tuple + " into arraydata"); loadTuple(method, tuple); method.dynamicSetIndex(callSiteFlags); } else { @@ -178,8 +183,7 @@ @Override protected PropertyMap makeMap() { assert propertyMap == null : "property map already initialized"; - final boolean dualFields = codegen.useDualFields(); - final Class<? extends ScriptObject> clazz = dualFields ? JD.class : JO.class; + final Class<? extends ScriptObject> clazz = getAllocatorClass(); propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields()); return propertyMap; } @@ -188,4 +192,9 @@ protected void loadValue(final Expression expr, final Type type) { codegen.loadExpressionAsType(expr, type); } + + @Override + protected Class<? extends ScriptObject> getAllocatorClass() { + return codegen.useDualFields() ? JD.class : JO.class; + } }
--- a/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Wed Dec 02 23:08:29 2015 -0800 @@ -175,8 +175,7 @@ FunctionNode.IS_ANONYMOUS | FunctionNode.USES_ANCESTOR_SCOPE | FunctionNode.IS_SPLIT ) .setBody(lc, body) - .setCompileUnit(lc, splitNode.getCompileUnit()) - .copyCompilationState(lc, originalFn); + .setCompileUnit(lc, splitNode.getCompileUnit()); // Call the function: // either "(function () { ... }).call(this)" @@ -308,10 +307,6 @@ assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't final Expression init = varNode.getInit(); - if (varNode.isAnonymousFunctionDeclaration()) { - // We ain't moving anonymous function declarations. - return super.enterVarNode(varNode); - } // Move a declaration-only var statement to the top of the outermost function. getCurrentFunctionState().varStatements.add(varNode.setInit(null));
--- a/src/jdk/nashorn/internal/codegen/Splitter.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/Splitter.java Wed Dec 02 23:08:29 2015 -0800 @@ -33,15 +33,15 @@ import java.util.Map; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.ObjectNode; +import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.Statement; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; @@ -52,7 +52,7 @@ * Split the IR into smaller compile units. */ @Logger(name="splitter") -final class Splitter extends NodeVisitor<LexicalContext> implements Loggable { +final class Splitter extends SimpleNodeVisitor implements Loggable { /** Current compiler. */ private final Compiler compiler; @@ -78,7 +78,6 @@ * @param outermostCompileUnit compile unit for outermost function, if non-lazy this is the script's compile unit */ public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) { - super(new LexicalContext()); this.compiler = compiler; this.outermost = functionNode; this.outermostCompileUnit = outermostCompileUnit; @@ -141,7 +140,7 @@ final Block body = functionNode.getBody(); final List<FunctionNode> dc = directChildren(functionNode); - final Block newBody = (Block)body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + final Block newBody = (Block)body.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode nestedFunction) { return dc.contains(nestedFunction); @@ -158,12 +157,12 @@ assert functionNode.getCompileUnit() != null; - return functionNode.setState(null, CompilationState.SPLIT); + return functionNode; } private static List<FunctionNode> directChildren(final FunctionNode functionNode) { final List<FunctionNode> dc = new ArrayList<>(); - functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + functionNode.accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode child) { if (child == functionNode) { @@ -296,7 +295,7 @@ final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal; final Node[] value = arrayLiteralNode.getValue(); final int[] postsets = arrayLiteralNode.getPostsets(); - final List<ArrayUnit> units = new ArrayList<>(); + final List<Splittable.SplitRange> ranges = new ArrayList<>(); long totalWeight = 0; int lo = 0; @@ -310,7 +309,7 @@ if (totalWeight >= SPLIT_THRESHOLD) { final CompileUnit unit = compiler.findUnit(totalWeight - weight); - units.add(new ArrayUnit(unit, lo, i)); + ranges.add(new Splittable.SplitRange(unit, lo, i)); lo = i; totalWeight = weight; } @@ -318,16 +317,59 @@ if (lo != postsets.length) { final CompileUnit unit = compiler.findUnit(totalWeight); - units.add(new ArrayUnit(unit, lo, postsets.length)); + ranges.add(new Splittable.SplitRange(unit, lo, postsets.length)); } - return arrayLiteralNode.setUnits(lc, units); + return arrayLiteralNode.setSplitRanges(lc, ranges); } return literal; } @Override + public Node leaveObjectNode(final ObjectNode objectNode) { + long weight = WeighNodes.weigh(objectNode); + + if (weight < SPLIT_THRESHOLD) { + return objectNode; + } + + final FunctionNode functionNode = lc.getCurrentFunction(); + lc.setFlag(functionNode, FunctionNode.IS_SPLIT); + + final List<Splittable.SplitRange> ranges = new ArrayList<>(); + final List<PropertyNode> properties = objectNode.getElements(); + final boolean isSpillObject = properties.size() > CodeGenerator.OBJECT_SPILL_THRESHOLD; + long totalWeight = 0; + int lo = 0; + + for (int i = 0; i < properties.size(); i++) { + + final PropertyNode property = properties.get(i); + final boolean isConstant = LiteralNode.isConstant(property.getValue()); + + if (!isConstant || !isSpillObject) { + weight = isConstant ? 0 : WeighNodes.weigh(property.getValue()); + totalWeight += WeighNodes.AASTORE_WEIGHT + weight; + + if (totalWeight >= SPLIT_THRESHOLD) { + final CompileUnit unit = compiler.findUnit(totalWeight - weight); + ranges.add(new Splittable.SplitRange(unit, lo, i)); + lo = i; + totalWeight = weight; + } + } + } + + if (lo != properties.size()) { + final CompileUnit unit = compiler.findUnit(totalWeight); + ranges.add(new Splittable.SplitRange(unit, lo, properties.size())); + } + + return objectNode.setSplitRanges(lc, ranges); + } + + @Override public boolean enterFunctionNode(final FunctionNode node) { //only go into the function node for this splitter. any subfunctions are rejected return node == outermost;
--- a/src/jdk/nashorn/internal/codegen/TypeMap.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/TypeMap.java Wed Dec 02 23:08:29 2015 -0800 @@ -27,21 +27,18 @@ import java.lang.invoke.MethodType; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; import java.util.NoSuchElementException; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.runtime.ScriptFunction; /** - * A data structure that maps one or several function nodes (by their unique id:s, not by - * the FunctionNode object itself, due to copy on write changing it several times through - * code generation. + * A tuple containing function id, parameter types, return type and needsCallee flag. */ -public class TypeMap { - private final Map<Integer, Type[]> paramTypeMap = new HashMap<>(); - private final Map<Integer, Type> returnTypeMap = new HashMap<>(); +public final class TypeMap { + private final int functionNodeId; + private final Type[] paramTypes; + private final Type returnType; private final boolean needsCallee; /** @@ -56,9 +53,10 @@ for (final Class<?> p : type.parameterArray()) { types[pos++] = Type.typeFor(p); } - paramTypeMap.put(functionNodeId, types); - returnTypeMap.put(functionNodeId, Type.typeFor(type.returnType())); + this.functionNodeId = functionNodeId; + this.paramTypes = types; + this.returnType = Type.typeFor(type.returnType()); this.needsCallee = needsCallee; } @@ -69,20 +67,14 @@ * @throws NoSuchElementException if the type map has no mapping for the requested function */ public Type[] getParameterTypes(final int functionNodeId) { - final Type[] paramTypes = paramTypeMap.get(functionNodeId); - if (paramTypes == null) { - throw new NoSuchElementException(Integer.toString(functionNodeId)); - } + assert this.functionNodeId == functionNodeId; return paramTypes.clone(); } MethodType getCallSiteType(final FunctionNode functionNode) { - final Type[] types = paramTypeMap.get(functionNode.getId()); - if (types == null) { - return null; - } - - MethodType mt = MethodType.methodType(returnTypeMap.get(functionNode.getId()).getTypeClass()); + assert this.functionNodeId == functionNode.getId(); + final Type[] types = paramTypes; + MethodType mt = MethodType.methodType(returnType.getTypeClass()); if (needsCallee) { mt = mt.appendParameterTypes(ScriptFunction.class); } @@ -116,7 +108,8 @@ * @return parameter type for this callsite if known */ Type get(final FunctionNode functionNode, final int pos) { - final Type[] types = paramTypeMap.get(functionNode.getId()); + assert this.functionNodeId == functionNode.getId(); + final Type[] types = paramTypes; assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this; if (types != null && pos < types.length) { return types[pos]; @@ -124,13 +117,6 @@ return null; } - boolean has(final FunctionNode functionNode) { - final int id = functionNode.getId(); - final Type[] paramTypes = paramTypeMap.get(id); - assert (paramTypes == null) == (returnTypeMap.get(id) == null) : "inconsistent param and return types in param map"; - return paramTypes != null; - } - @Override public String toString() { return toString(""); @@ -139,27 +125,16 @@ String toString(final String prefix) { final StringBuilder sb = new StringBuilder(); - if (paramTypeMap.isEmpty()) { - sb.append(prefix).append("\t<empty>"); - return sb.toString(); - } - - for (final Map.Entry<Integer, Type[]> entry : paramTypeMap.entrySet()) { - final int id = entry.getKey(); - sb.append(prefix).append('\t'); - sb.append("function ").append(id).append('\n'); - sb.append(prefix).append("\t\tparamTypes="); - if (entry.getValue() == null) { - sb.append("[]"); - } else { - sb.append(Arrays.toString(entry.getValue())); - } - sb.append('\n'); - sb.append(prefix).append("\t\treturnType="); - final Type ret = returnTypeMap.get(id); - sb.append(ret == null ? "N/A" : ret); - sb.append('\n'); - } + final int id = functionNodeId; + sb.append(prefix).append('\t'); + sb.append("function ").append(id).append('\n'); + sb.append(prefix).append("\t\tparamTypes="); + sb.append(Arrays.toString(paramTypes)); + sb.append('\n'); + sb.append(prefix).append("\t\treturnType="); + final Type ret = returnType; + sb.append(ret == null ? "N/A" : ret); + sb.append('\n'); return sb.toString(); }
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed Dec 02 23:08:29 2015 -0800 @@ -44,12 +44,13 @@ import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TryNode; @@ -88,6 +89,8 @@ static final long THROW_WEIGHT = 2; static final long VAR_WEIGHT = 40; static final long WITH_WEIGHT = 8; + static final long OBJECT_WEIGHT = 16; + static final long SETPROP_WEIGHT = 5; /** Accumulated weight. */ private long weight; @@ -213,7 +216,7 @@ final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; final Node[] value = arrayLiteralNode.getValue(); final int[] postsets = arrayLiteralNode.getPostsets(); - final List<ArrayUnit> units = arrayLiteralNode.getUnits(); + final List<Splittable.SplitRange> units = arrayLiteralNode.getSplitRanges(); if (units == null) { for (final int postset : postsets) { @@ -233,6 +236,27 @@ } @Override + public boolean enterObjectNode(final ObjectNode objectNode) { + weight += OBJECT_WEIGHT; + final List<PropertyNode> properties = objectNode.getElements(); + final boolean isSpillObject = properties.size() > CodeGenerator.OBJECT_SPILL_THRESHOLD; + + for (final PropertyNode property : properties) { + if (!LiteralNode.isConstant(property.getValue())) { + weight += SETPROP_WEIGHT; + property.getValue().accept(this); + } else if (!isSpillObject) { + // constants in spill object are set via preset spill array, + // but fields objects need to set constants. + weight += SETPROP_WEIGHT; + } + + } + + return false; + } + + @Override public Node leavePropertyNode(final PropertyNode propertyNode) { weight += LITERAL_WEIGHT; return propertyNode;
--- a/src/jdk/nashorn/internal/codegen/types/BytecodeOps.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/types/BytecodeOps.java Wed Dec 02 23:08:29 2015 -0800 @@ -36,7 +36,7 @@ * The bytecode ops are coupled to a MethodVisitor from ASM for * byte code generation. They know nothing about our MethodGenerator, * which is the abstraction for working with Nashorn JS types - * For exmaple, anything like "two or one slots" for a type, which + * For example, anything like "two or one slots" for a type, which * is represented in bytecode and ASM, is abstracted away in the * MethodGenerator. There you just say "dup" or "store". *
--- a/src/jdk/nashorn/internal/codegen/types/Type.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/codegen/types/Type.java Wed Dec 02 23:08:29 2015 -0800 @@ -65,6 +65,7 @@ import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.nashorn.internal.codegen.CompilerConstants.Call; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.Undefined; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -256,6 +257,9 @@ case jdk.internal.org.objectweb.asm.Type.DOUBLE: return NUMBER; case jdk.internal.org.objectweb.asm.Type.OBJECT: + if (Context.isStructureClass(itype.getClassName())) { + return SCRIPT_OBJECT; + } try { return Type.typeFor(Class.forName(itype.getClassName())); } catch(final ClassNotFoundException e) { @@ -949,7 +953,7 @@ /** * This is the singleton for integer arrays */ - public static final ArrayType INT_ARRAY = new ArrayType(int[].class) { + public static final ArrayType INT_ARRAY = putInCache(new ArrayType(int[].class) { private static final long serialVersionUID = 1L; @Override @@ -973,12 +977,12 @@ public Type getElementType() { return INT; } - }; + }); /** * This is the singleton for long arrays */ - public static final ArrayType LONG_ARRAY = new ArrayType(long[].class) { + public static final ArrayType LONG_ARRAY = putInCache(new ArrayType(long[].class) { private static final long serialVersionUID = 1L; @Override @@ -1002,12 +1006,12 @@ public Type getElementType() { return LONG; } - }; + }); /** * This is the singleton for numeric arrays */ - public static final ArrayType NUMBER_ARRAY = new ArrayType(double[].class) { + public static final ArrayType NUMBER_ARRAY = putInCache(new ArrayType(double[].class) { private static final long serialVersionUID = 1L; @Override @@ -1031,13 +1035,7 @@ public Type getElementType() { return NUMBER; } - }; - - /** Singleton for method handle arrays used for properties etc. */ - public static final ArrayType METHODHANDLE_ARRAY = putInCache(new ArrayType(MethodHandle[].class)); - - /** This is the singleton for string arrays */ - public static final ArrayType STRING_ARRAY = putInCache(new ArrayType(String[].class)); + }); /** This is the singleton for object arrays */ public static final ArrayType OBJECT_ARRAY = putInCache(new ArrayType(Object[].class));
--- a/src/jdk/nashorn/internal/ir/Block.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/Block.java Wed Dec 02 23:08:29 2015 -0800 @@ -130,11 +130,42 @@ } /** - * Clear the symbols in the block. - * TODO: make this immutable. + * Returns true if this block defines any symbols. + * @return true if this block defines any symbols. + */ + public boolean hasSymbols() { + return !symbols.isEmpty(); + } + + /** + * Replaces symbols defined in this block with different symbols. Used to ensure symbol tables are + * immutable upon construction and have copy-on-write semantics. Note that this method only replaces the + * symbols in the symbol table, it does not act on any contained AST nodes that might reference the symbols. + * Those should be updated separately as this method is meant to be used as part of such an update pass. + * @param lc the current lexical context + * @param replacements the map of symbol replacements + * @return a new block with replaced symbols, or this block if none of the replacements modified the symbol + * table. */ - public void clearSymbols() { - symbols.clear(); + public Block replaceSymbols(final LexicalContext lc, final Map<Symbol, Symbol> replacements) { + if (symbols.isEmpty()) { + return this; + } + final LinkedHashMap<String, Symbol> newSymbols = new LinkedHashMap<>(symbols); + for (final Map.Entry<String, Symbol> entry: newSymbols.entrySet()) { + final Symbol newSymbol = replacements.get(entry.getValue()); + assert newSymbol != null : "Missing replacement for " + entry.getKey(); + entry.setValue(newSymbol); + } + return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, newSymbols, conversion)); + } + + /** + * Returns a copy of this block with a shallow copy of the symbol table. + * @return a copy of this block with a shallow copy of the symbol table. + */ + public Block copyWithNewSymbols() { + return new Block(this, finish, statements, flags, new LinkedHashMap<>(symbols), conversion); } @Override @@ -162,7 +193,7 @@ * @return symbol iterator */ public List<Symbol> getSymbols() { - return Collections.unmodifiableList(new ArrayList<>(symbols.values())); + return symbols.isEmpty() ? Collections.<Symbol>emptyList() : Collections.unmodifiableList(new ArrayList<>(symbols.values())); } /** @@ -326,10 +357,9 @@ /** * Add or overwrite an existing symbol in the block * - * @param lc get lexical context * @param symbol symbol */ - public void putSymbol(final LexicalContext lc, final Symbol symbol) { + public void putSymbol(final Symbol symbol) { symbols.put(symbol.getName(), symbol); }
--- a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Wed Dec 02 23:08:29 2015 -0800 @@ -34,7 +34,7 @@ * This is a subclass of lexical context used for filling * blocks (and function nodes) with statements. When popping * a block from the lexical context, any statements that have - * been generated in it are commited to the block. This saves + * been generated in it are committed to the block. This saves * unnecessary object mutations and lexical context replacement */ public class BlockLexicalContext extends LexicalContext {
--- a/src/jdk/nashorn/internal/ir/ForNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/ForNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -43,7 +43,7 @@ private final JoinPredecessorExpression modify; /** Iterator symbol. */ - private Symbol iterator; + private final Symbol iterator; /** Is this a normal for in loop? */ public static final int IS_FOR_IN = 1 << 0; @@ -70,22 +70,22 @@ this.flags = flags; this.init = null; this.modify = null; + this.iterator = null; } private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, - final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion) { + final Block body, final JoinPredecessorExpression modify, final int flags, + final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) { super(forNode, test, body, controlFlowEscapes, conversion); this.init = init; this.modify = modify; this.flags = flags; - // Even if the for node gets cloned in try/finally, the symbol can be shared as only one branch of the finally - // is executed. - this.iterator = forNode.iterator; + this.iterator = iterator; } @Override public Node ensureUniqueLabels(final LexicalContext lc) { - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -158,7 +158,7 @@ if (this.init == init) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } /** @@ -206,10 +206,15 @@ /** * Assign an iterator symbol to this ForNode. Used for for in and for each constructs + * @param lc the current lexical context * @param iterator the iterator symbol + * @return a ForNode with the iterator set */ - public void setIterator(final Symbol iterator) { - this.iterator = iterator; + public ForNode setIterator(final LexicalContext lc, final Symbol iterator) { + if (this.iterator == iterator) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } /** @@ -230,7 +235,7 @@ if (this.modify == modify) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -238,7 +243,7 @@ if (this.test == test) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -251,7 +256,7 @@ if (this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override @@ -259,19 +264,19 @@ if (this.controlFlowEscapes == controlFlowEscapes) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } private ForNode setFlags(final LexicalContext lc, final int flags) { if (this.flags == flags) { return this; } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); + return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); } @Override
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -33,10 +33,8 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES; import java.util.Collections; -import java.util.EnumSet; import java.util.Iterator; import java.util.List; -import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.CompilerConstants; @@ -73,40 +71,6 @@ SETTER } - /** Compilation states available */ - public enum CompilationState { - /** compiler is ready */ - INITIALIZED, - /** method has been parsed */ - PARSED, - /** method has been parsed */ - PARSE_ERROR, - /** constant folding pass */ - CONSTANT_FOLDED, - /** method has been lowered */ - LOWERED, - /** program points have been assigned to unique locations */ - PROGRAM_POINTS_ASSIGNED, - /** any transformations of builtins have taken place, e.g. apply=>call */ - BUILTINS_TRANSFORMED, - /** method has been split */ - SPLIT, - /** method has had symbols assigned */ - SYMBOLS_ASSIGNED, - /** computed scope depths for symbols */ - SCOPE_DEPTHS_COMPUTED, - /** method has had types calculated*/ - OPTIMISTIC_TYPES_ASSIGNED, - /** method has had types calculated */ - LOCAL_VARIABLE_TYPES_CALCULATED, - /** compile units reused (optional) */ - COMPILE_UNITS_REUSED, - /** method has been emitted to bytecode */ - BYTECODE_GENERATED, - /** method has been installed */ - BYTECODE_INSTALLED - } - /** Source of entity. */ private transient final Source source; @@ -144,10 +108,6 @@ /** Method's namespace. */ private transient final Namespace namespace; - /** Current compilation state */ - @Ignore - private final EnumSet<CompilationState> compilationState; - /** Number of properties of "this" object assigned in this function */ @Ignore private final int thisProperties; @@ -263,6 +223,11 @@ */ public static final int NEEDS_CALLEE = 1 << 26; + /** + * Is the function node cached? + */ + public static final int IS_CACHED = 1 << 27; + /** extension callsite flags mask */ public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST | @@ -322,7 +287,6 @@ this.firstToken = firstToken; this.lastToken = token; this.namespace = namespace; - this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.flags = flags; this.compileUnit = null; this.body = null; @@ -339,12 +303,11 @@ final String name, final Type returnType, final CompileUnit compileUnit, - final EnumSet<CompilationState> compilationState, final Block body, final List<IdentNode> parameters, final int thisProperties, final Class<?> rootClass, - final Source source, Namespace namespace) { + final Source source, final Namespace namespace) { super(functionNode); this.endParserState = endParserState; @@ -354,7 +317,6 @@ this.returnType = returnType; this.compileUnit = compileUnit; this.lastToken = lastToken; - this.compilationState = compilationState; this.body = body; this.parameters = parameters; this.thisProperties = thisProperties; @@ -454,7 +416,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -530,80 +491,6 @@ } /** - * Get the compilation state of this function - * @return the compilation state - */ - public EnumSet<CompilationState> getState() { - return compilationState; - } - - /** - * Check whether this FunctionNode has reached a give CompilationState. - * - * @param state the state to check for - * @return true of the node is in the given state - */ - public boolean hasState(final EnumSet<CompilationState> state) { - return !AssertsEnabled.assertsEnabled() || compilationState.containsAll(state); - } - - /** - * Add a state to the total CompilationState of this node, e.g. if - * FunctionNode has been lowered, the compiler will add - * {@code CompilationState#LOWERED} to the state vector - * - * @param lc lexical context - * @param state {@link CompilationState} to add - * @return function node or a new one if state was changed - */ - public FunctionNode setState(final LexicalContext lc, final CompilationState state) { - if (!AssertsEnabled.assertsEnabled() || this.compilationState.contains(state)) { - return this; - } - final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); - newState.add(state); - return setCompilationState(lc, newState); - } - - /** - * Copy a compilation state from an original function to this function. Used when creating synthetic - * function nodes by the splitter. - * - * @param lc lexical context - * @param original the original function node to copy compilation state from - * @return function node or a new one if state was changed - */ - public FunctionNode copyCompilationState(final LexicalContext lc, final FunctionNode original) { - final EnumSet<CompilationState> origState = original.compilationState; - if (!AssertsEnabled.assertsEnabled() || this.compilationState.containsAll(origState)) { - return this; - } - final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); - newState.addAll(origState); - return setCompilationState(lc, newState); - } - - private FunctionNode setCompilationState(final LexicalContext lc, final EnumSet<CompilationState> compilationState) { - return Node.replaceInLexicalContext( - lc, - this, - new FunctionNode( - this, - lastToken, - endParserState, - flags, - name, - returnType, - compileUnit, - compilationState, - body, - parameters, - thisProperties, - rootClass, source, namespace)); - } - - - /** * Create a unique name in the namespace of this FunctionNode * @param base prefix for name * @return base if no collision exists, otherwise a name prefix with base @@ -668,7 +555,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -748,7 +634,7 @@ */ public boolean needsCallee() { // NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units. - return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall(); + return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasApplyToCallSpecialization(); } /** @@ -765,7 +651,7 @@ * Return true if function contains an apply to call transform * @return true if this function has transformed apply to call */ - public boolean hasOptimisticApplyToCall() { + public boolean hasApplyToCallSpecialization() { return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION); } @@ -809,7 +695,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -905,7 +790,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -966,7 +850,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1002,7 +885,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1040,7 +922,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1077,6 +958,14 @@ } /** + * Return the number of parameters to this function + * @return the number of parameters + */ + public int getNumOfParams() { + return parameters.size(); + } + + /** * Returns the identifier for a named parameter at the specified position in this function's parameter list. * @param index the parameter's position. * @return the identifier for the requested named parameter. @@ -1108,7 +997,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1196,7 +1084,6 @@ name, type, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1213,6 +1100,24 @@ } /** + * Returns true if this function node has been cached. + * @return true if this function node has been cached. + */ + public boolean isCached() { + return getFlag(IS_CACHED); + } + + /** + * Mark this function node as having been cached. + * @param lc the current lexical context + * @return a function node equivalent to this one, with the flag set. + */ + public FunctionNode setCached(final LexicalContext lc) { + return setFlag(lc, IS_CACHED); + } + + + /** * Get the compile unit used to compile this function * @see Compiler * @return the compile unit @@ -1244,7 +1149,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties, @@ -1300,7 +1204,6 @@ name, returnType, compileUnit, - compilationState, body, parameters, thisProperties,
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -25,11 +25,9 @@ package jdk.nashorn.internal.ir; -import java.io.Serializable; 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; @@ -430,7 +428,7 @@ * * @param token token * @param finish finish - * @param value undefined value, passed only for polymorphisism discrimination + * @param value undefined value, passed only for polymorphism discrimination * * @return the new literal node */ @@ -561,6 +559,15 @@ return POSTSET_MARKER; } + /** + * Test whether {@code object} represents a constant value. + * @param object a node or value object + * @return true if object is a constant value + */ + public static boolean isConstant(final Object object) { + return objectAsConstant(object) != POSTSET_MARKER; + } + private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> { private static final long serialVersionUID = 1L; @@ -592,7 +599,7 @@ * Array literal node class. */ @Immutable - public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode { + public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode, Splittable { private static final long serialVersionUID = 1L; /** Array element type. */ @@ -604,59 +611,8 @@ /** Indices of array elements requiring computed post sets. */ private final int[] postsets; - /** Sub units with indexes ranges, in which to split up code generation, for large literals */ - private final List<ArrayUnit> units; - - /** - * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can - * be split if they are too large, for bytecode generation reasons - */ - public static final class ArrayUnit implements CompileUnitHolder, Serializable { - private static final long serialVersionUID = 1L; - - /** Compile unit associated with the postsets range. */ - private final CompileUnit compileUnit; - - /** postsets range associated with the unit (hi not inclusive). */ - private final int lo, hi; - - /** - * Constructor - * @param compileUnit compile unit - * @param lo lowest array index in unit - * @param hi highest array index in unit + 1 - */ - public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) { - this.compileUnit = compileUnit; - this.lo = lo; - this.hi = hi; - } - - /** - * Get the high index position of the ArrayUnit (non inclusive) - * @return high index position - */ - public int getHi() { - return hi; - } - - /** - * Get the low index position of the ArrayUnit (inclusive) - * @return low index position - */ - public int getLo() { - return lo; - } - - /** - * The array compile unit - * @return array compile unit - */ - @Override - public CompileUnit getCompileUnit() { - return compileUnit; - } - } + /** Ranges for splitting up large literals in code generation */ + private final List<Splittable.SplitRange> splitRanges; private static final class ArrayLiteralInitializer { @@ -664,7 +620,7 @@ final Type elementType = computeElementType(node.value); final int[] postsets = computePostsets(node.value); final Object presets = computePresets(node.value, elementType, postsets); - return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units); + return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.splitRanges); } private static Type computeElementType(final Expression[] value) { @@ -697,7 +653,7 @@ for (int i = 0; i < value.length; i++) { final Expression element = value[i]; - if (element == null || objectAsConstant(element) == POSTSET_MARKER) { + if (element == null || !isConstant(element)) { computed[nComputed++] = i; } } @@ -814,19 +770,19 @@ this.elementType = Type.UNKNOWN; this.presets = null; this.postsets = null; - this.units = null; + this.splitRanges = null; } /** * Copy constructor * @param node source array literal node */ - private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) { + private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<Splittable.SplitRange> splitRanges) { super(node, value); this.elementType = elementType; this.postsets = postsets; this.presets = presets; - this.units = units; + this.splitRanges = splitRanges; } /** @@ -917,26 +873,27 @@ } /** - * Get the array units that make up this ArrayLiteral - * @see ArrayUnit - * @return list of array units + * Get the split ranges for this ArrayLiteral, or null if this array does not have to be split. + * @see Splittable.SplitRange + * @return list of split ranges */ - public List<ArrayUnit> getUnits() { - return units == null ? null : Collections.unmodifiableList(units); + @Override + public List<Splittable.SplitRange> getSplitRanges() { + return splitRanges == null ? null : Collections.unmodifiableList(splitRanges); } /** - * Set the ArrayUnits that make up this ArrayLiteral + * Set the SplitRanges that make up this ArrayLiteral * @param lc lexical context - * @see ArrayUnit - * @param units list of array units - * @return new or changed arrayliteralnode + * @see Splittable.SplitRange + * @param splitRanges list of split ranges + * @return new or changed node */ - public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) { - if (this.units == units) { + public ArrayLiteralNode setSplitRanges(final LexicalContext lc, final List<Splittable.SplitRange> splitRanges) { + if (this.splitRanges == splitRanges) { return this; } - return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); + return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges)); } @Override @@ -958,7 +915,7 @@ if (this.value == value) { return this; } - return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); + return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, splitRanges)); } private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
--- a/src/jdk/nashorn/internal/ir/Node.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/Node.java Wed Dec 02 23:08:29 2015 -0800 @@ -129,9 +129,17 @@ public abstract Node accept(NodeVisitor<? extends LexicalContext> visitor); @Override - public String toString() { + public final String toString() { + return toString(true); + } + + /* + * Return String representation of this Node. + * @param includeTypeInfo include type information or not + */ + public final String toString(final boolean includeTypeInfo) { final StringBuilder sb = new StringBuilder(); - toString(sb); + toString(sb, includeTypeInfo); return sb.toString(); }
--- a/src/jdk/nashorn/internal/ir/ObjectNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/ObjectNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.List; +import java.util.RandomAccess; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -35,12 +36,15 @@ * IR representation of an object literal. */ @Immutable -public final class ObjectNode extends Expression { +public final class ObjectNode extends Expression implements LexicalContextNode, Splittable { private static final long serialVersionUID = 1L; /** Literal elements. */ private final List<PropertyNode> elements; + /** Ranges for splitting large literals over multiple compile units in codegen. */ + private final List<Splittable.SplitRange> splitRanges; + /** * Constructor * @@ -51,19 +55,27 @@ public ObjectNode(final long token, final int finish, final List<PropertyNode> elements) { super(token, finish); this.elements = elements; + this.splitRanges = null; + assert elements instanceof RandomAccess : "Splitting requires random access lists"; } - private ObjectNode(final ObjectNode objectNode, final List<PropertyNode> elements) { + private ObjectNode(final ObjectNode objectNode, final List<PropertyNode> elements, + final List<Splittable.SplitRange> splitRanges ) { super(objectNode); this.elements = elements; + this.splitRanges = splitRanges; } @Override public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { + return Acceptor.accept(this, visitor); + } + + @Override + public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { if (visitor.enterObjectNode(this)) { - return visitor.leaveObjectNode(setElements(Node.accept(visitor, elements))); + return visitor.leaveObjectNode(setElements(lc, Node.accept(visitor, elements))); } - return this; } @@ -102,10 +114,35 @@ return Collections.unmodifiableList(elements); } - private ObjectNode setElements(final List<PropertyNode> elements) { + private ObjectNode setElements(final LexicalContext lc, final List<PropertyNode> elements) { if (this.elements == elements) { return this; } - return new ObjectNode(this, elements); + return Node.replaceInLexicalContext(lc, this, new ObjectNode(this, elements, this.splitRanges)); } + + /** + * Set the split ranges for this ObjectNode + * @see Splittable.SplitRange + * @param lc the lexical context + * @param splitRanges list of split ranges + * @return new or changed object node + */ + public ObjectNode setSplitRanges(final LexicalContext lc, final List<Splittable.SplitRange> splitRanges) { + if (this.splitRanges == splitRanges) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new ObjectNode(this, elements, splitRanges)); + } + + /** + * Get the split ranges for this ObjectNode, or null if the object is not split. + * @see Splittable.SplitRange + * @return list of split ranges + */ + @Override + public List<Splittable.SplitRange> getSplitRanges() { + return splitRanges == null ? null : Collections.unmodifiableList(splitRanges); + } + }
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -56,6 +56,8 @@ REFERENCE_ERROR, /** Delete operator */ DELETE(TokenType.DELETE, Type.BOOLEAN, 1), + /** Delete operator for slow scopes */ + SLOW_DELETE(TokenType.DELETE, Type.BOOLEAN, 1, false), /** Delete operator that always fails -- see Lower */ FAIL_DELETE(TokenType.DELETE, Type.BOOLEAN, 1, false), /** === operator with at least one object */ @@ -274,7 +276,7 @@ * * @param request a request * - * @return the inverted rquest, or null if not applicable + * @return the inverted request, or null if not applicable */ public static Request invert(final Request request) { switch (request) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/ir/Splittable.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, 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 java.io.Serializable; +import java.util.List; +import jdk.nashorn.internal.codegen.CompileUnit; + +/** + * An interface for splittable expressions. + */ +public interface Splittable { + + /** + * Get a list of split ranges for this splittable expression, or null + * if the expression should not be split. + * + * @return a list of split ranges + */ + List<SplitRange> getSplitRanges(); + + /** + * A SplitRange is a range in a splittable expression. It defines the + * boundaries of the split range and provides a compile unit for code generation. + */ + final class SplitRange implements CompileUnitHolder, Serializable { + private static final long serialVersionUID = 1L; + + /** Compile unit associated with the postsets range. */ + private final CompileUnit compileUnit; + + /** postsets range associated with the unit (hi not inclusive). */ + private final int low, high; + + /** + * Constructor + * @param compileUnit compile unit + * @param low lowest array index in unit + * @param high highest array index in unit + 1 + */ + public SplitRange(final CompileUnit compileUnit, final int low, final int high) { + this.compileUnit = compileUnit; + this.low = low; + this.high = high; + } + + /** + * Get the high index position of the ArrayUnit (exclusive) + * @return high index position + */ + public int getHigh() { + return high; + } + + /** + * Get the low index position of the ArrayUnit (inclusive) + * @return low index position + */ + public int getLow() { + return low; + } + + /** + * The array compile unit + * @return array compile unit + */ + @Override + public CompileUnit getCompileUnit() { + return compileUnit; + } + } +}
--- a/src/jdk/nashorn/internal/ir/SwitchNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/SwitchNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -53,7 +53,7 @@ private final boolean uniqueInteger; /** Tag symbol. */ - private Symbol tag; + private final Symbol tag; /** * Constructor @@ -71,15 +71,16 @@ this.cases = cases; this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase); this.uniqueInteger = false; + this.tag = null; } private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases, - final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger) { + final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger, final Symbol tag) { super(switchNode, conversion); this.expression = expression; this.cases = cases; this.defaultCaseIndex = defaultCaseIndex; - this.tag = switchNode.getTag(); //TODO are symbols inherited as references? + this.tag = tag; this.uniqueInteger = uniqueInteger; } @@ -89,7 +90,7 @@ for (final CaseNode caseNode : cases) { newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion())); } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger, tag)); } @Override @@ -157,7 +158,7 @@ if (this.cases == cases) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -189,7 +190,7 @@ if (this.expression == expression) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -204,10 +205,15 @@ /** * Set the tag symbol for this switch. The tag symbol is where * the switch expression result is stored + * @param lc lexical context * @param tag a symbol + * @return a switch node with the symbol set */ - public void setTag(final Symbol tag) { - this.tag = tag; + public SwitchNode setTag(final LexicalContext lc, final Symbol tag) { + if (this.tag == tag) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } /** @@ -229,12 +235,12 @@ if(this.uniqueInteger == uniqueInteger) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag)); } }
--- a/src/jdk/nashorn/internal/ir/Symbol.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Wed Dec 02 23:08:29 2015 -0800 @@ -25,7 +25,10 @@ package jdk.nashorn.internal.ir; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.PrintWriter; +import java.io.Serializable; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; @@ -47,7 +50,9 @@ * refer to their location. */ -public final class Symbol implements Comparable<Symbol> { +public final class Symbol implements Comparable<Symbol>, Cloneable, Serializable { + private static final long serialVersionUID = 1L; + /** Is this Global */ public static final int IS_GLOBAL = 1; /** Is this a variable */ @@ -94,10 +99,10 @@ /** First bytecode method local variable slot for storing the value(s) of this variable. -1 indicates the variable * is not stored in local variable slots or it is not yet known. */ - private int firstSlot = -1; + private transient int firstSlot = -1; /** Field number in scope or property; array index in varargs when not using arguments object. */ - private int fieldIndex = -1; + private transient int fieldIndex = -1; /** Number of times this symbol is used in code */ private int useCount; @@ -144,6 +149,15 @@ } } + @Override + public Symbol clone() { + try { + return (Symbol)super.clone(); + } catch (final CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + private static String align(final String string, final int max) { final StringBuilder sb = new StringBuilder(); sb.append(string.substring(0, Math.min(string.length(), max))); @@ -337,7 +351,7 @@ * Flag this symbol as scope as described in {@link Symbol#isScope()} * @return the symbol */ - public Symbol setIsScope() { + public Symbol setIsScope() { if (!isScope()) { if(shouldTrace()) { trace("SET IS SCOPE"); @@ -609,11 +623,11 @@ /** * Increase the symbol's use count by one. - * @return the symbol */ - public Symbol increaseUseCount() { - useCount++; - return this; + public void increaseUseCount() { + if (isScope()) { // Avoid dirtying a cache line; we only need the use count for scoped symbols + useCount++; + } } /** @@ -669,4 +683,10 @@ new Throwable().printStackTrace(Context.getCurrentErr()); } } + + private void readObject(final ObjectInputStream in) throws ClassNotFoundException, IOException { + in.defaultReadObject(); + firstSlot = -1; + fieldIndex = -1; + } }
--- a/src/jdk/nashorn/internal/ir/TryNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/TryNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -65,7 +65,7 @@ private final List<Block> inlinedFinallies; /** Exception symbol. */ - private Symbol exception; + private final Symbol exception; private final LocalVariableConversion conversion; @@ -86,22 +86,23 @@ this.finallyBody = finallyBody; this.conversion = null; this.inlinedFinallies = Collections.emptyList(); + this.exception = null; } - private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies) { + private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies, final Symbol exception) { super(tryNode); this.body = body; this.catchBlocks = catchBlocks; this.finallyBody = finallyBody; this.conversion = conversion; this.inlinedFinallies = inlinedFinallies; - this.exception = tryNode.exception; + this.exception = exception; } @Override public Node ensureUniqueLabels(final LexicalContext lc) { //try nodes are never in lex context - return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); } @Override @@ -160,7 +161,7 @@ if (this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -197,7 +198,7 @@ if (this.catchBlocks == catchBlocks) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -209,12 +210,15 @@ } /** * Set the exception symbol for this try block + * @param lc lexical context * @param exception a symbol for the compiler to store the exception in * @return new TryNode or same if unchanged */ - public TryNode setException(final Symbol exception) { - this.exception = exception; - return this; + public TryNode setException(final LexicalContext lc, final Symbol exception) { + if (this.exception == exception) { + return this; + } + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -277,7 +281,7 @@ if (this.finallyBody == finallyBody) { return this; } - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } /** @@ -293,7 +297,7 @@ return this; } assert checkInlinedFinallies(inlinedFinallies); - return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); } private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) { @@ -314,7 +318,7 @@ if(this.conversion == conversion) { return this; } - return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); } @Override
--- a/src/jdk/nashorn/internal/ir/VarNode.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/VarNode.java Wed Dec 02 23:08:29 2015 -0800 @@ -262,12 +262,4 @@ public boolean isFunctionDeclaration() { return init instanceof FunctionNode && ((FunctionNode)init).isDeclared(); } - - /** - * Returns true if this is an anonymous function declaration. - * @return true if this is an anonymous function declaration. - */ - public boolean isAnonymousFunctionDeclaration() { - return isFunctionDeclaration() && ((FunctionNode)init).isAnonymous(); - } }
--- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Wed Dec 02 23:08:29 2015 -0800 @@ -48,7 +48,6 @@ import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; @@ -65,7 +64,7 @@ import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.parser.JSONParser; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.Parser; @@ -77,7 +76,7 @@ /** * This IR writer produces a JSON string that represents AST as a JSON string. */ -public final class JSONWriter extends NodeVisitor<LexicalContext> { +public final class JSONWriter extends SimpleNodeVisitor { /** * Returns AST as JSON compatible string. @@ -939,7 +938,6 @@ // Internals below private JSONWriter(final boolean includeLocation) { - super(new LexicalContext()); this.buf = new StringBuilder(); this.includeLocation = includeLocation; }
--- a/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java Wed Dec 02 23:08:29 2015 -0800 @@ -36,7 +36,7 @@ import jdk.nashorn.internal.ir.debug.NashornTextifier.NashornLabel; /** - * Subclass of the ASM classs reader that retains more info, such + * Subclass of the ASM class reader that retains more info, such * as bytecode offsets */ public class NashornClassReader extends ClassReader {
--- a/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java Wed Dec 02 23:08:29 2015 -0800 @@ -193,7 +193,7 @@ } /** - * Get the class histograpm + * Get the class histogram * @return class histogram element list */ public List<ClassHistogramElement> getClassHistogram() {
--- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Wed Dec 02 23:08:29 2015 -0800 @@ -41,7 +41,6 @@ import jdk.nashorn.internal.ir.JoinPredecessor; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.SplitNode; @@ -53,7 +52,7 @@ import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; /** * Print out the AST as human readable source code. @@ -61,7 +60,7 @@ * * see the flags --print-parse and --print-lower-parse */ -public final class PrintVisitor extends NodeVisitor<LexicalContext> { +public final class PrintVisitor extends SimpleNodeVisitor { /** Tab width */ private static final int TABWIDTH = 4; @@ -96,7 +95,6 @@ * @param printTypes should we print optimistic and inferred types? */ public PrintVisitor(final boolean printLineNumbers, final boolean printTypes) { - super(new LexicalContext()); this.EOLN = System.lineSeparator(); this.sb = new StringBuilder(); this.printLineNumbers = printLineNumbers;
--- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Wed Dec 02 23:08:29 2015 -0800 @@ -34,7 +34,7 @@ * Like NodeVisitor but navigating further into operators. * @param <T> Lexical context class for this NodeOperatorVisitor */ -public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T> { +public abstract class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T> { /** * Constructor *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/ir/visitor/SimpleNodeVisitor.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.visitor; + +import jdk.nashorn.internal.ir.LexicalContext; + +/** + * Convenience base class for a {@link NodeVisitor} with a plain {@link LexicalContext}. + */ +public abstract class SimpleNodeVisitor extends NodeVisitor<LexicalContext> { + + /** + * Creates a new simple node visitor. + */ + public SimpleNodeVisitor() { + super(new LexicalContext()); + } +}
--- a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java Wed Dec 02 23:08:29 2015 -0800 @@ -131,8 +131,8 @@ static Object traceReturn(final DebugLogger logger, final Object value) { final String str = " return" + (VOID_TAG.equals(value) ? - ";" : - " " + stripName(value) + "; // [type=" + (value == null ? "null]" : stripName(value.getClass()) + ']')); + ";" : + " " + stripName(value) + "; // [type=" + (value == null ? "null]" : stripName(value.getClass()) + ']')); if (logger == null) { err(str); } else if (logger.isEnabled()) { @@ -164,13 +164,13 @@ } sb.append('\''). - append(stripName(argString(args[i]))). - append('\''). - append(' '). - append('['). - append("type="). - append(args[i] == null ? "null" : stripName(args[i].getClass())). - append(']'); + append(stripName(argString(args[i]))). + append('\''). + append(' '). + append('['). + append("type="). + append(args[i] == null ? "null" : stripName(args[i].getClass())). + append(']'); if (i + 1 < args.length) { sb.append(", "); @@ -216,8 +216,8 @@ if (arg instanceof ScriptObject) { return arg.toString() + - " (map=" + Debug.id(((ScriptObject)arg).getMap()) + - ')'; + " (map=" + Debug.id(((ScriptObject)arg).getMap()) + + ')'; } return arg.toString(); @@ -262,7 +262,7 @@ return addDebugPrintout(null, Level.OFF, mh, paramStart, printReturnValue, tag); } - /** + /** * Add a debug printout to a method handle, tracing parameters and return values * * @param logger a specific logger to which to write the output @@ -278,7 +278,7 @@ //if there is no logger, or if it's set to log only coarser events //than the trace level, skip and return - if (logger != null && logger.levelCoarserThan(level)) { + if (logger == null || !logger.isLoggable(level)) { return mh; } @@ -289,9 +289,9 @@ trace = MethodHandles.foldArguments( mh, trace.asCollector( - Object[].class, - type.parameterCount()). - asType(type.changeReturnType(void.class))); + Object[].class, + type.parameterCount()). + asType(type.changeReturnType(void.class))); final Class<?> retType = type.returnType(); if (printReturnValue) { @@ -299,7 +299,7 @@ final MethodHandle traceReturn = MethodHandles.insertArguments(TRACE_RETURN, 0, logger); trace = MethodHandles.filterReturnValue(trace, traceReturn.asType( - traceReturn.type().changeParameterType(0, retType).changeReturnType(retType))); + traceReturn.type().changeParameterType(0, retType).changeReturnType(retType))); } else { trace = MethodHandles.filterReturnValue(trace, MethodHandles.insertArguments(TRACE_RETURN_VOID, 0, logger)); } @@ -355,9 +355,9 @@ sb.append("] "); } else { sb.append(d) - .append('{') - .append(Integer.toHexString(System.identityHashCode(d))) - .append('}'); + .append('{') + .append(Integer.toHexString(System.identityHashCode(d))) + .append('}'); } if (i + 1 < data.length) {
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java Wed Dec 02 23:08:29 2015 -0800 @@ -192,7 +192,7 @@ /** * Factory method for array data * - * @param nb underlying nativebuffer + * @param nb underlying native buffer * @param start start element * @param end end element *
--- a/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java Tue Dec 01 22:55:46 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +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.objects; - -import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptFunctionData; -import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.ScriptRuntime; - -/** - * A {@code ScriptFunctionImpl} subclass for functions created using {@code Function.prototype.bind}. Such functions - * must track their {@code [[TargetFunction]]} property for purposes of correctly implementing {@code [[HasInstance]]}; - * see {@link ScriptFunction#isInstance(ScriptObject)}. - */ -final class BoundScriptFunctionImpl extends ScriptFunctionImpl { - private final ScriptFunction targetFunction; - - BoundScriptFunctionImpl(final ScriptFunctionData data, final ScriptFunction targetFunction) { - super(data, Global.instance()); - setPrototype(ScriptRuntime.UNDEFINED); - this.targetFunction = targetFunction; - } - - @Override - protected ScriptFunction getTargetFunction() { - return targetFunction; - } -}
--- a/src/jdk/nashorn/internal/objects/Global.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/Global.java Wed Dec 02 23:08:29 2015 -0800 @@ -88,14 +88,14 @@ */ @ScriptClass("Global") public final class Global extends Scope { - // Placeholder value used in place of a location property (__FILE__, __DIR__, __LINE__) - private static final Object LOCATION_PROPERTY_PLACEHOLDER = new Object(); + // This special value is used to flag a lazily initialized global property. + // This also serves as placeholder value used in place of a location property + // (__FILE__, __DIR__, __LINE__) + private static final Object LAZY_SENTINEL = new Object(); + private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class); private final InvokeByName VALUE_OF = new InvokeByName("valueOf", ScriptObject.class); - // placeholder value for lazily initialized global objects - private static final Object LAZY_SENTINEL = new Object(); - /** * Optimistic builtin names that require switchpoint invalidation * upon assignment. Overly conservative, but works for now, to avoid @@ -182,15 +182,15 @@ /** Value property NaN of the Global Object - ECMA 15.1.1.1 NaN */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final double NaN = Double.NaN; + public static final double NaN = Double.NaN; /** Value property Infinity of the Global Object - ECMA 15.1.1.2 Infinity */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final double Infinity = Double.POSITIVE_INFINITY; + public static final double Infinity = Double.POSITIVE_INFINITY; /** Value property Undefined of the Global Object - ECMA 15.1.1.3 Undefined */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final Object undefined = UNDEFINED; + public static final Object undefined = UNDEFINED; /** ECMA 15.1.2.1 eval(x) */ @Property(attributes = Attribute.NOT_ENUMERABLE) @@ -830,15 +830,15 @@ /** Nashorn extension: current script's file name */ @Property(name = "__FILE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final Object __FILE__ = LOCATION_PROPERTY_PLACEHOLDER; + public static final Object __FILE__ = LAZY_SENTINEL; /** Nashorn extension: current script's directory */ @Property(name = "__DIR__", attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final Object __DIR__ = LOCATION_PROPERTY_PLACEHOLDER; + public static final Object __DIR__ = LAZY_SENTINEL; /** Nashorn extension: current source line number being executed */ @Property(name = "__LINE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final Object __LINE__ = LOCATION_PROPERTY_PLACEHOLDER; + public static final Object __LINE__ = LAZY_SENTINEL; private volatile NativeDate DEFAULT_DATE; @@ -928,8 +928,6 @@ private ThreadLocal<ScriptContext> scontext; // current ScriptEngine associated - can be null. private ScriptEngine engine; - // initial ScriptContext - can be null - private volatile ScriptContext initscontext; // ES6 global lexical scope. private final LexicalScope lexicalScope; @@ -957,7 +955,7 @@ private ScriptContext currentContext() { final ScriptContext sc = scontext != null? scontext.get() : null; - return sc == null? initscontext : sc; + return (sc != null)? sc : (engine != null? engine.getContext() : null); } @Override @@ -1067,16 +1065,14 @@ * of the global scope object. * * @param eng ScriptEngine to initialize - * @param ctxt ScriptContext to initialize */ - public void initBuiltinObjects(final ScriptEngine eng, final ScriptContext ctxt) { + public void initBuiltinObjects(final ScriptEngine eng) { if (this.builtinObject != null) { // already initialized, just return return; } this.engine = eng; - this.initscontext = ctxt; if (this.engine != null) { this.scontext = new ThreadLocal<>(); } @@ -1583,7 +1579,11 @@ return ScriptFunction.getPrototype(builtinObject); } - ScriptObject getFunctionPrototype() { + /** + * Get the builtin Function prototype. + * @return the Function.prototype. + */ + public ScriptObject getFunctionPrototype() { return ScriptFunction.getPrototype(builtinFunction); } @@ -1768,36 +1768,13 @@ return ScriptFunction.getPrototype(getBuiltinFloat64Array()); } - private ScriptFunction getBuiltinArray() { - return builtinArray; - } - - ScriptFunction getTypeErrorThrower() { - return typeErrorThrower; - } - /** - * Called from compiled script code to test if builtin has been overridden + * Return the function that throws TypeError unconditionally. Used as "poison" methods for certain Function properties. * - * @return true if builtin array has not been overridden + * @return the TypeError throwing function */ - public static boolean isBuiltinArray() { - final Global instance = Global.instance(); - return instance.array == instance.getBuiltinArray(); - } - - private ScriptFunction getBuiltinBoolean() { - return builtinBoolean; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin boolean has not been overridden - */ - public static boolean isBuiltinBoolean() { - final Global instance = Global.instance(); - return instance._boolean == instance.getBuiltinBoolean(); + public ScriptFunction getTypeErrorThrower() { + return typeErrorThrower; } private synchronized ScriptFunction getBuiltinDate() { @@ -1810,30 +1787,6 @@ return this.builtinDate; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin date has not been overridden - */ - public static boolean isBuiltinDate() { - final Global instance = Global.instance(); - return instance.date == LAZY_SENTINEL || instance.date == instance.getBuiltinDate(); - } - - private ScriptFunction getBuiltinError() { - return builtinError; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin error has not been overridden - */ - public static boolean isBuiltinError() { - final Global instance = Global.instance(); - return instance.error == instance.getBuiltinError(); - } - private synchronized ScriptFunction getBuiltinEvalError() { if (this.builtinEvalError == null) { this.builtinEvalError = initErrorSubtype("EvalError", getErrorPrototype()); @@ -1841,31 +1794,11 @@ return this.builtinEvalError; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin eval error has not been overridden - */ - public static boolean isBuiltinEvalError() { - final Global instance = Global.instance(); - return instance.evalError == LAZY_SENTINEL || instance.evalError == instance.getBuiltinEvalError(); - } - private ScriptFunction getBuiltinFunction() { return builtinFunction; } /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin function has not been overridden - */ - public static boolean isBuiltinFunction() { - final Global instance = Global.instance(); - return instance.function == instance.getBuiltinFunction(); - } - - /** * Get the switchpoint used to check property changes for Function.prototype.apply * @return the switchpoint guarding apply (same as guarding call, and everything else in function) */ @@ -1906,16 +1839,6 @@ return builtinJSAdapter; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin JSAdapter has not been overridden - */ - public static boolean isBuiltinJSAdapter() { - final Global instance = Global.instance(); - return instance.jsadapter == LAZY_SENTINEL || instance.jsadapter == instance.getBuiltinJSAdapter(); - } - private synchronized ScriptObject getBuiltinJSON() { if (this.builtinJSON == null) { this.builtinJSON = initConstructorAndSwitchPoint("JSON", ScriptObject.class); @@ -1923,44 +1846,6 @@ return this.builtinJSON; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin JSON has has not been overridden - */ - public static boolean isBuiltinJSON() { - final Global instance = Global.instance(); - return instance.json == LAZY_SENTINEL || instance.json == instance.getBuiltinJSON(); - } - - private ScriptObject getBuiltinJava() { - return builtinJava; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin Java has not been overridden - */ - public static boolean isBuiltinJava() { - final Global instance = Global.instance(); - return instance.java == instance.getBuiltinJava(); - } - - private ScriptObject getBuiltinJavax() { - return builtinJavax; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin Javax has not been overridden - */ - public static boolean isBuiltinJavax() { - final Global instance = Global.instance(); - return instance.javax == instance.getBuiltinJavax(); - } - private synchronized ScriptFunction getBuiltinJavaImporter() { if (this.builtinJavaImporter == null) { this.builtinJavaImporter = initConstructor("JavaImporter", ScriptFunction.class); @@ -1975,68 +1860,6 @@ return this.builtinJavaApi; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin Java importer has not been overridden - */ - public static boolean isBuiltinJavaImporter() { - final Global instance = Global.instance(); - return instance.javaImporter == LAZY_SENTINEL || instance.javaImporter == instance.getBuiltinJavaImporter(); - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin math has not been overridden - */ - public static boolean isBuiltinMath() { - final Global instance = Global.instance(); - return instance.math == instance.builtinMath; - } - - private ScriptFunction getBuiltinNumber() { - return builtinNumber; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin number has not been overridden - */ - public static boolean isBuiltinNumber() { - final Global instance = Global.instance(); - return instance.number == instance.getBuiltinNumber(); - } - - private ScriptFunction getBuiltinObject() { - return builtinObject; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin object has not been overridden - */ - public static boolean isBuiltinObject() { - final Global instance = Global.instance(); - return instance.object == instance.getBuiltinObject(); - } - - private ScriptObject getBuiltinPackages() { - return builtinPackages; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin package has not been overridden - */ - public static boolean isBuiltinPackages() { - final Global instance = Global.instance(); - return instance.packages == instance.getBuiltinPackages(); - } - private synchronized ScriptFunction getBuiltinRangeError() { if (this.builtinRangeError == null) { this.builtinRangeError = initErrorSubtype("RangeError", getErrorPrototype()); @@ -2044,30 +1867,6 @@ return builtinRangeError; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin range error has not been overridden - */ - public static boolean isBuiltinRangeError() { - final Global instance = Global.instance(); - return instance.rangeError == LAZY_SENTINEL || instance.rangeError == instance.getBuiltinRangeError(); - } - - private synchronized ScriptFunction getBuiltinReferenceError() { - return builtinReferenceError; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin reference error has not been overridden - */ - public static boolean isBuiltinReferenceError() { - final Global instance = Global.instance(); - return instance.referenceError == instance.getBuiltinReferenceError(); - } - private synchronized ScriptFunction getBuiltinRegExp() { if (this.builtinRegExp == null) { this.builtinRegExp = initConstructorAndSwitchPoint("RegExp", ScriptFunction.class); @@ -2081,58 +1880,6 @@ return builtinRegExp; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin regexp has not been overridden - */ - public static boolean isBuiltinRegExp() { - final Global instance = Global.instance(); - return instance.regexp == LAZY_SENTINEL || instance.regexp == instance.getBuiltinRegExp(); - } - - private ScriptFunction getBuiltinString() { - return builtinString; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin Java has not been overridden - */ - public static boolean isBuiltinString() { - final Global instance = Global.instance(); - return instance.string == instance.getBuiltinString(); - } - - private ScriptFunction getBuiltinSyntaxError() { - return builtinSyntaxError; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin syntax error has not been overridden - */ - public static boolean isBuiltinSyntaxError() { - final Global instance = Global.instance(); - return instance.syntaxError == instance.getBuiltinSyntaxError(); - } - - private ScriptFunction getBuiltinTypeError() { - return builtinTypeError; - } - - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin type error has not been overridden - */ - public static boolean isBuiltinTypeError() { - final Global instance = Global.instance(); - return instance.typeError == instance.getBuiltinTypeError(); - } - private synchronized ScriptFunction getBuiltinURIError() { if (this.builtinURIError == null) { this.builtinURIError = initErrorSubtype("URIError", getErrorPrototype()); @@ -2140,16 +1887,6 @@ return this.builtinURIError; } - /** - * Called from compiled script code to test if builtin has been overridden - * - * @return true if builtin URI error has not been overridden - */ - public static boolean isBuiltinURIError() { - final Global instance = Global.instance(); - return instance.uriError == LAZY_SENTINEL || instance.uriError == instance.getBuiltinURIError(); - } - @Override public String getClassName() { return "global"; @@ -2288,7 +2025,7 @@ * @return true if the value is a placeholder, false otherwise. */ public static boolean isLocationPropertyPlaceholder(final Object placeholder) { - return placeholder == LOCATION_PROPERTY_PLACEHOLDER; + return placeholder == LAZY_SENTINEL; } /** @@ -2386,17 +2123,18 @@ } } + final boolean extensible = isExtensible(); for (final jdk.nashorn.internal.runtime.Property property : properties) { if (property.isLexicalBinding()) { assert lexScope != null; - lexicalMap = lexScope.addBoundProperty(lexicalMap, source, property); + lexicalMap = lexScope.addBoundProperty(lexicalMap, source, property, true); if (ownMap.findProperty(property.getKey()) != null) { // If property exists in the global object invalidate any global constant call sites. invalidateGlobalConstant(property.getKey()); } } else { - ownMap = addBoundProperty(ownMap, source, property); + ownMap = addBoundProperty(ownMap, source, property, extensible); } } @@ -2424,7 +2162,7 @@ // We want to avoid adding our generic lexical scope switchpoint to global constant invocations, // because those are invalidated per-key in the addBoundProperties method above. - // We therefor check if the invocation does already have a switchpoint and the property is non-inherited, + // We therefore check if the invocation does already have a switchpoint and the property is non-inherited, // assuming this only applies to global constants. If other non-inherited properties will // start using switchpoints some time in the future we'll have to revisit this. if (isScope && context.getEnv()._es6 && (invocation.getSwitchPoints() == null || !hasOwnProperty(name))) { @@ -2469,10 +2207,10 @@ * Adds jjs shell interactive mode builtin functions to global scope. */ public void addShellBuiltins() { - Object value = ScriptFunctionImpl.makeFunction("input", ShellFunctions.INPUT); + Object value = ScriptFunction.createBuiltin("input", ShellFunctions.INPUT); addOwnProperty("input", Attribute.NOT_ENUMERABLE, value); - value = ScriptFunctionImpl.makeFunction("evalinput", ShellFunctions.EVALINPUT); + value = ScriptFunction.createBuiltin("evalinput", ShellFunctions.EVALINPUT); addOwnProperty("evalinput", Attribute.NOT_ENUMERABLE, value); } @@ -2519,35 +2257,35 @@ this.setInitialProto(getObjectPrototype()); // initialize global function properties - this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL); - - this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT, + this.eval = this.builtinEval = ScriptFunction.createBuiltin("eval", EVAL); + + this.parseInt = ScriptFunction.createBuiltin("parseInt", GlobalFunctions.PARSEINT, new Specialization[] { new Specialization(GlobalFunctions.PARSEINT_Z), new Specialization(GlobalFunctions.PARSEINT_I), new Specialization(GlobalFunctions.PARSEINT_J), new Specialization(GlobalFunctions.PARSEINT_OI), new Specialization(GlobalFunctions.PARSEINT_O) }); - this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT); - this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN, + this.parseFloat = ScriptFunction.createBuiltin("parseFloat", GlobalFunctions.PARSEFLOAT); + this.isNaN = ScriptFunction.createBuiltin("isNaN", GlobalFunctions.IS_NAN, new Specialization[] { new Specialization(GlobalFunctions.IS_NAN_I), new Specialization(GlobalFunctions.IS_NAN_J), new Specialization(GlobalFunctions.IS_NAN_D) }); - this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT); - this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN); - this.isFinite = ScriptFunctionImpl.makeFunction("isFinite", GlobalFunctions.IS_FINITE); - this.encodeURI = ScriptFunctionImpl.makeFunction("encodeURI", GlobalFunctions.ENCODE_URI); - this.encodeURIComponent = ScriptFunctionImpl.makeFunction("encodeURIComponent", GlobalFunctions.ENCODE_URICOMPONENT); - this.decodeURI = ScriptFunctionImpl.makeFunction("decodeURI", GlobalFunctions.DECODE_URI); - this.decodeURIComponent = ScriptFunctionImpl.makeFunction("decodeURIComponent", GlobalFunctions.DECODE_URICOMPONENT); - this.escape = ScriptFunctionImpl.makeFunction("escape", GlobalFunctions.ESCAPE); - this.unescape = ScriptFunctionImpl.makeFunction("unescape", GlobalFunctions.UNESCAPE); - this.print = ScriptFunctionImpl.makeFunction("print", env._print_no_newline ? PRINT : PRINTLN); - this.load = ScriptFunctionImpl.makeFunction("load", LOAD); - this.loadWithNewGlobal = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOAD_WITH_NEW_GLOBAL); - this.exit = ScriptFunctionImpl.makeFunction("exit", EXIT); - this.quit = ScriptFunctionImpl.makeFunction("quit", EXIT); + this.parseFloat = ScriptFunction.createBuiltin("parseFloat", GlobalFunctions.PARSEFLOAT); + this.isNaN = ScriptFunction.createBuiltin("isNaN", GlobalFunctions.IS_NAN); + this.isFinite = ScriptFunction.createBuiltin("isFinite", GlobalFunctions.IS_FINITE); + this.encodeURI = ScriptFunction.createBuiltin("encodeURI", GlobalFunctions.ENCODE_URI); + this.encodeURIComponent = ScriptFunction.createBuiltin("encodeURIComponent", GlobalFunctions.ENCODE_URICOMPONENT); + this.decodeURI = ScriptFunction.createBuiltin("decodeURI", GlobalFunctions.DECODE_URI); + this.decodeURIComponent = ScriptFunction.createBuiltin("decodeURIComponent", GlobalFunctions.DECODE_URICOMPONENT); + this.escape = ScriptFunction.createBuiltin("escape", GlobalFunctions.ESCAPE); + this.unescape = ScriptFunction.createBuiltin("unescape", GlobalFunctions.UNESCAPE); + this.print = ScriptFunction.createBuiltin("print", env._print_no_newline ? PRINT : PRINTLN); + this.load = ScriptFunction.createBuiltin("load", LOAD); + this.loadWithNewGlobal = ScriptFunction.createBuiltin("loadWithNewGlobal", LOAD_WITH_NEW_GLOBAL); + this.exit = ScriptFunction.createBuiltin("exit", EXIT); + this.quit = ScriptFunction.createBuiltin("quit", EXIT); // built-in constructors this.builtinArray = initConstructorAndSwitchPoint("Array", ScriptFunction.class); @@ -2627,7 +2365,7 @@ // default file name addOwnProperty(ScriptEngine.FILENAME, Attribute.NOT_ENUMERABLE, null); // __noSuchProperty__ hook for ScriptContext search of missing variables - final ScriptFunction noSuchProp = ScriptFunctionImpl.makeStrictFunction(NO_SUCH_PROPERTY_NAME, NO_SUCH_PROPERTY); + final ScriptFunction noSuchProp = ScriptFunction.createStrictBuiltin(NO_SUCH_PROPERTY_NAME, NO_SUCH_PROPERTY); addOwnProperty(NO_SUCH_PROPERTY_NAME, Attribute.NOT_ENUMERABLE, noSuchProp); } } @@ -2638,17 +2376,17 @@ final ScriptObject errorProto = getErrorPrototype(); // Nashorn specific accessors on Error.prototype - stack, lineNumber, columnNumber and fileName - final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", NativeError.GET_STACK); - final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", NativeError.SET_STACK); + final ScriptFunction getStack = ScriptFunction.createBuiltin("getStack", NativeError.GET_STACK); + final ScriptFunction setStack = ScriptFunction.createBuiltin("setStack", NativeError.SET_STACK); errorProto.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); - final ScriptFunction getLineNumber = ScriptFunctionImpl.makeFunction("getLineNumber", NativeError.GET_LINENUMBER); - final ScriptFunction setLineNumber = ScriptFunctionImpl.makeFunction("setLineNumber", NativeError.SET_LINENUMBER); + final ScriptFunction getLineNumber = ScriptFunction.createBuiltin("getLineNumber", NativeError.GET_LINENUMBER); + final ScriptFunction setLineNumber = ScriptFunction.createBuiltin("setLineNumber", NativeError.SET_LINENUMBER); errorProto.addOwnProperty("lineNumber", Attribute.NOT_ENUMERABLE, getLineNumber, setLineNumber); - final ScriptFunction getColumnNumber = ScriptFunctionImpl.makeFunction("getColumnNumber", NativeError.GET_COLUMNNUMBER); - final ScriptFunction setColumnNumber = ScriptFunctionImpl.makeFunction("setColumnNumber", NativeError.SET_COLUMNNUMBER); + final ScriptFunction getColumnNumber = ScriptFunction.createBuiltin("getColumnNumber", NativeError.GET_COLUMNNUMBER); + final ScriptFunction setColumnNumber = ScriptFunction.createBuiltin("setColumnNumber", NativeError.SET_COLUMNNUMBER); errorProto.addOwnProperty("columnNumber", Attribute.NOT_ENUMERABLE, getColumnNumber, setColumnNumber); - final ScriptFunction getFileName = ScriptFunctionImpl.makeFunction("getFileName", NativeError.GET_FILENAME); - final ScriptFunction setFileName = ScriptFunctionImpl.makeFunction("setFileName", NativeError.SET_FILENAME); + final ScriptFunction getFileName = ScriptFunction.createBuiltin("getFileName", NativeError.GET_FILENAME); + final ScriptFunction setFileName = ScriptFunction.createBuiltin("setFileName", NativeError.SET_FILENAME); errorProto.addOwnProperty("fileName", Attribute.NOT_ENUMERABLE, getFileName, setFileName); // ECMA 15.11.4.2 Error.prototype.name @@ -2687,20 +2425,22 @@ } private void initScripting(final ScriptEnvironment scriptEnv) { - Object value; - value = ScriptFunctionImpl.makeFunction("readLine", ScriptingFunctions.READLINE); + ScriptObject value; + value = ScriptFunction.createBuiltin("readLine", ScriptingFunctions.READLINE); addOwnProperty("readLine", Attribute.NOT_ENUMERABLE, value); - value = ScriptFunctionImpl.makeFunction("readFully", ScriptingFunctions.READFULLY); + value = ScriptFunction.createBuiltin("readFully", ScriptingFunctions.READFULLY); addOwnProperty("readFully", Attribute.NOT_ENUMERABLE, value); final String execName = ScriptingFunctions.EXEC_NAME; - value = ScriptFunctionImpl.makeFunction(execName, ScriptingFunctions.EXEC); + value = ScriptFunction.createBuiltin(execName, ScriptingFunctions.EXEC); + value.addOwnProperty(ScriptingFunctions.THROW_ON_ERROR_NAME, Attribute.NOT_ENUMERABLE, false); + addOwnProperty(execName, Attribute.NOT_ENUMERABLE, value); // Nashorn extension: global.echo (scripting-mode-only) // alias for "print" - value = get("print"); + value = (ScriptObject)get("print"); addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value); // Nashorn extension: global.$OPTIONS (scripting-mode-only) @@ -2876,7 +2616,7 @@ this.builtinFunction = initConstructor("Function", ScriptFunction.class); // create global anonymous function - final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction(); + final ScriptFunction anon = ScriptFunction.createAnonymous(); // need to copy over members of Function.prototype to anon function anon.addBoundProperties(getFunctionPrototype()); @@ -2888,10 +2628,7 @@ anon.deleteOwnProperty(anon.getMap().findProperty("prototype")); // use "getter" so that [[ThrowTypeError]] function's arity is 0 - as specified in step 10 of section 13.2.3 - this.typeErrorThrower = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER, null, null, 0); - typeErrorThrower.setPrototype(UNDEFINED); - // Non-constructor built-in functions do not have "prototype" property - typeErrorThrower.deleteOwnProperty(typeErrorThrower.getMap().findProperty("prototype")); + this.typeErrorThrower = ScriptFunction.createBuiltin("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER); typeErrorThrower.preventExtensions(); // now initialize Object @@ -2902,8 +2639,8 @@ // ES6 draft compliant __proto__ property of Object.prototype // accessors on Object.prototype for "__proto__" - final ScriptFunction getProto = ScriptFunctionImpl.makeFunction("getProto", NativeObject.GET__PROTO__); - final ScriptFunction setProto = ScriptFunctionImpl.makeFunction("setProto", NativeObject.SET__PROTO__); + final ScriptFunction getProto = ScriptFunction.createBuiltin("getProto", NativeObject.GET__PROTO__); + final ScriptFunction setProto = ScriptFunction.createBuiltin("setProto", NativeObject.SET__PROTO__); ObjectPrototype.addOwnProperty("__proto__", Attribute.NOT_ENUMERABLE, getProto, setProto); // Function valued properties of Function.prototype were not properly @@ -2999,9 +2736,9 @@ } @Override - protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final jdk.nashorn.internal.runtime.Property property) { + protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final jdk.nashorn.internal.runtime.Property property, final boolean extensible) { // We override this method just to make it callable by Global - return super.addBoundProperty(propMap, source, property); + return super.addBoundProperty(propMap, source, property, extensible); } private static GuardedInvocation filterInvocation(final GuardedInvocation invocation) {
--- a/src/jdk/nashorn/internal/objects/NativeDataView.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeDataView.java Wed Dec 02 23:08:29 2015 -0800 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
--- a/src/jdk/nashorn/internal/objects/NativeDebug.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeDebug.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; + import java.io.PrintWriter; import java.util.LinkedList; import java.util.Objects; @@ -246,7 +247,7 @@ final PrintWriter out = Context.getCurrentErr(); out.println("ScriptObject count " + ScriptObject.getCount()); - out.println("Scope count " + Scope.getCount()); + out.println("Scope count " + Scope.getScopeCount()); out.println("ScriptObject listeners added " + PropertyListeners.getListenersAdded()); out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved()); out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount());
--- a/src/jdk/nashorn/internal/objects/NativeError.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeError.java Wed Dec 02 23:08:29 2015 -0800 @@ -150,8 +150,8 @@ initException(sobj); sobj.delete(STACK, false); if (! sobj.has("stack")) { - final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", GET_STACK); - final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", SET_STACK); + final ScriptFunction getStack = ScriptFunction.createBuiltin("getStack", GET_STACK); + final ScriptFunction setStack = ScriptFunction.createBuiltin("setStack", SET_STACK); sobj.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); } return UNDEFINED;
--- a/src/jdk/nashorn/internal/objects/NativeFunction.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeFunction.java Wed Dec 02 23:08:29 2015 -0800 @@ -108,7 +108,7 @@ throw new AssertionError("Should not reach here"); } - /** + /** * Given an array-like object, converts it into a Java object array suitable for invocation of ScriptRuntime.apply * or for direct invocation of the applied function. * @param array the array-like object. Can be null in which case a zero-length array is created.
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Wed Dec 02 23:08:29 2015 -0800 @@ -621,13 +621,13 @@ if (find != null) { final Object value = find.getObjectValue(); if (value instanceof ScriptFunction) { - final ScriptFunctionImpl func = (ScriptFunctionImpl)value; + final ScriptFunction func = (ScriptFunction)value; // TODO: It's a shame we need to produce a function bound to this and name, when we'd only need it bound // 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), + func.createBound(this, new Object[] { name })), 0, Object.class), testJSAdaptor(adaptee, null, null, null), - adaptee.getProtoSwitchPoint(__call__, find.getOwner())); + adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null); } } throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this)); @@ -698,7 +698,7 @@ return new GuardedInvocation( methodHandle, testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func), - adaptee.getProtoSwitchPoint(hook, findData.getOwner())); + adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null); } } } @@ -710,7 +710,7 @@ final MethodHandle methodHandle = hook.equals(__put__) ? MH.asType(Lookup.EMPTY_SETTER, type) : Lookup.emptyGetter(type.returnType()); - return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoint(hook, null)); + return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null); } }
--- a/src/jdk/nashorn/internal/objects/NativeJava.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeJava.java Wed Dec 02 23:08:29 2015 -0800 @@ -93,7 +93,7 @@ @Function(name="synchronized", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object synchronizedFunc(final Object self, final Object func, final Object obj) { if (func instanceof ScriptFunction) { - return ((ScriptFunction)func).makeSynchronizedFunction(obj); + return ((ScriptFunction)func).createSynchronized(obj); } throw typeError("not.a.function", ScriptRuntime.safeToString(func)); @@ -342,7 +342,8 @@ /** * Given a script object and a Java type, converts the script object into the desired Java type. Currently it * performs shallow creation of Java arrays, as well as wrapping of objects in Lists, Dequeues, Queues, - * and Collections. Example: + * and Collections. If conversion is not possible or fails for some reason, TypeError is thrown. + * Example: * <pre> * var anArray = [1, "13", false] * var javaIntArray = Java.to(anArray, "int[]") @@ -386,7 +387,11 @@ } if(targetClass.isArray()) { - return JSType.toJavaArray(obj, targetClass.getComponentType()); + try { + return JSType.toJavaArray(obj, targetClass.getComponentType()); + } catch (final Exception exp) { + throw typeError(exp, "java.array.conversion.failed", targetClass.getName()); + } } if (targetClass == List.class || targetClass == Deque.class || targetClass == Queue.class || targetClass == Collection.class) {
--- a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Wed Dec 02 23:08:29 2015 -0800 @@ -134,7 +134,7 @@ } @Override - protected Object invokeNoSuchProperty(final String name, final int programPoint) { + protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) { final Object retval = createProperty(name); if (isValid(programPoint)) { throw new UnwarrantedOptimismException(retval, programPoint);
--- a/src/jdk/nashorn/internal/objects/NativeNumber.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeNumber.java Wed Dec 02 23:08:29 2015 -0800 @@ -33,6 +33,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.math.RoundingMode; import java.text.NumberFormat; import java.util.Locale; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -187,6 +188,7 @@ format.setMinimumFractionDigits(fractionDigits); format.setMaximumFractionDigits(fractionDigits); format.setGroupingUsed(false); + format.setRoundingMode(RoundingMode.HALF_UP); return format.format(x); }
--- a/src/jdk/nashorn/internal/objects/NativeRegExp.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java Wed Dec 02 23:08:29 2015 -0800 @@ -728,7 +728,7 @@ * * $$ -> $ * $& -> the matched substring - * $` -> the portion of string that preceeds matched substring + * $` -> the portion of string that precedes matched substring * $' -> the portion of string that follows the matched substring * $n -> the nth capture, where n is [1-9] and $n is NOT followed by a decimal digit * $nn -> the nnth capture, where nn is a two digit decimal number [01-99].
--- a/src/jdk/nashorn/internal/objects/PrototypeObject.java Tue Dec 01 22:55:46 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +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.objects; - -import static jdk.nashorn.internal.lookup.Lookup.MH; -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import jdk.nashorn.internal.runtime.AccessorProperty; -import jdk.nashorn.internal.runtime.Property; -import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptObject; - -/** - * Instances of this class serve as "prototype" object for script functions. - * The purpose is to expose "constructor" property from "prototype". Also, nasgen - * generated prototype classes extend from this class. - * - */ -public class PrototypeObject extends ScriptObject { - private static final PropertyMap map$; - - private Object constructor; - - private static final MethodHandle GET_CONSTRUCTOR = findOwnMH("getConstructor", Object.class, Object.class); - private static final MethodHandle SET_CONSTRUCTOR = findOwnMH("setConstructor", void.class, Object.class, Object.class); - - static { - final ArrayList<Property> properties = new ArrayList<>(1); - properties.add(AccessorProperty.create("constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR)); - map$ = PropertyMap.newMap(properties); - } - - private PrototypeObject(final Global global, final PropertyMap map) { - super(global.getObjectPrototype(), map != map$? map.addAll(map$) : map$); - } - - PrototypeObject() { - this(Global.instance(), map$); - } - - /** - * PropertyObject constructor - * - * @param map property map - */ - PrototypeObject(final PropertyMap map) { - this(Global.instance(), map); - } - - PrototypeObject(final ScriptFunction func) { - this(Global.instance(), map$); - this.constructor = func; - } - - /** - * Get the constructor for this {@code PrototypeObject} - * @param self self reference - * @return constructor, probably, but not necessarily, a {@link ScriptFunction} - */ - static Object getConstructor(final Object self) { - return (self instanceof PrototypeObject) ? - ((PrototypeObject)self).getConstructor() : - UNDEFINED; - } - - /** - * Reset the constructor for this {@code PrototypeObject} - * @param self self reference - * @param constructor constructor, probably, but not necessarily, a {@link ScriptFunction} - */ - static void setConstructor(final Object self, final Object constructor) { - if (self instanceof PrototypeObject) { - ((PrototypeObject)self).setConstructor(constructor); - } - } - - private Object getConstructor() { - return constructor; - } - - private void setConstructor(final Object constructor) { - this.constructor = constructor; - } - - private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { - return MH.findStatic(MethodHandles.lookup(), PrototypeObject.class, name, MH.type(rtype, types)); - } -}
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Tue Dec 01 22:55:46 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,313 +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.objects; - -import static jdk.nashorn.internal.lookup.Lookup.MH; -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - -import java.lang.invoke.MethodHandle; -import java.util.ArrayList; -import jdk.nashorn.internal.runtime.AccessorProperty; -import jdk.nashorn.internal.runtime.GlobalFunctions; -import jdk.nashorn.internal.runtime.Property; -import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; -import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptFunctionData; -import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.Specialization; - -/** - * Concrete implementation of ScriptFunction. This sets correct map for the - * function objects -- to expose properties like "prototype", "length" etc. - */ -public class ScriptFunctionImpl extends ScriptFunction { - - /** Reference to constructor prototype. */ - private Object prototype; - - // property map for strict mode functions - private static final PropertyMap strictmodemap$; - // property map for bound functions - private static final PropertyMap boundfunctionmap$; - // property map for non-strict, non-bound functions. - private static final PropertyMap map$; - - // Marker object for lazily initialized prototype object - private static final Object LAZY_PROTOTYPE = new Object(); - - private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs, final Global global) { - super(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); - init(global); - } - - /** - * Constructor called by Nasgen generated code, no membercount, use the default map. - * Creates builtin functions only. - * - * @param name name of function - * @param invokeHandle handle for invocation - * @param specs specialized versions of this method, if available, null otherwise - */ - ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs) { - this(name, invokeHandle, specs, Global.instance()); - } - - private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs, final Global global) { - super(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); - init(global); - } - - /** - * Constructor called by Nasgen generated code, no membercount, use the map passed as argument. - * Creates builtin functions only. - * - * @param name name of function - * @param invokeHandle handle for invocation - * @param map initial property map - * @param specs specialized versions of this method, if available, null otherwise - */ - ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) { - this(name, invokeHandle, map, specs, Global.instance()); - } - - private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags, final Global global) { - super(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags); - init(global); - } - - /** - * Constructor called by Global.newScriptFunction (runtime). - * - * @param name name of function - * @param methodHandle handle for invocation - * @param scope scope object - * @param specs specialized versions of this method, if available, null otherwise - * @param flags {@link ScriptFunctionData} flags - */ - ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags) { - this(name, methodHandle, scope, specs, flags, Global.instance()); - } - - private ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope, final Global global) { - super(data, getMap(data.isStrict()), scope); - init(global); - } - - /** - * Factory method called by compiler generated code for functions that need parent scope. - * - * @param constants the generated class' constant array - * @param index the index of the {@code RecompilableScriptFunctionData} object in the constants array. - * @param scope the parent scope object - * @return a newly created function object - */ - public static ScriptFunction create(final Object[] constants, final int index, final ScriptObject scope) { - return new ScriptFunctionImpl((RecompilableScriptFunctionData)constants[index], scope, Global.instance()); - } - - /** - * Factory method called by compiler generated code for functions that don't need parent scope. - * - * @param constants the generated class' constant array - * @param index the index of the {@code RecompilableScriptFunctionData} object in the constants array. - * @return a newly created function object - */ - public static ScriptFunction create(final Object[] constants, final int index) { - return create(constants, index, null); - } - - /** - * Only invoked internally from {@link BoundScriptFunctionImpl} constructor. - * @param data the script function data for the bound function. - * @param global the global object - */ - ScriptFunctionImpl(final ScriptFunctionData data, final Global global) { - super(data, boundfunctionmap$, null); - init(global); - } - - static { - final ArrayList<Property> properties = new ArrayList<>(3); - properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE)); - properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null)); - properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null)); - map$ = PropertyMap.newMap(properties); - strictmodemap$ = createStrictModeMap(map$); - boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); - } - - private static PropertyMap createStrictModeMap(final PropertyMap map) { - final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; - PropertyMap newMap = map; - // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. - newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags)); - newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags)); - return newMap; - } - - private static boolean isStrict(final int flags) { - return (flags & ScriptFunctionData.IS_STRICT) != 0; - } - - // Choose the map based on strict mode! - private static PropertyMap getMap(final boolean strict) { - return strict ? strictmodemap$ : map$; - } - - private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { - // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see - // ECMAScript 5.1 section 15.3.4.5 - return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype")); - } - - // Instance of this class is used as global anonymous function which - // serves as Function.prototype object. - private static class AnonymousFunction extends ScriptFunctionImpl { - private static final PropertyMap anonmap$ = PropertyMap.newMap(); - - AnonymousFunction() { - super("", GlobalFunctions.ANONYMOUS, anonmap$, null); - } - } - - static ScriptFunctionImpl newAnonymousFunction() { - return new AnonymousFunction(); - } - - private static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) { - final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, flags); - func.setPrototype(UNDEFINED); - // Non-constructor built-in functions do not have "prototype" property - func.deleteOwnProperty(func.getMap().findProperty("prototype")); - - return func; - } - - /** - * Factory method for non-constructor built-in functions - * - * @param name function name - * @param methodHandle handle for invocation - * @param specs specialized versions of function if available, null otherwise - * @return new ScriptFunction - */ - static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs) { - return makeFunction(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN); - } - - /** - * Factory method for non-constructor built-in, strict functions - * - * @param name function name - * @param methodHandle handle for invocation - * @return new ScriptFunction - */ - static ScriptFunction makeStrictFunction(final String name, final MethodHandle methodHandle) { - return makeFunction(name, methodHandle, null, ScriptFunctionData.IS_BUILTIN | ScriptFunctionData.IS_STRICT ); - } - - /** - * Factory method for non-constructor built-in functions - * - * @param name function name - * @param methodHandle handle for invocation - * @return new ScriptFunction - */ - static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) { - 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. - * @param self the self to bind to this function. Can be null (in which case, null is bound as this). - * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. - * @return a function with the specified self and parameters bound. - */ - @Override - public ScriptFunction makeBoundFunction(final Object self, final Object[] args) { - return super.makeBoundFunction(self, args); - } - - /** - * This method is used to create a bound function based on this function. - * - * @param data the {@code ScriptFunctionData} specifying the functions immutable portion. - * @return a function initialized from the specified data. Its parent scope will be set to null, therefore the - * passed in data should not expect a callee. - */ - @Override - protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) { - return new BoundScriptFunctionImpl(data, getTargetFunction()); - } - - // return Object.prototype - used by "allocate" - @Override - protected final ScriptObject getObjectPrototype() { - return Global.objectPrototype(); - } - - @Override - public final Object getPrototype() { - if (prototype == LAZY_PROTOTYPE) { - prototype = new PrototypeObject(this); - } - return prototype; - } - - @Override - public final void setPrototype(final Object newProto) { - if (newProto instanceof ScriptObject && newProto != this.prototype && allocatorMap != null) { - // Replace our current allocator map with one that is associated with the new prototype. - allocatorMap = allocatorMap.changeProto((ScriptObject)newProto); - } - this.prototype = newProto; - } - - // Internals below.. - private void init(final Global global) { - this.setInitialProto(global.getFunctionPrototype()); - this.prototype = LAZY_PROTOTYPE; - - // We have to fill user accessor functions late as these are stored - // in this object rather than in the PropertyMap of this object. - assert objectSpill == null; - final ScriptFunction typeErrorThrower = global.getTypeErrorThrower(); - if (findProperty("arguments", true) != null) { - initUserAccessors("arguments", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); - } - if (findProperty("caller", true) != null) { - initUserAccessors("caller", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); - } - } -}
--- a/src/jdk/nashorn/internal/parser/Lexer.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/parser/Lexer.java Wed Dec 02 23:08:29 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015, 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 @@ -1451,9 +1451,22 @@ skip(3); } - // Scan identifier. + // Scan identifier. It might be quoted, indicating that no string editing should take place. + final char quoteChar = ch0; + final boolean noStringEditing = quoteChar == '"' || quoteChar == '\''; + if (noStringEditing) { + skip(1); + } final int identStart = position; final int identLength = scanIdentifier(); + if (noStringEditing) { + if (ch0 != quoteChar) { + error(Lexer.message("here.non.matching.delimiter"), last, position, position); + restoreState(saved); + return false; + } + skip(1); + } // Check for identifier. if (identLength == 0) { @@ -1523,7 +1536,7 @@ } // Edit string if appropriate. - if (scripting && !stringState.isEmpty()) { + if (!noStringEditing && !stringState.isEmpty()) { editString(STRING, stringState); } else { // Add here string. @@ -1640,9 +1653,9 @@ //and new Color(float, float, float) will get ambiguous for cases like //new Color(1.0, 1.5, 1.5) if we don't respect the decimal point. //yet we don't want e.g. 1e6 to be a double unnecessarily - if (JSType.isRepresentableAsInt(value) && !JSType.isNegativeZero(value)) { + if (JSType.isStrictlyRepresentableAsInt(value)) { return (int)value; - } else if (JSType.isRepresentableAsLong(value) && !JSType.isNegativeZero(value)) { + } else if (JSType.isStrictlyRepresentableAsLong(value)) { return (long)value; } return value;
--- a/src/jdk/nashorn/internal/parser/Parser.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/parser/Parser.java Wed Dec 02 23:08:29 2015 -0800 @@ -84,7 +84,6 @@ 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; @@ -513,8 +512,7 @@ return lc.pop(functionNode). setBody(lc, newBody). - setLastToken(lc, lastToken). - setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED); + setLastToken(lc, lastToken); } /** @@ -805,7 +803,7 @@ if (!oldStrictMode && directiveStmts != null) { // check that directives preceding this one do not violate strictness for (final Node statement : directiveStmts) { - // the get value will force unescape of preceeding + // the get value will force unescape of preceding // escaped string directives getValue(statement.getToken()); } @@ -2469,7 +2467,7 @@ // run: function() { println("run"); } // }; // - // The object literal following the "new Constructor()" expresssion + // The object literal following the "new Constructor()" expression // is passed as an additional (last) argument to the constructor. if (!env._no_syntax_extensions && type == LBRACE) { arguments.add(objectLiteral()); @@ -2720,6 +2718,11 @@ } if (isStatement) { + if (isAnonymous) { + appendStatement(new ExpressionStatement(functionLine, functionToken, finish, functionNode)); + return functionNode; + } + final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET; final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, varFlags); if (topLevel) {
--- a/src/jdk/nashorn/internal/runtime/AllocationStrategy.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/AllocationStrategy.java Wed Dec 02 23:08:29 2015 -0800 @@ -29,6 +29,7 @@ import java.io.Serializable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.ref.WeakReference; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.ObjectClassGenerator; @@ -53,6 +54,9 @@ /** lazily generated allocator */ private transient MethodHandle allocator; + /** Last used allocator map */ + private transient AllocatorMap lastMap; + /** * Construct an allocation strategy with the given map and class name. * @param fieldCount number of fields in the allocated object @@ -71,11 +75,49 @@ return allocatorClassName; } - PropertyMap getAllocatorMap() { - // Create a new map for each function instance - return PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0); + /** + * Get the property map for the allocated object. + * @param prototype the prototype object + * @return the property map + */ + synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) { + assert prototype != null; + final PropertyMap protoMap = prototype.getMap(); + + if (lastMap != null) { + if (!lastMap.hasSharedProtoMap()) { + if (lastMap.hasSamePrototype(prototype)) { + return lastMap.allocatorMap; + } + if (lastMap.hasSameProtoMap(protoMap) && lastMap.hasUnchangedProtoMap()) { + // Convert to shared prototype map. Allocated objects will use the same property map + // that can be used as long as none of the prototypes modify the shared proto map. + final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0); + final SharedPropertyMap sharedProtoMap = new SharedPropertyMap(protoMap); + allocatorMap.setSharedProtoMap(sharedProtoMap); + prototype.setMap(sharedProtoMap); + lastMap = new AllocatorMap(prototype, protoMap, allocatorMap); + return allocatorMap; + } + } + + if (lastMap.hasValidSharedProtoMap() && lastMap.hasSameProtoMap(protoMap)) { + prototype.setMap(lastMap.getSharedProtoMap()); + return lastMap.allocatorMap; + } + } + + final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0); + lastMap = new AllocatorMap(prototype, protoMap, allocatorMap); + + return allocatorMap; } + /** + * Allocate an object with the given property map + * @param map the property map + * @return the allocated object + */ ScriptObject allocate(final PropertyMap map) { try { if (allocator == null) { @@ -94,4 +136,43 @@ public String toString() { return "AllocationStrategy[fieldCount=" + fieldCount + "]"; } + + static class AllocatorMap { + final private WeakReference<ScriptObject> prototype; + final private WeakReference<PropertyMap> prototypeMap; + + private PropertyMap allocatorMap; + + AllocatorMap(final ScriptObject prototype, final PropertyMap protoMap, final PropertyMap allocMap) { + this.prototype = new WeakReference<>(prototype); + this.prototypeMap = new WeakReference<>(protoMap); + this.allocatorMap = allocMap; + } + + boolean hasSamePrototype(final ScriptObject proto) { + return prototype.get() == proto; + } + + boolean hasSameProtoMap(final PropertyMap protoMap) { + return prototypeMap.get() == protoMap || allocatorMap.getSharedProtoMap() == protoMap; + } + + boolean hasUnchangedProtoMap() { + final ScriptObject proto = prototype.get(); + return proto != null && proto.getMap() == prototypeMap.get(); + } + + boolean hasSharedProtoMap() { + return getSharedProtoMap() != null; + } + + boolean hasValidSharedProtoMap() { + return hasSharedProtoMap() && getSharedProtoMap().isValidSharedProtoMap(); + } + + PropertyMap getSharedProtoMap() { + return allocatorMap.getSharedProtoMap(); + } + + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/AstSerializer.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.runtime; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.runtime.options.Options; + +/** + * This static utility class performs serialization of FunctionNode ASTs to a byte array. + * The format is a standard Java serialization stream, deflated. + */ +final class AstSerializer { + // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed + // and size. + private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); + static byte[] serialize(final FunctionNode fn) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Deflater deflater = new Deflater(COMPRESSION_LEVEL); + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { + oout.writeObject(fn); + } catch (final IOException e) { + throw new AssertionError("Unexpected exception serializing function", e); + } finally { + deflater.end(); + } + return out.toByteArray(); + } +}
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java Wed Dec 02 23:08:29 2015 -0800 @@ -38,15 +38,14 @@ * The compiler still retains most of the state around code emission * and management internally, so this is to avoid passing around any * logic that isn't directly related to installing a class - * @param <T> owner class type for this code installer * */ -public interface CodeInstaller<T> { +public interface CodeInstaller { /** - * Return the owner for the CodeInstaller, e.g. a {@link Context} - * @return owner + * Return the {@link Context} associated with this code installer. + * @return the context. */ - public T getOwner(); + public Context getContext(); /** * Install a class. @@ -106,7 +105,7 @@ * new, independent class loader. * @return a new code installer with a new independent class loader. */ - public CodeInstaller<T> withNewLoader(); + public CodeInstaller withNewLoader(); /** * Returns true if this code installer is compatible with the other code installer. Compatibility is expected to be @@ -115,6 +114,6 @@ * @param other the other code installer tested for compatibility with this code installer. * @return true if this code installer is compatible with the other code installer. */ - public boolean isCompatibleWith(CodeInstaller<T> other); + public boolean isCompatibleWith(CodeInstaller other); }
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java Wed Dec 02 23:08:29 2015 -0800 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; + import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -101,7 +102,7 @@ /* * An optimistic builtin with isOptimistic=true works like any optimistic generated function, i.e. it * can throw unwarranted optimism exceptions. As native functions trivially can't have parts of them - * regenerated as restof methods, this only works if the methods are atomic/functional in their behavior + * regenerated as "restOf" methods, this only works if the methods are atomic/functional in their behavior * and doesn't modify state before an UOE can be thrown. If they aren't, we can reexecute a wider version * of the same builtin in a recompilation handler for FinalScriptFunctionData. There are several * candidate methods in Native* that would benefit from this, but I haven't had time to implement any @@ -566,7 +567,7 @@ return handle; } - // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itslef + // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itself // to the compiled function's changed target whenever the optimistic assumptions are invalidated. final CallSite cs = new MutableCallSite(handle.type()); relinkComposableInvoker(cs, this, isConstructor); @@ -820,7 +821,7 @@ // isn't available, we'll use the old one bound into the call site. final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo; FunctionNode fn = effectiveOptInfo.reparse(); - final boolean serialized = effectiveOptInfo.isSerialized(); + final boolean cached = fn.isCached(); final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of if (!shouldRecompile) { @@ -828,11 +829,11 @@ // recompiled a deoptimized version for an inner invocation. // We still need to do the rest of from the beginning logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); - return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); + return restOfHandle(effectiveOptInfo, compiler.compile(fn, cached ? CompilationPhases.COMPILE_CACHED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); } logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); - fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); + fn = compiler.compile(fn, cached ? CompilationPhases.RECOMPILE_CACHED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); log.fine("Reusable IR generated"); // compile the rest of the function, and install it @@ -956,10 +957,6 @@ FunctionNode reparse() { return data.reparse(); } - - boolean isSerialized() { - return data.isSerialized(); - } } @SuppressWarnings("unused")
--- a/src/jdk/nashorn/internal/runtime/Context.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/Context.java Wed Dec 02 23:08:29 2015 -0800 @@ -153,7 +153,7 @@ * Currently we are conservative and associate the name of a builtin class with all * its properties, so it's enough to invalidate a property to break all assumptions * about a prototype. This can be changed to a more fine grained approach, but no one - * ever needs this, given the very rare occurance of swapping out only parts of + * ever needs this, given the very rare occurrence of swapping out only parts of * a builtin v.s. the entire builtin object */ private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>(); @@ -167,7 +167,7 @@ * ContextCodeInstaller that has the privilege of installing classes in the Context. * Can only be instantiated from inside the context and is opaque to other classes */ - public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> { + public static class ContextCodeInstaller implements CodeInstaller { private final Context context; private final ScriptLoader loader; private final CodeSource codeSource; @@ -185,13 +185,9 @@ this.codeSource = codeSource; } - /** - * Return the script environment for this installer - * @return ScriptEnvironment - */ @Override - public ScriptEnvironment getOwner() { - return context.env; + public Context getContext() { + return context; } @Override @@ -254,7 +250,7 @@ } @Override - public CodeInstaller<ScriptEnvironment> withNewLoader() { + public CodeInstaller withNewLoader() { // Reuse this installer if we're within our limits. if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) { return this; @@ -263,7 +259,7 @@ } @Override - public boolean isCompatibleWith(final CodeInstaller<ScriptEnvironment> other) { + public boolean isCompatibleWith(final CodeInstaller other) { if (other instanceof ContextCodeInstaller) { final ContextCodeInstaller cci = (ContextCodeInstaller)other; return cci.context == context && cci.codeSource == codeSource; @@ -987,6 +983,16 @@ } /** + * Is {@code className} the name of a structure class? + * + * @param className a class name + * @return true if className is a structure class name + */ + public static boolean isStructureClass(final String className) { + return StructureLoader.isStructureClass(className); + } + + /** * Checks that the given Class can be accessed from no permissions context. * * @param clazz Class object @@ -1129,17 +1135,16 @@ * * @param global the global * @param engine the associated ScriptEngine instance, can be null - * @param ctxt the initial ScriptContext, can be null * @return the initialized global scope object. */ - public Global initGlobal(final Global global, final ScriptEngine engine, final ScriptContext ctxt) { + public Global initGlobal(final Global global, final ScriptEngine engine) { // Need only minimal global object, if we are just compiling. if (!env._compile_only) { final Global oldGlobal = Context.getGlobal(); try { Context.setGlobal(global); // initialize global scope with builtin global objects - global.initBuiltinObjects(engine, ctxt); + global.initBuiltinObjects(engine); } finally { Context.setGlobal(oldGlobal); } @@ -1155,7 +1160,7 @@ * @return the initialized global scope object. */ public Global initGlobal(final Global global) { - return initGlobal(global, null, null); + return initGlobal(global, null); } /** @@ -1300,14 +1305,12 @@ final URL url = source.getURL(); final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; final CodeSource cs = new CodeSource(url, (CodeSigner[])null); - final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); + final CodeInstaller installer = new ContextCodeInstaller(this, loader, cs); if (storedScript == null) { final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; - final Compiler compiler = new Compiler( - this, - env, + final Compiler compiler = Compiler.forInitialCompilation( installer, source, errMan, @@ -1456,7 +1459,7 @@ * @param level log level * @param mh method handle * @param paramStart first parameter to print - * @param printReturnValue should we print the return vaulue? + * @param printReturnValue should we print the return value? * @param text debug printout to add * * @return instrumented method handle, or null if logger not enabled
--- a/src/jdk/nashorn/internal/runtime/Debug.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/Debug.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,6 +26,8 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.parser.TokenType.EOF; + +import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenStream; @@ -63,6 +65,15 @@ } /** + * Return a formatted script stack trace string with frames information separated by '\n'. + * This is a shortcut for {@code NashornException.getScriptStackString(new Throwable())}. + * @return formatted stack trace string + */ + public static String scriptStack() { + return NashornException.getScriptStackString(new Throwable()); + } + + /** * Return the system identity hashcode for an object as a human readable * string *
--- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Wed Dec 02 23:08:29 2015 -0800 @@ -27,6 +27,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.util.Collection; import java.util.List; /** @@ -72,11 +73,6 @@ } @Override - boolean isRecompilable() { - return false; - } - - @Override protected boolean needsCallee() { final boolean needsCallee = code.getFirst().needsCallee(); assert allNeedCallee(needsCallee); @@ -93,6 +89,20 @@ } @Override + CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) { + assert isValidCallSite(callSiteType) : callSiteType; + + CompiledFunction best = null; + for (final CompiledFunction candidate: code) { + if (!forbidden.contains(candidate) && candidate.betterThanFinal(best, callSiteType)) { + best = candidate; + } + } + + return best; + } + + @Override MethodType getGenericType() { // We need to ask the code for its generic type. We can't just rely on this function data's arity, as it's not // actually correct for lots of built-ins. E.g. ECMAScript 5.1 section 15.5.3.2 prescribes that
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java Wed Dec 02 23:08:29 2015 -0800 @@ -297,4 +297,3 @@ } } -
--- a/src/jdk/nashorn/internal/runtime/GlobalConstants.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/GlobalConstants.java Wed Dec 02 23:08:29 2015 -0800 @@ -67,7 +67,7 @@ * * Thus everything registered as a global constant gets an extra chance. Set once, * reregister the switchpoint. Set twice or more - don't try again forever, or we'd - * just end up relinking our way into megamorphisism. + * just end up relinking our way into megamorphism. * * Also it has to be noted that this kind of linking creates a coupling between a Global * and the call sites in compiled code belonging to the Context. For this reason, the
--- a/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Wed Dec 02 23:08:29 2015 -0800 @@ -187,14 +187,14 @@ double result = 0.0; int digit; - // we should see atleast one valid digit + // we should see at least one valid digit boolean entered = false; while (idx < length) { digit = fastDigit(str.charAt(idx++), radix); if (digit < 0) { break; } - // we have seen atleast one valid digit in the specified radix + // we have seen at least one valid digit in the specified radix entered = true; result *= radix; result += digit;
--- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,7 +26,6 @@ package jdk.nashorn.internal.runtime; import java.lang.invoke.MethodHandle; -import java.util.Iterator; import java.util.concurrent.Callable; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.JSONParser; @@ -104,16 +103,28 @@ final Object val = holder.get(name); if (val instanceof ScriptObject) { final ScriptObject valueObj = (ScriptObject)val; - final Iterator<String> iter = valueObj.propertyIterator(); + if (valueObj.isArray()) { + final int length = JSType.toInteger(valueObj.getLength()); + for (int i = 0; i < length; i++) { + final String key = Integer.toString(i); + final Object newElement = walk(valueObj, key, reviver); - while (iter.hasNext()) { - final String key = iter.next(); - final Object newElement = walk(valueObj, key, reviver); + if (newElement == ScriptRuntime.UNDEFINED) { + valueObj.delete(i, false); + } else { + setPropertyValue(valueObj, key, newElement); + } + } + } else { + final String[] keys = valueObj.getOwnKeys(false); + for (final String key : keys) { + final Object newElement = walk(valueObj, key, reviver); - if (newElement == ScriptRuntime.UNDEFINED) { - valueObj.delete(key, false); - } else { - setPropertyValue(valueObj, key, newElement); + if (newElement == ScriptRuntime.UNDEFINED) { + valueObj.delete(key, false); + } else { + setPropertyValue(valueObj, key, newElement); + } } } }
--- a/src/jdk/nashorn/internal/runtime/JSType.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Wed Dec 02 23:08:29 2015 -0800 @@ -377,8 +377,8 @@ } /** - * Returns true if double number can be represented as an int. Note that it returns true for negative zero. If you - * need to exclude negative zero, combine this check with {@link #isNegativeZero(double)}. + * Returns true if double number can be represented as an int. Note that it returns true for negative + * zero. If you need to exclude negative zero, use {@link #isStrictlyRepresentableAsInt(double)}. * * @param number a double to inspect * @@ -389,6 +389,18 @@ } /** + * Returns true if double number can be represented as an int. Note that it returns false for negative + * zero. If you don't need to distinguish negative zero, use {@link #isRepresentableAsInt(double)}. + * + * @param number a double to inspect + * + * @return true for int representable doubles + */ + public static boolean isStrictlyRepresentableAsInt(final double number) { + return isRepresentableAsInt(number) && isNotNegativeZero(number); + } + + /** * Returns true if Object can be represented as an int * * @param obj an object to inspect @@ -403,8 +415,8 @@ } /** - * Returns true if double number can be represented as a long. Note that it returns true for negative zero. If you - * need to exclude negative zero, combine this check with {@link #isNegativeZero(double)}. + * Returns true if double number can be represented as a long. Note that it returns true for negative + * zero. If you need to exclude negative zero, use {@link #isStrictlyRepresentableAsLong(double)}. * * @param number a double to inspect * @return true for long representable doubles @@ -414,6 +426,18 @@ } /** + * Returns true if double number can be represented as a long. Note that it returns false for negative + * zero. If you don't need to distinguish negative zero, use {@link #isRepresentableAsLong(double)}. + * + * @param number a double to inspect + * + * @return true for long representable doubles + */ + public static boolean isStrictlyRepresentableAsLong(final double number) { + return isRepresentableAsLong(number) && isNotNegativeZero(number); + } + + /** * Returns true if Object can be represented as a long * * @param obj an object to inspect @@ -428,12 +452,12 @@ } /** - * Returns true if the number is the negative zero ({@code -0.0d}). + * Returns true if the number is not the negative zero ({@code -0.0d}). * @param number the number to test - * @return true if it is the negative zero, false otherwise. + * @return true if it is not the negative zero, false otherwise. */ - public static boolean isNegativeZero(final double number) { - return number == 0.0d && Double.doubleToRawLongBits(number) == 0x8000000000000000L; + private static boolean isNotNegativeZero(final double number) { + return Double.doubleToRawLongBits(number) != 0x8000000000000000L; } /** @@ -1944,7 +1968,7 @@ /** * Get the unboxed (primitive) type for an object * @param o object - * @return primive type or Object.class if not primitive + * @return primitive type or Object.class if not primitive */ public static Class<?> unboxedFieldType(final Object o) { if (o == null) {
--- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Wed Dec 02 23:08:29 2015 -0800 @@ -206,7 +206,7 @@ } @Override - protected Object invokeNoSuchProperty(final String key, final int programPoint) { + protected Object invokeNoSuchProperty(final String key, final boolean isScope, final int programPoint) { final Object retval = createProperty(key); if (isValid(programPoint)) { throw new UnwarrantedOptimismException(retval, programPoint);
--- a/src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java Wed Dec 02 23:08:29 2015 -0800 @@ -184,7 +184,7 @@ @SuppressWarnings("unused") private static int ensureInt(final double arg, final int programPoint) { - if (JSType.isRepresentableAsInt(arg) && !JSType.isNegativeZero(arg)) { + if (JSType.isStrictlyRepresentableAsInt(arg)) { return (int)arg; } throw new UnwarrantedOptimismException(arg, programPoint); @@ -206,7 +206,7 @@ // Long into the exception. if (isPrimitiveNumberWrapper(arg)) { final double d = ((Number)arg).doubleValue(); - if (JSType.isRepresentableAsInt(d) && !JSType.isNegativeZero(d)) { + if (JSType.isStrictlyRepresentableAsInt(d)) { return (int)d; } } @@ -239,7 +239,7 @@ } private static long ensureLong(final double arg, final int programPoint) { - if (JSType.isRepresentableAsLong(arg) && !JSType.isNegativeZero(arg)) { + if (JSType.isStrictlyRepresentableAsLong(arg)) { return (long)arg; } throw new UnwarrantedOptimismException(arg, programPoint);
--- a/src/jdk/nashorn/internal/runtime/ParserException.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/ParserException.java Wed Dec 02 23:08:29 2015 -0800 @@ -38,7 +38,7 @@ private final Source source; // token responsible for this exception private final long token; - // if this is traslated as ECMA error, which type should be used? + // if this is translated as ECMA error, which type should be used? private final JSErrorType errorType; /**
--- a/src/jdk/nashorn/internal/runtime/Property.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/Property.java Wed Dec 02 23:08:29 2015 -0800 @@ -562,8 +562,8 @@ @Override public int hashCode() { - final Class<?> type = getLocalType(); - return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode()); + final Class<?> t = getLocalType(); + return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (t == null ? 0 : t.hashCode()); } @Override @@ -588,7 +588,7 @@ getKey().equals(otherProperty.getKey()); } - private static final String type(final Class<?> type) { + private static String type(final Class<?> type) { if (type == null) { return "undef"; } else if (type == int.class) { @@ -608,8 +608,8 @@ */ public final String toStringShort() { final StringBuilder sb = new StringBuilder(); - final Class<?> type = getLocalType(); - sb.append(getKey()).append(" (").append(type(type)).append(')'); + final Class<?> t = getLocalType(); + sb.append(getKey()).append(" (").append(type(t)).append(')'); return sb.toString(); } @@ -625,7 +625,7 @@ @Override public String toString() { final StringBuilder sb = new StringBuilder(); - final Class<?> type = getLocalType(); + final Class<?> t = getLocalType(); sb.append(indent(getKey(), 20)). append(" id="). @@ -635,7 +635,7 @@ append(") "). append(getClass().getSimpleName()). append(" {"). - append(indent(type(type), 5)). + append(indent(type(t), 5)). append('}'); if (slot != -1) {
--- a/src/jdk/nashorn/internal/runtime/PropertyListeners.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/PropertyListeners.java Wed Dec 02 23:08:29 2015 -0800 @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.atomic.LongAdder; /** * Helper class to manage property listeners and notification. @@ -37,8 +38,15 @@ private Map<String, WeakPropertyMapSet> listeners; // These counters are updated in debug mode - private static int listenersAdded; - private static int listenersRemoved; + private static LongAdder listenersAdded; + private static LongAdder listenersRemoved; + + static { + if (Context.DEBUG) { + listenersAdded = new LongAdder(); + listenersRemoved = new LongAdder(); + } + } /** * Copy constructor @@ -54,29 +62,33 @@ * Return aggregate listeners added to all PropertyListenerManagers * @return the listenersAdded */ - public static int getListenersAdded() { - return listenersAdded; + public static long getListenersAdded() { + return listenersAdded.longValue(); } /** * Return aggregate listeners removed from all PropertyListenerManagers * @return the listenersRemoved */ - public static int getListenersRemoved() { - return listenersRemoved; + public static long getListenersRemoved() { + return listenersRemoved.longValue(); } /** - * Return listeners added to this ScriptObject. + * Return number of listeners added to a ScriptObject. * @param obj the object * @return the listener count */ public static int getListenerCount(final ScriptObject obj) { - final PropertyListeners propertyListeners = obj.getMap().getListeners(); - if (propertyListeners != null) { - return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size(); - } - return 0; + return obj.getMap().getListenerCount(); + } + + /** + * Return the number of listeners added to this PropertyListeners instance. + * @return the listener count; + */ + public int getListenerCount() { + return listeners == null ? 0 : listeners.size(); } // Property listener management methods @@ -122,7 +134,7 @@ */ synchronized final void addListener(final String key, final PropertyMap propertyMap) { if (Context.DEBUG) { - listenersAdded++; + listenersAdded.increment(); } if (listeners == null) { listeners = new WeakHashMap<>(); @@ -148,9 +160,12 @@ final WeakPropertyMapSet set = listeners.get(prop.getKey()); if (set != null) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyAdded(prop); + propertyMap.propertyAdded(prop, false); } listeners.remove(prop.getKey()); + if (Context.DEBUG) { + listenersRemoved.increment(); + } } } } @@ -165,9 +180,12 @@ final WeakPropertyMapSet set = listeners.get(prop.getKey()); if (set != null) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyDeleted(prop); + propertyMap.propertyDeleted(prop, false); } listeners.remove(prop.getKey()); + if (Context.DEBUG) { + listenersRemoved.increment(); + } } } } @@ -184,9 +202,12 @@ final WeakPropertyMapSet set = listeners.get(oldProp.getKey()); if (set != null) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyModified(oldProp, newProp); + propertyMap.propertyModified(oldProp, newProp, false); } listeners.remove(oldProp.getKey()); + if (Context.DEBUG) { + listenersRemoved.increment(); + } } } } @@ -198,7 +219,7 @@ if (listeners != null) { for (final WeakPropertyMapSet set : listeners.values()) { for (final PropertyMap propertyMap : set.elements()) { - propertyMap.protoChanged(); + propertyMap.protoChanged(false); } } listeners.clear();
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Wed Dec 02 23:08:29 2015 -0800 @@ -34,7 +34,9 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.invoke.SwitchPoint; +import java.lang.ref.Reference; import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; @@ -42,6 +44,8 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.WeakHashMap; +import java.util.concurrent.atomic.LongAdder; +import jdk.nashorn.internal.runtime.options.Options; import jdk.nashorn.internal.scripts.JO; /** @@ -53,35 +57,49 @@ * All property maps are immutable. If a property is added, modified or removed, the mutator * will return a new map. */ -public final class PropertyMap implements Iterable<Object>, Serializable { +public class PropertyMap implements Iterable<Object>, Serializable { + private static final int INITIAL_SOFT_REFERENCE_DERIVATION_LIMIT = + Math.max(0, Options.getIntProperty("nashorn.propertyMap.softReferenceDerivationLimit", 32)); + /** 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; + private static final int NOT_EXTENSIBLE = 0b0000_0001; /** Does this map contain valid array keys? */ - public static final int CONTAINS_ARRAY_KEYS = 0b0000_0010; + private static final int CONTAINS_ARRAY_KEYS = 0b0000_0010; /** Map status flags. */ - private int flags; + private final int flags; /** Map of properties. */ private transient PropertyHashMap properties; /** Number of fields in use. */ - private int fieldCount; + private final int fieldCount; /** Number of fields available. */ private final int fieldMaximum; /** Length of spill in use. */ - private int spillLength; + private final int spillLength; /** Structure class name */ - private String className; + private final String className; + + /** + * Countdown of number of times this property map has been derived from another property map. When it + * reaches zero, the property map will start using weak references instead of soft references to hold on + * to its history elements. + */ + private final int softReferenceDerivationLimit; + + /** A reference to the expected shared prototype property map. If this is set this + * property map should only be used if it the same as the actual prototype map. */ + private transient SharedPropertyMap sharedProtoMap; /** {@link SwitchPoint}s for gets on inherited properties. */ - private transient HashMap<String, SwitchPoint> protoGetSwitches; + private transient HashMap<String, SwitchPoint> protoSwitches; /** History of maps, used to limit map duplication. */ - private transient WeakHashMap<Property, SoftReference<PropertyMap>> history; + private transient WeakHashMap<Property, Reference<PropertyMap>> history; /** History of prototypes, used to limit map duplication. */ private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory; @@ -94,59 +112,60 @@ private static final long serialVersionUID = -7041836752008732533L; /** - * Constructor. + * Constructs a new property map. * * @param properties A {@link PropertyHashMap} with initial contents. * @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 String className, final int fieldCount, - final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) { + private PropertyMap(final PropertyHashMap properties, final int flags, final String className, + final int fieldCount, final int fieldMaximum, final int spillLength) { this.properties = properties; this.className = className; this.fieldCount = fieldCount; this.fieldMaximum = fieldMaximum; this.spillLength = spillLength; - if (containsArrayKeys) { - setContainsArrayKeys(); - } + this.flags = flags; + this.softReferenceDerivationLimit = INITIAL_SOFT_REFERENCE_DERIVATION_LIMIT; if (Context.DEBUG) { - count++; + count.increment(); } } /** - * Cloning constructor. + * Constructs a clone of {@code propertyMap} with changed properties, flags, or boundaries. * * @param propertyMap Existing property map. * @param properties A {@link PropertyHashMap} with a new set of properties. */ - private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties) { + private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties, final int flags, final int fieldCount, final int spillLength, final int softReferenceDerivationLimit) { this.properties = properties; - this.flags = propertyMap.flags; - this.spillLength = propertyMap.spillLength; - this.fieldCount = propertyMap.fieldCount; + this.flags = flags; + this.spillLength = spillLength; + this.fieldCount = fieldCount; this.fieldMaximum = propertyMap.fieldMaximum; + this.className = propertyMap.className; // We inherit the parent property listeners instance. It will be cloned when a new listener is added. this.listeners = propertyMap.listeners; this.freeSlots = propertyMap.freeSlots; + this.sharedProtoMap = propertyMap.sharedProtoMap; + this.softReferenceDerivationLimit = softReferenceDerivationLimit; if (Context.DEBUG) { - count++; - clonedCount++; + count.increment(); + clonedCount.increment(); } } /** - * Cloning constructor. + * Constructs an exact clone of {@code propertyMap}. * * @param propertyMap Existing property map. */ - private PropertyMap(final PropertyMap propertyMap) { - this(propertyMap, propertyMap.properties); + protected PropertyMap(final PropertyMap propertyMap) { + this(propertyMap, propertyMap.properties, propertyMap.flags, propertyMap.fieldCount, propertyMap.spillLength, propertyMap.softReferenceDerivationLimit); } private void writeObject(final ObjectOutputStream out) throws IOException { @@ -182,7 +201,7 @@ */ public static PropertyMap newMap(final Collection<Property> properties, final String className, final int fieldCount, final int fieldMaximum, final int spillLength) { final PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties); - return new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength, false); + return new PropertyMap(newProperties, 0, className, fieldCount, fieldMaximum, spillLength); } /** @@ -204,7 +223,7 @@ * @return New empty {@link PropertyMap}. */ public static PropertyMap newMap(final Class<? extends ScriptObject> clazz) { - return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false); + return new PropertyMap(EMPTY_HASHMAP, 0, clazz.getName(), 0, 0, 0); } /** @@ -226,12 +245,12 @@ } /** - * Get the listeners of this map, or null if none exists + * Get the number of listeners of this map * - * @return the listeners + * @return the number of listeners */ - public PropertyListeners getListeners() { - return listeners; + public int getListenerCount() { + return listeners == null ? 0 : listeners.getListenerCount(); } /** @@ -252,9 +271,12 @@ * A new property is being added. * * @param property The new Property added. + * @param isSelf was the property added to this map? */ - public void propertyAdded(final Property property) { - invalidateProtoGetSwitchPoint(property); + public void propertyAdded(final Property property, final boolean isSelf) { + if (!isSelf) { + invalidateProtoSwitchPoint(property.getKey()); + } if (listeners != null) { listeners.propertyAdded(property); } @@ -264,9 +286,12 @@ * An existing property is being deleted. * * @param property The property being deleted. + * @param isSelf was the property deleted from this map? */ - public void propertyDeleted(final Property property) { - invalidateProtoGetSwitchPoint(property); + public void propertyDeleted(final Property property, final boolean isSelf) { + if (!isSelf) { + invalidateProtoSwitchPoint(property.getKey()); + } if (listeners != null) { listeners.propertyDeleted(property); } @@ -277,9 +302,12 @@ * * @param oldProperty The old property * @param newProperty The new property + * @param isSelf was the property modified on this map? */ - public void propertyModified(final Property oldProperty, final Property newProperty) { - invalidateProtoGetSwitchPoint(oldProperty); + public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) { + if (!isSelf) { + invalidateProtoSwitchPoint(oldProperty.getKey()); + } if (listeners != null) { listeners.propertyModified(oldProperty, newProperty); } @@ -287,9 +315,15 @@ /** * The prototype of an object associated with this {@link PropertyMap} is changed. + * + * @param isSelf was the prototype changed on the object using this map? */ - public void protoChanged() { - invalidateAllProtoGetSwitchPoints(); + public void protoChanged(final boolean isSelf) { + if (!isSelf) { + invalidateAllProtoSwitchPoints(); + } else if (sharedProtoMap != null) { + sharedProtoMap.invalidateSwitchPoint(); + } if (listeners != null) { listeners.protoChanged(); } @@ -302,14 +336,14 @@ * @return A shared {@link SwitchPoint} for the property. */ public synchronized SwitchPoint getSwitchPoint(final String key) { - if (protoGetSwitches == null) { - protoGetSwitches = new HashMap<>(); + if (protoSwitches == null) { + protoSwitches = new HashMap<>(); } - SwitchPoint switchPoint = protoGetSwitches.get(key); + SwitchPoint switchPoint = protoSwitches.get(key); if (switchPoint == null) { switchPoint = new SwitchPoint(); - protoGetSwitches.put(key, switchPoint); + protoSwitches.put(key, switchPoint); } return switchPoint; @@ -318,19 +352,17 @@ /** * Indicate that a prototype property has changed. * - * @param property {@link Property} to invalidate. + * @param key {@link Property} key to invalidate. */ - synchronized void invalidateProtoGetSwitchPoint(final Property property) { - if (protoGetSwitches != null) { - - final String key = property.getKey(); - final SwitchPoint sp = protoGetSwitches.get(key); + synchronized void invalidateProtoSwitchPoint(final String key) { + if (protoSwitches != null) { + final SwitchPoint sp = protoSwitches.get(key); if (sp != null) { - protoGetSwitches.remove(key); + protoSwitches.remove(key); if (Context.DEBUG) { - protoInvalidations++; + protoInvalidations.increment(); } - SwitchPoint.invalidateAll(new SwitchPoint[] { sp }); + SwitchPoint.invalidateAll(new SwitchPoint[]{sp}); } } } @@ -338,15 +370,15 @@ /** * Indicate that proto itself has changed in hierarchy somewhere. */ - synchronized void invalidateAllProtoGetSwitchPoints() { - if (protoGetSwitches != null) { - final int size = protoGetSwitches.size(); + synchronized void invalidateAllProtoSwitchPoints() { + if (protoSwitches != null) { + final int size = protoSwitches.size(); if (size > 0) { if (Context.DEBUG) { - protoInvalidations += size; + protoInvalidations.add(size); } - SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[size])); - protoGetSwitches.clear(); + SwitchPoint.invalidateAll(protoSwitches.values().toArray(new SwitchPoint[size])); + protoSwitches.clear(); } } } @@ -362,7 +394,7 @@ * @return New {@link PropertyMap} with {@link Property} added. */ PropertyMap addPropertyBind(final AccessorProperty property, final Object bindTo) { - // No need to store bound property in the history as bound properties can't be reused. + // We must not store bound property in the history as bound properties can't be reused. return addPropertyNoHistory(new AccessorProperty(property, bindTo)); } @@ -375,16 +407,16 @@ return property.isSpill() ? slot + fieldMaximum : slot; } - // Update boundaries and flags after a property has been added - private void updateFlagsAndBoundaries(final Property newProperty) { - if(newProperty.isSpill()) { - spillLength = Math.max(spillLength, newProperty.getSlot() + 1); - } else { - fieldCount = Math.max(fieldCount, newProperty.getSlot() + 1); - } - if (isValidArrayIndex(getArrayIndex(newProperty.getKey()))) { - setContainsArrayKeys(); - } + private int newSpillLength(final Property newProperty) { + return newProperty.isSpill() ? Math.max(spillLength, newProperty.getSlot() + 1) : spillLength; + } + + private int newFieldCount(final Property newProperty) { + return !newProperty.isSpill() ? Math.max(fieldCount, newProperty.getSlot() + 1) : fieldCount; + } + + private int newFlags(final Property newProperty) { + return isValidArrayIndex(getArrayIndex(newProperty.getKey())) ? flags | CONTAINS_ARRAY_KEYS : flags; } // Update the free slots bitmap for a property that has been deleted and/or added. This method is not synchronized @@ -419,16 +451,9 @@ * @param property {@link Property} being added. * @return New {@link PropertyMap} with {@link Property} added. */ - public PropertyMap addPropertyNoHistory(final Property property) { - if (listeners != null) { - listeners.propertyAdded(property); - } - final PropertyHashMap newProperties = properties.immutableAdd(property); - final PropertyMap newMap = new PropertyMap(this, newProperties); - newMap.updateFlagsAndBoundaries(property); - newMap.updateFreeSlots(null, property); - - return newMap; + public final PropertyMap addPropertyNoHistory(final Property property) { + propertyAdded(property, true); + return addPropertyInternal(property); } /** @@ -438,23 +463,29 @@ * * @return New {@link PropertyMap} with {@link Property} added. */ - public synchronized PropertyMap addProperty(final Property property) { - if (listeners != null) { - listeners.propertyAdded(property); - } + public final synchronized PropertyMap addProperty(final Property property) { + propertyAdded(property, true); PropertyMap newMap = checkHistory(property); if (newMap == null) { - final PropertyHashMap newProperties = properties.immutableAdd(property); - newMap = new PropertyMap(this, newProperties); - newMap.updateFlagsAndBoundaries(property); - newMap.updateFreeSlots(null, property); + newMap = addPropertyInternal(property); addToHistory(property, newMap); } return newMap; } + private PropertyMap deriveMap(final PropertyHashMap newProperties, final int newFlags, final int newFieldCount, final int newSpillLength) { + return new PropertyMap(this, newProperties, newFlags, newFieldCount, newSpillLength, softReferenceDerivationLimit == 0 ? 0 : softReferenceDerivationLimit - 1); + } + + private PropertyMap addPropertyInternal(final Property property) { + final PropertyHashMap newProperties = properties.immutableAdd(property); + final PropertyMap newMap = deriveMap(newProperties, newFlags(property), newFieldCount(property), newSpillLength(property)); + newMap.updateFreeSlots(null, property); + return newMap; + } + /** * Remove a property from a map. Cloning or using an existing map if available. * @@ -462,10 +493,8 @@ * * @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found. */ - public synchronized PropertyMap deleteProperty(final Property property) { - if (listeners != null) { - listeners.propertyDeleted(property); - } + public final synchronized PropertyMap deleteProperty(final Property property) { + propertyDeleted(property, true); PropertyMap newMap = checkHistory(property); final String key = property.getKey(); @@ -476,13 +505,13 @@ // If deleted property was last field or spill slot we can make it reusable by reducing field/slot count. // Otherwise mark it as free in free slots bitset. if (isSpill && slot >= 0 && slot == spillLength - 1) { - newMap = new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength - 1, containsArrayKeys()); + newMap = deriveMap(newProperties, flags, fieldCount, spillLength - 1); newMap.freeSlots = freeSlots; } else if (!isSpill && slot >= 0 && slot == fieldCount - 1) { - newMap = new PropertyMap(newProperties, className, fieldCount - 1, fieldMaximum, spillLength, containsArrayKeys()); + newMap = deriveMap(newProperties, flags, fieldCount - 1, spillLength); newMap.freeSlots = freeSlots; } else { - newMap = new PropertyMap(this, newProperties); + newMap = deriveMap(newProperties, flags, fieldCount, spillLength); newMap.updateFreeSlots(property, null); } addToHistory(property, newMap); @@ -499,13 +528,8 @@ * * @return New {@link PropertyMap} with {@link Property} replaced. */ - public PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) { - if (listeners != null) { - listeners.propertyModified(oldProperty, newProperty); - } - // Add replaces existing property. - final PropertyHashMap newProperties = properties.immutableReplace(oldProperty, newProperty); - final PropertyMap newMap = new PropertyMap(this, newProperties); + public final PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) { + propertyModified(oldProperty, newProperty, true); /* * See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods. * @@ -527,14 +551,17 @@ newProperty instanceof UserAccessorProperty : "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getLocalType() + " => " + newProperty.getLocalType() + "]"; - newMap.flags = flags; - /* * spillLength remains same in case (1) and (2) because of slot reuse. Only for case (3), we need * to add spill count of the newly added UserAccessorProperty property. */ + final int newSpillLength = sameType ? spillLength : Math.max(spillLength, newProperty.getSlot() + 1); + + // Add replaces existing property. + final PropertyHashMap newProperties = properties.immutableReplace(oldProperty, newProperty); + final PropertyMap newMap = deriveMap(newProperties, flags, fieldCount, newSpillLength); + if (!sameType) { - newMap.spillLength = Math.max(spillLength, newProperty.getSlot() + 1); newMap.updateFreeSlots(oldProperty, newProperty); } return newMap; @@ -550,7 +577,7 @@ * @param propertyFlags attribute flags of the property * @return the newly created UserAccessorProperty */ - public UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) { + public final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) { return new UserAccessorProperty(key, propertyFlags, getFreeSpillSlot()); } @@ -561,7 +588,7 @@ * * @return {@link Property} matching key. */ - public Property findProperty(final String key) { + public final Property findProperty(final String key) { return properties.find(key); } @@ -572,12 +599,12 @@ * * @return New {@link PropertyMap} with added properties. */ - public PropertyMap addAll(final PropertyMap other) { + public final PropertyMap addAll(final PropertyMap other) { assert this != other : "adding property map to itself"; final Property[] otherProperties = other.properties.getProperties(); final PropertyHashMap newProperties = properties.immutableAdd(otherProperties); - final PropertyMap newMap = new PropertyMap(this, newProperties); + final PropertyMap newMap = deriveMap(newProperties, flags, fieldCount, spillLength); for (final Property property : otherProperties) { // This method is only safe to use with non-slotted, native getter/setter properties assert property.getSlot() == -1; @@ -592,19 +619,26 @@ * * @return Properties as an array. */ - public Property[] getProperties() { + public final Property[] getProperties() { return properties.getProperties(); } /** + * Return the name of the class of objects using this property map. + * + * @return class name of owner objects. + */ + public final String getClassName() { + return className; + } + + /** * Prevents the map from having additional properties. * * @return New map with {@link #NOT_EXTENSIBLE} flag set. */ PropertyMap preventExtensions() { - final PropertyMap newMap = new PropertyMap(this); - newMap.flags |= NOT_EXTENSIBLE; - return newMap; + return deriveMap(properties, flags | NOT_EXTENSIBLE, fieldCount, spillLength); } /** @@ -620,10 +654,7 @@ newProperties = newProperties.immutableAdd(oldProperty.addFlags(Property.NOT_CONFIGURABLE)); } - final PropertyMap newMap = new PropertyMap(this, newProperties); - newMap.flags |= NOT_EXTENSIBLE; - - return newMap; + return deriveMap(newProperties, flags | NOT_EXTENSIBLE, fieldCount, spillLength); } /** @@ -645,10 +676,7 @@ newProperties = newProperties.immutableAdd(oldProperty.addFlags(propertyFlags)); } - final PropertyMap newMap = new PropertyMap(this, newProperties); - newMap.flags |= NOT_EXTENSIBLE; - - return newMap; + return deriveMap(newProperties, flags | NOT_EXTENSIBLE, fieldCount, spillLength); } /** @@ -704,7 +732,7 @@ } if (Context.DEBUG && cachedMap != null) { - protoHistoryHit++; + protoHistoryHit.increment(); } return cachedMap; @@ -735,7 +763,7 @@ history = new WeakHashMap<>(); } - history.put(property, new SoftReference<>(newMap)); + history.put(property, softReferenceDerivationLimit == 0 ? new WeakReference<>(newMap) : new SoftReference<>(newMap)); } /** @@ -748,12 +776,12 @@ private PropertyMap checkHistory(final Property property) { if (history != null) { - final SoftReference<PropertyMap> ref = history.get(property); + final Reference<PropertyMap> ref = history.get(property); final PropertyMap historicMap = ref == null ? null : ref.get(); if (historicMap != null) { if (Context.DEBUG) { - historyHit++; + historyHit.increment(); } return historicMap; @@ -820,13 +848,6 @@ } /** - * Flag this object as having array keys in defined properties - */ - private void setContainsArrayKeys() { - flags |= CONTAINS_ARRAY_KEYS; - } - - /** * Test to see if {@link PropertyMap} is extensible. * * @return {@code true} if {@link PropertyMap} can be added to. @@ -901,15 +922,75 @@ } if (Context.DEBUG) { - setProtoNewMapCount++; + setProtoNewMapCount.increment(); } - final PropertyMap newMap = new PropertyMap(this); + final PropertyMap newMap = makeUnsharedCopy(); addToProtoHistory(newProto, newMap); return newMap; } + /** + * Make a copy of this property map with the shared prototype field set to null. Note that this is + * only necessary for shared maps of top-level objects. Shared prototype maps represented by + * {@link SharedPropertyMap} are automatically converted to plain property maps when they evolve. + * + * @return a copy with the shared proto map unset + */ + PropertyMap makeUnsharedCopy() { + final PropertyMap newMap = new PropertyMap(this); + newMap.sharedProtoMap = null; + return newMap; + } + + /** + * Set a reference to the expected parent prototype map. This is used for class-like + * structures where we only want to use a top-level property map if all of the + * prototype property maps have not been modified. + * + * @param protoMap weak reference to the prototype property map + */ + void setSharedProtoMap(final SharedPropertyMap protoMap) { + sharedProtoMap = protoMap; + } + + /** + * Get the expected prototype property map if it is known, or null. + * + * @return parent map or null + */ + public PropertyMap getSharedProtoMap() { + return sharedProtoMap; + } + + /** + * Returns {@code true} if this map has been used as a shared prototype map (i.e. as a prototype + * for a JavaScript constructor function) and has not had properties added, deleted or replaced since then. + * @return true if this is a valid shared prototype map + */ + boolean isValidSharedProtoMap() { + return false; + } + + /** + * Returns the shared prototype switch point, or null if this is not a shared prototype map. + * @return the shared prototype switch point, or null + */ + SwitchPoint getSharedProtoSwitchPoint() { + return null; + } + + /** + * Return true if this map has a shared prototype map which has either been invalidated or does + * not match the map of {@code proto}. + * @param prototype the prototype object + * @return true if this is an invalid shared map for {@code prototype} + */ + boolean isInvalidSharedMapFor(final ScriptObject prototype) { + return sharedProtoMap != null + && (!sharedProtoMap.isValidSharedProtoMap() || prototype == null || sharedProtoMap != prototype.getMap()); + } /** * {@link PropertyMap} iterator. @@ -990,10 +1071,10 @@ for (final Property p : map0.getProperties()) { final Property p2 = map1.findProperty(p.getKey()); if (p2 == null) { - sb.append("FIRST ONLY : [" + p + "]"); + sb.append("FIRST ONLY : [").append(p).append("]"); found = true; } else if (p2 != p) { - sb.append("DIFFERENT : [" + p + "] != [" + p2 + "]"); + sb.append("DIFFERENT : [").append(p).append("] != [").append(p2).append("]"); found = true; } } @@ -1001,7 +1082,7 @@ for (final Property p2 : map1.getProperties()) { final Property p1 = map0.findProperty(p2.getKey()); if (p1 == null) { - sb.append("SECOND ONLY: [" + p2 + "]"); + sb.append("SECOND ONLY: [").append(p2).append("]"); found = true; } } @@ -1021,52 +1102,62 @@ } // counters updated only in debug mode - private static int count; - private static int clonedCount; - private static int historyHit; - private static int protoInvalidations; - private static int protoHistoryHit; - private static int setProtoNewMapCount; + private static LongAdder count; + private static LongAdder clonedCount; + private static LongAdder historyHit; + private static LongAdder protoInvalidations; + private static LongAdder protoHistoryHit; + private static LongAdder setProtoNewMapCount; + static { + if (Context.DEBUG) { + count = new LongAdder(); + clonedCount = new LongAdder(); + historyHit = new LongAdder(); + protoInvalidations = new LongAdder(); + protoHistoryHit = new LongAdder(); + setProtoNewMapCount = new LongAdder(); + } + } /** * @return Total number of maps. */ - public static int getCount() { - return count; + public static long getCount() { + return count.longValue(); } /** * @return The number of maps that were cloned. */ - public static int getClonedCount() { - return clonedCount; + public static long getClonedCount() { + return clonedCount.longValue(); } /** * @return The number of times history was successfully used. */ - public static int getHistoryHit() { - return historyHit; + public static long getHistoryHit() { + return historyHit.longValue(); } /** * @return The number of times prototype changes caused invalidation. */ - public static int getProtoInvalidations() { - return protoInvalidations; + public static long getProtoInvalidations() { + return protoInvalidations.longValue(); } /** * @return The number of times proto history was successfully used. */ - public static int getProtoHistoryHit() { - return protoHistoryHit; + public static long getProtoHistoryHit() { + return protoHistoryHit.longValue(); } /** * @return The number of times prototypes were modified. */ - public static int getSetProtoNewMapCount() { - return setProtoNewMapCount; + public static long getSetProtoNewMapCount() { + return setProtoNewMapCount.longValue(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/PrototypeObject.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.runtime; + +import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import jdk.nashorn.internal.objects.Global; + +/** + * Instances of this class serve as "prototype" object for script functions. + * The purpose is to expose "constructor" property from "prototype". Also, nasgen + * generated prototype classes extend from this class. + */ +public class PrototypeObject extends ScriptObject { + private static final PropertyMap map$; + + private Object constructor; + + private static final MethodHandle GET_CONSTRUCTOR = findOwnMH("getConstructor", Object.class, Object.class); + private static final MethodHandle SET_CONSTRUCTOR = findOwnMH("setConstructor", void.class, Object.class, Object.class); + + static { + final ArrayList<Property> properties = new ArrayList<>(1); + properties.add(AccessorProperty.create("constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR)); + map$ = PropertyMap.newMap(properties); + } + + private PrototypeObject(final Global global, final PropertyMap map) { + super(global.getObjectPrototype(), map != map$? map.addAll(map$) : map$); + } + + /** + * Prototype constructor + */ + protected PrototypeObject() { + this(Global.instance(), map$); + } + + /** + * PropertyObject constructor + * + * @param map property map + */ + protected PrototypeObject(final PropertyMap map) { + this(Global.instance(), map); + } + + /** + * PropertyObject constructor + * + * @param func constructor function + */ + protected PrototypeObject(final ScriptFunction func) { + this(Global.instance(), map$); + this.constructor = func; + } + + /** + * Get the constructor for this {@code PrototypeObject} + * @param self self reference + * @return constructor, probably, but not necessarily, a {@link ScriptFunction} + */ + public static Object getConstructor(final Object self) { + return (self instanceof PrototypeObject) ? + ((PrototypeObject)self).getConstructor() : + UNDEFINED; + } + + /** + * Reset the constructor for this {@code PrototypeObject} + * @param self self reference + * @param constructor constructor, probably, but not necessarily, a {@link ScriptFunction} + */ + public static void setConstructor(final Object self, final Object constructor) { + if (self instanceof PrototypeObject) { + ((PrototypeObject)self).setConstructor(constructor); + } + } + + private Object getConstructor() { + return constructor; + } + + private void setConstructor(final Object constructor) { + this.constructor = constructor; + } + + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { + return MH.findStatic(MethodHandles.lookup(), PrototypeObject.class, name, MH.type(rtype, types)); + } +}
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,16 +26,25 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; + import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; @@ -45,9 +54,16 @@ import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; import jdk.nashorn.internal.codegen.TypeMap; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.SwitchNode; +import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.ir.TryNode; +import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Parser; import jdk.nashorn.internal.parser.Token; @@ -55,6 +71,7 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; import jdk.nashorn.internal.runtime.logging.Logger; +import jdk.nashorn.internal.runtime.options.Options; /** * This is a subclass that represents a script function that may be regenerated, * for example with specialization based on call site types, or lazily generated. @@ -66,6 +83,8 @@ /** Prefix used for all recompiled script classes */ public static final String RECOMPILATION_PREFIX = "Recompilation$"; + private static final ExecutorService astSerializerExecutorService = createAstSerializerExecutorService(); + /** Unique function node id for this function node */ private final int functionNodeId; @@ -77,8 +96,12 @@ /** Source from which FunctionNode was parsed. */ private transient Source source; - /** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */ - private final byte[] serializedAst; + /** + * Cached form of the AST. Either a {@code SerializedAst} object used by split functions as they can't be + * reparsed from source, or a soft reference to a {@code FunctionNode} for other functions (it is safe + * to be cleared as they can be reparsed). + */ + private volatile Object cachedAst; /** Token of this function within the source. */ private final long token; @@ -97,7 +120,7 @@ private final Object endParserState; /** Code installer used for all further recompilation/specialization of this ScriptFunction */ - private transient CodeInstaller<ScriptEnvironment> installer; + private transient CodeInstaller installer; private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions; @@ -128,16 +151,14 @@ * @param nestedFunctions nested function map * @param externalScopeDepths external scope depths * @param internalSymbols internal symbols to method, defined in its scope - * @param serializedAst a serialized AST representation. Normally only used for split functions. */ public RecompilableScriptFunctionData( final FunctionNode functionNode, - final CodeInstaller<ScriptEnvironment> installer, + final CodeInstaller installer, final AllocationStrategy allocationStrategy, final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, final Map<String, Integer> externalScopeDepths, - final Set<String> internalSymbols, - final byte[] serializedAst) { + final Set<String> internalSymbols) { super(functionName(functionNode), Math.min(functionNode.getParameters().size(), MAX_ARITY), @@ -161,7 +182,6 @@ nfn.setParent(this); } - this.serializedAst = serializedAst; createLogger(); } @@ -244,7 +264,7 @@ * @return parent data, or null if non exists and also null IF UNKNOWN. */ public RecompilableScriptFunctionData getParent() { - return parent; + return parent; } void setParent(final RecompilableScriptFunctionData parent) { @@ -266,7 +286,7 @@ * @param src source * @param inst code installer */ - public void initTransients(final Source src, final CodeInstaller<ScriptEnvironment> inst) { + public void initTransients(final Source src, final CodeInstaller inst) { if (this.source == null && this.installer == null) { this.source = src; this.installer = inst; @@ -349,8 +369,8 @@ } @Override - PropertyMap getAllocatorMap() { - return allocationStrategy.getAllocatorMap(); + PropertyMap getAllocatorMap(final ScriptObject prototype) { + return allocationStrategy.getAllocatorMap(prototype); } @Override @@ -358,13 +378,11 @@ return allocationStrategy.allocate(map); } - boolean isSerialized() { - return serializedAst != null; - } - FunctionNode reparse() { - if (isSerialized()) { - return deserialize(); + final FunctionNode cachedFunction = getCachedAst(); + if (cachedFunction != null) { + assert cachedFunction.isCached(); + return cachedFunction; } final int descPosition = Token.descPosition(token); @@ -391,8 +409,105 @@ return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); } - private FunctionNode deserialize() { - final ScriptEnvironment env = installer.getOwner(); + private FunctionNode getCachedAst() { + final Object lCachedAst = cachedAst; + // Are we softly caching the AST? + if (lCachedAst instanceof Reference<?>) { + final FunctionNode fn = (FunctionNode)((Reference<?>)lCachedAst).get(); + if (fn != null) { + // Yes we are - this is fast + return cloneSymbols(fn); + } + // Are we strongly caching a serialized AST (for split functions only)? + } else if (lCachedAst instanceof SerializedAst) { + final SerializedAst serializedAst = (SerializedAst)lCachedAst; + // Even so, are we also softly caching the AST? + final FunctionNode cachedFn = serializedAst.cachedAst.get(); + if (cachedFn != null) { + // Yes we are - this is fast + return cloneSymbols(cachedFn); + } + final FunctionNode deserializedFn = deserialize(serializedAst.serializedAst); + // Softly cache after deserialization, maybe next time we won't need to deserialize + serializedAst.cachedAst = new SoftReference<>(deserializedFn); + return deserializedFn; + } + // No cached representation; return null for reparsing + return null; + } + + /** + * Sets the AST to cache in this function + * @param astToCache the new AST to cache + */ + public void setCachedAst(final FunctionNode astToCache) { + assert astToCache.getId() == functionNodeId; // same function + assert !(cachedAst instanceof SerializedAst); // Can't overwrite serialized AST + + final boolean isSplit = astToCache.isSplit(); + // If we're caching a split function, we're doing it in the eager pass, hence there can be no other + // cached representation already. In other words, isSplit implies cachedAst == null. + assert !isSplit || cachedAst == null; // + + final FunctionNode symbolClonedAst = cloneSymbols(astToCache); + final Reference<FunctionNode> ref = new SoftReference<>(symbolClonedAst); + cachedAst = ref; + + // Asynchronously serialize split functions. + if (isSplit) { + astSerializerExecutorService.execute(new Runnable() { + @Override + public void run() { + cachedAst = new SerializedAst(symbolClonedAst, ref); + } + }); + } + } + + /** + * Creates the AST serializer executor service used for in-memory serialization of split functions' ASTs. + * It is created with an unbounded queue (so it can queue any number of pending tasks). Its core and max + * threads is the same, but they are all allowed to time out so when there's no work, they can all go + * away. The threads will be daemons, and they will time out if idle for a minute. Their priority is also + * slightly lower than normal priority as we'd prefer the CPU to keep running the program; serializing + * split function is a memory conservation measure (it allows us to release the AST), it can wait a bit. + * @return an executor service with above described characteristics. + */ + private static ExecutorService createAstSerializerExecutorService() { + final int threads = Math.max(1, Options.getIntProperty("nashorn.serialize.threads", Runtime.getRuntime().availableProcessors() / 2)); + final ThreadPoolExecutor service = new ThreadPoolExecutor(threads, threads, 1L, TimeUnit.MINUTES, new LinkedBlockingDeque<Runnable>(), + new ThreadFactory() { + @Override + public Thread newThread(final Runnable r) { + final Thread t = new Thread(r, "Nashorn AST Serializer"); + t.setDaemon(true); + t.setPriority(Thread.NORM_PRIORITY - 1); + return t; + } + }); + service.allowCoreThreadTimeOut(true); + return service; + } + + /** + * A tuple of a serialized AST and a soft reference to a deserialized AST. This is used to cache split + * functions. Since split functions are altered from their source form, they can't be reparsed from + * source. While we could just use the {@code byte[]} representation in {@link RecompilableScriptFunctionData#cachedAst} + * we're using this tuple instead to also keep a deserialized AST around in memory to cut down on + * deserialization costs. + */ + private static class SerializedAst { + private final byte[] serializedAst; + private volatile Reference<FunctionNode> cachedAst; + + SerializedAst(final FunctionNode fn, final Reference<FunctionNode> cachedAst) { + this.serializedAst = AstSerializer.serialize(fn); + this.cachedAst = cachedAst; + } + } + + private FunctionNode deserialize(final byte[] serializedAst) { + final ScriptEnvironment env = installer.getContext().getEnv(); final Timing timing = env._timing; final long t1 = System.nanoTime(); try { @@ -402,6 +517,107 @@ } } + private FunctionNode cloneSymbols(final FunctionNode fn) { + final IdentityHashMap<Symbol, Symbol> symbolReplacements = new IdentityHashMap<>(); + final boolean cached = fn.isCached(); + // blockDefinedSymbols is used to re-mark symbols defined outside the function as global. We only + // need to do this when we cache an eagerly parsed function (which currently means a split one, as we + // don't cache non-split functions from the eager pass); those already cached, or those not split + // don't need this step. + final Set<Symbol> blockDefinedSymbols = fn.isSplit() && !cached ? Collections.newSetFromMap(new IdentityHashMap<Symbol, Boolean>()) : null; + FunctionNode newFn = (FunctionNode)fn.accept(new SimpleNodeVisitor() { + + private Symbol getReplacement(final Symbol original) { + if (original == null) { + return null; + } + final Symbol existingReplacement = symbolReplacements.get(original); + if (existingReplacement != null) { + return existingReplacement; + } + final Symbol newReplacement = original.clone(); + symbolReplacements.put(original, newReplacement); + return newReplacement; + } + + @Override + public Node leaveIdentNode(final IdentNode identNode) { + final Symbol oldSymbol = identNode.getSymbol(); + if (oldSymbol != null) { + final Symbol replacement = getReplacement(oldSymbol); + return identNode.setSymbol(replacement); + } + return identNode; + } + + @Override + public Node leaveForNode(final ForNode forNode) { + return ensureUniqueLabels(forNode.setIterator(lc, getReplacement(forNode.getIterator()))); + } + + @Override + public Node leaveSwitchNode(final SwitchNode switchNode) { + return ensureUniqueLabels(switchNode.setTag(lc, getReplacement(switchNode.getTag()))); + } + + @Override + public Node leaveTryNode(final TryNode tryNode) { + return ensureUniqueLabels(tryNode.setException(lc, getReplacement(tryNode.getException()))); + } + + @Override + public boolean enterBlock(final Block block) { + for(final Symbol symbol: block.getSymbols()) { + final Symbol replacement = getReplacement(symbol); + if (blockDefinedSymbols != null) { + blockDefinedSymbols.add(replacement); + } + } + return true; + } + + @Override + public Node leaveBlock(final Block block) { + return ensureUniqueLabels(block.replaceSymbols(lc, symbolReplacements)); + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + return functionNode.setParameters(lc, functionNode.visitParameters(this)); + } + + @Override + protected Node leaveDefault(final Node node) { + return ensureUniqueLabels(node); + }; + + private Node ensureUniqueLabels(final Node node) { + // If we're returning a cached AST, we must also ensure unique labels + return cached ? node.ensureUniqueLabels(lc) : node; + } + }); + + if (blockDefinedSymbols != null) { + // Mark all symbols not defined in blocks as globals + Block newBody = null; + for(final Symbol symbol: symbolReplacements.values()) { + if(!blockDefinedSymbols.contains(symbol)) { + assert symbol.isScope(); // must be scope + assert externalScopeDepths.containsKey(symbol.getName()); // must be known to us as an external + // Register it in the function body symbol table as a new global symbol + symbol.setFlags((symbol.getFlags() & ~Symbol.KINDMASK) | Symbol.IS_GLOBAL); + if (newBody == null) { + newBody = newFn.getBody().copyWithNewSymbols(); + newFn = newFn.setBody(null, newBody); + } + assert newBody.getExistingSymbol(symbol.getName()) == null; // must not be defined in the body already + newBody.putSymbol(symbol); + } + } + } + return newFn.setCached(null); + } + private boolean getFunctionFlag(final int flag) { return (functionFlags & flag) != 0; } @@ -438,8 +654,8 @@ * a new class loader with optimistic typing so that deoptimized code can get reclaimed by GC. * @return a code installer for installing new code. */ - private CodeInstaller<ScriptEnvironment> getInstallerForNewCode() { - final ScriptEnvironment env = installer.getOwner(); + private CodeInstaller getInstallerForNewCode() { + final ScriptEnvironment env = installer.getContext().getEnv(); return env._optimistic_types || env._loader_per_compile ? installer.withNewLoader() : installer; } @@ -449,15 +665,10 @@ final TypeMap typeMap = typeMap(actualCallSiteType); final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId); final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, paramTypes); - final Context context = Context.getContextTrusted(); - return new Compiler( - context, - context.getEnv(), + return Compiler.forOnDemandCompilation( getInstallerForNewCode(), functionNode.getSource(), // source - context.getErrorManager(), isStrict() | functionNode.isStrict(), // is strict - true, // is on demand this, // compiledFunction, i.e. this RecompilableScriptFunctionData typeMap, // type map getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points @@ -500,7 +711,7 @@ final TypeMap typeMap = typeMap(actualCallSiteType); final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId); cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes); - final CodeInstaller<ScriptEnvironment> newInstaller = getInstallerForNewCode(); + final CodeInstaller newInstaller = getInstallerForNewCode(); final StoredScript script = newInstaller.loadScript(source, cacheKey); if (script != null) { @@ -512,16 +723,16 @@ final FunctionNode fn = reparse(); final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope); final FunctionNode compiledFn = compiler.compile(fn, - isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL); + fn.isCached() ? CompilationPhases.COMPILE_ALL_CACHED : CompilationPhases.COMPILE_ALL); - if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) { + if (persist && !compiledFn.hasApplyToCallSpecialization()) { compiler.persistClassInfo(cacheKey, compiledFn); } return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints()); } boolean usePersistentCodeCache() { - return installer != null && installer.getOwner()._persistent_cache; + return installer != null && installer.getContext().getEnv()._persistent_cache; } private MethodType explicitParams(final MethodType callSiteType) { @@ -553,7 +764,7 @@ private FunctionNode extractFunctionFromScript(final FunctionNode script) { final Set<FunctionNode> fns = new HashSet<>(); - script.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + script.getBody().accept(new SimpleNodeVisitor() { @Override public boolean enterFunctionNode(final FunctionNode fn) { fns.add(fn); @@ -617,6 +828,7 @@ private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int fnFlags) { final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, callSiteType, fnFlags); + assert noDuplicateCode(cfn) : "duplicate code"; code.add(cfn); return cfn; } @@ -683,14 +895,17 @@ @Override synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) { - CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope, forbidden); + assert isValidCallSite(callSiteType) : callSiteType; + + CompiledFunction existingBest = pickFunction(callSiteType, false); + if (existingBest == null) { + existingBest = pickFunction(callSiteType, true); // try vararg last + } if (existingBest == null) { existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType); } assert existingBest != null; - //we are calling a vararg method with real args - boolean varArgWithRealArgs = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType); //if the best one is an apply to call, it has to match the callsite exactly //or we need to regenerate @@ -699,27 +914,18 @@ if (best != null) { return best; } - varArgWithRealArgs = true; - } - if (varArgWithRealArgs) { // special case: we had an apply to call, but we failed to make it fit. // Try to generate a specialized one for this callsite. It may // be another apply to call specialization, or it may not, but whatever // it is, it is a specialization that is guaranteed to fit - final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false); - existingBest = addCode(fnInit, callSiteType); + existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, false), callSiteType); } return existingBest; } @Override - boolean isRecompilable() { - return true; - } - - @Override public boolean needsCallee() { return getFunctionFlag(FunctionNode.NEEDS_CALLEE); } @@ -827,6 +1033,16 @@ return newFn; } + // Make sure code does not contain a compiled function with the same signature as compiledFunction + private boolean noDuplicateCode(final CompiledFunction compiledFunction) { + for (final CompiledFunction cf : code) { + if (cf.type().equals(compiledFunction.type())) { + return false; + } + } + return true; + } + private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); createLogger();
--- a/src/jdk/nashorn/internal/runtime/Scope.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/Scope.java Wed Dec 02 23:08:29 2015 -0800 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; +import java.util.concurrent.atomic.LongAdder; import jdk.nashorn.internal.codegen.CompilerConstants; /** @@ -38,7 +39,7 @@ private int splitState = -1; /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */ - private static int count; + private static final LongAdder count = Context.DEBUG ? new LongAdder() : null; /** Method handle that points to {@link Scope#getSplitState}. */ public static final CompilerConstants.Call GET_SPLIT_STATE = virtualCallNoLookup(Scope.class, "getSplitState", int.class); @@ -52,9 +53,7 @@ */ public Scope(final PropertyMap map) { super(map); - if (Context.DEBUG) { - count++; - } + incrementCount(); } /** @@ -65,9 +64,7 @@ */ public Scope(final ScriptObject proto, final PropertyMap map) { super(proto, map); - if (Context.DEBUG) { - count++; - } + incrementCount(); } /** @@ -79,9 +76,7 @@ */ public Scope(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) { super(map, primitiveSpill, objectSpill); - if (Context.DEBUG) { - count++; - } + incrementCount(); } @Override @@ -123,7 +118,13 @@ * * @return number of scope ScriptObjects created */ - public static int getScopeCount() { - return count; + public static long getScopeCount() { + return count != null ? count.sum() : 0; + } + + private static void incrementCount() { + if (Context.DEBUG) { + count.increment(); + } } }
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed Dec 02 23:08:29 2015 -0800 @@ -46,6 +46,11 @@ * and output and error writers, top level Namespace etc. */ public final class ScriptEnvironment { + // Primarily intended to be used in test environments so that eager compilation tests work without an + // error when tested with optimistic compilation. + private static final boolean ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE = Options.getBooleanProperty( + "nashorn.options.allowEagerCompilationSilentOverride", false); + /** Output writer for this environment */ private final PrintWriter out; @@ -237,8 +242,20 @@ } _fx = options.getBoolean("fx"); _global_per_engine = options.getBoolean("global.per.engine"); - _lazy_compilation = options.getBoolean("lazy.compilation"); _optimistic_types = options.getBoolean("optimistic.types"); + final boolean lazy_compilation = options.getBoolean("lazy.compilation"); + if (!lazy_compilation && _optimistic_types) { + if (!ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE) { + throw new IllegalStateException( + ECMAErrors.getMessage( + "config.error.eagerCompilationConflictsWithOptimisticTypes", + options.getOptionTemplateByKey("lazy.compilation").getName(), + options.getOptionTemplateByKey("optimistic.types").getName())); + } + _lazy_compilation = true; + } else { + _lazy_compilation = lazy_compilation; + } _loader_per_compile = options.getBoolean("loader.per.compile"); _no_java = options.getBoolean("no.java"); _no_syntax_extensions = options.getBoolean("no.syntax.extensions");
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed Dec 02 23:08:29 2015 -0800 @@ -22,7 +22,6 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; @@ -40,6 +39,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.concurrent.atomic.LongAdder; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -55,38 +55,54 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; /** - * Runtime representation of a JavaScript function. + * Runtime representation of a JavaScript function. This class has only private + * and protected constructors. There are no *public* constructors - but only + * factory methods that follow the naming pattern "createXYZ". */ -public abstract class ScriptFunction extends ScriptObject { +public class ScriptFunction extends ScriptObject { - /** Method handle for prototype getter for this ScriptFunction */ + /** + * Method handle for prototype getter for this ScriptFunction + */ public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class); - /** Method handle for prototype setter for this ScriptFunction */ + /** + * Method handle for prototype setter for this ScriptFunction + */ public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class); - /** Method handle for length getter for this ScriptFunction */ + /** + * Method handle for length getter for this ScriptFunction + */ public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class); - /** Method handle for name getter for this ScriptFunction */ + /** + * Method handle for name getter for this ScriptFunction + */ public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class); - /** Method handle used for implementing sync() in mozilla_compat */ + /** + * Method handle used for implementing sync() in mozilla_compat + */ public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class); - /** Method handle for allocate function for this ScriptFunction */ + /** + * Method handle for allocate function for this ScriptFunction + */ static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class); private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class); private static final MethodHandle SCRIPTFUNCTION_GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); - /** method handle to scope getter for this ScriptFunction */ + /** + * method handle to scope getter for this ScriptFunction + */ public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); - private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); + private static final MethodHandle IS_FUNCTION_MH = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); - private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class); + private static final MethodHandle IS_APPLY_FUNCTION = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class); private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class); @@ -94,55 +110,298 @@ private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class)); - /** The parent scope. */ + // various property maps used for different kinds of functions + // property map for anonymous function that serves as Function.prototype + private static final PropertyMap anonmap$; + // property map for strict mode functions + private static final PropertyMap strictmodemap$; + // property map for bound functions + private static final PropertyMap boundfunctionmap$; + // property map for non-strict, non-bound functions. + private static final PropertyMap map$; + + // Marker object for lazily initialized prototype object + private static final Object LAZY_PROTOTYPE = new Object(); + + private static PropertyMap createStrictModeMap(final PropertyMap map) { + final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; + PropertyMap newMap = map; + // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. + newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags)); + newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags)); + return newMap; + } + + private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { + // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see + // ECMAScript 5.1 section 15.3.4.5 + return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype")); + } + + static { + anonmap$ = PropertyMap.newMap(); + final ArrayList<Property> properties = new ArrayList<>(3); + properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE)); + properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null)); + properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null)); + map$ = PropertyMap.newMap(properties); + strictmodemap$ = createStrictModeMap(map$); + boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); + } + + private static boolean isStrict(final int flags) { + return (flags & ScriptFunctionData.IS_STRICT) != 0; + } + + // Choose the map based on strict mode! + private static PropertyMap getMap(final boolean strict) { + return strict ? strictmodemap$ : map$; + } + + /** + * The parent scope. + */ private final ScriptObject scope; private final ScriptFunctionData data; - /** The property map used for newly allocated object when function is used as constructor. */ + /** + * The property map used for newly allocated object when function is used as + * constructor. + */ protected PropertyMap allocatorMap; /** + * Reference to constructor prototype. + */ + protected Object prototype; + + /** * Constructor * - * @param name function name - * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) - * @param map property map - * @param scope scope - * @param specs specialized version of this function - other method handles - * @param flags {@link ScriptFunctionData} flags + * @param data static function data + * @param map property map + * @param scope scope */ - protected ScriptFunction( + private ScriptFunction( + final ScriptFunctionData data, + final PropertyMap map, + final ScriptObject scope, + final Global global) { + + super(map); + + if (Context.DEBUG) { + constructorCount.increment(); + } + + this.data = data; + this.scope = scope; + this.setInitialProto(global.getFunctionPrototype()); + this.prototype = LAZY_PROTOTYPE; + + // We have to fill user accessor functions late as these are stored + // in this object rather than in the PropertyMap of this object. + assert objectSpill == null; + if (isStrict() || isBoundFunction()) { + final ScriptFunction typeErrorThrower = global.getTypeErrorThrower(); + initUserAccessors("arguments", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); + initUserAccessors("caller", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower); + } + } + + /** + * Constructor + * + * @param name function name + * @param methodHandle method handle to function (if specializations are + * present, assumed to be most generic) + * @param map property map + * @param scope scope + * @param specs specialized version of this function - other method handles + * @param flags {@link ScriptFunctionData} flags + */ + private ScriptFunction( final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, final Specialization[] specs, - final int flags) { - - this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope); + final int flags, + final Global global) { + this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope, global); } /** * Constructor * - * @param data static function data - * @param map property map - * @param scope scope + * @param name name of function + * @param methodHandle handle for invocation + * @param scope scope object + * @param specs specialized versions of this method, if available, null + * otherwise + * @param flags {@link ScriptFunctionData} flags + */ + private ScriptFunction( + final String name, + final MethodHandle methodHandle, + final ScriptObject scope, + final Specialization[] specs, + final int flags) { + this(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags, Global.instance()); + } + + /** + * Constructor called by Nasgen generated code, zero added members, use the + * default map. Creates builtin functions only. + * + * @param name name of function + * @param invokeHandle handle for invocation + * @param specs specialized versions of this method, if available, null + * otherwise + */ + protected ScriptFunction(final String name, final MethodHandle invokeHandle, final Specialization[] specs) { + this(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance()); + } + + /** + * Constructor called by Nasgen generated code, non zero member count, use + * the map passed as argument. Creates builtin functions only. + * + * @param name name of function + * @param invokeHandle handle for invocation + * @param map initial property map + * @param specs specialized versions of this method, if available, null + * otherwise + */ + protected ScriptFunction(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) { + this(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR, Global.instance()); + } + + // Factory methods to create various functions + /** + * Factory method called by compiler generated code for functions that need + * parent scope. + * + * @param constants the generated class' constant array + * @param index the index of the {@code RecompilableScriptFunctionData} + * object in the constants array. + * @param scope the parent scope object + * @return a newly created function object */ - protected ScriptFunction( - final ScriptFunctionData data, - final PropertyMap map, - final ScriptObject scope) { + public static ScriptFunction create(final Object[] constants, final int index, final ScriptObject scope) { + final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constants[index]; + return new ScriptFunction(data, getMap(data.isStrict()), scope, Global.instance()); + } + + /** + * Factory method called by compiler generated code for functions that don't + * need parent scope. + * + * @param constants the generated class' constant array + * @param index the index of the {@code RecompilableScriptFunctionData} + * object in the constants array. + * @return a newly created function object + */ + public static ScriptFunction create(final Object[] constants, final int index) { + return create(constants, index, null); + } + + /** + * Create anonymous function that serves as Function.prototype + * + * @return anonymous function object + */ + public static ScriptFunction createAnonymous() { + return new ScriptFunction("", GlobalFunctions.ANONYMOUS, anonmap$, null); + } + + // builtin function create helper factory + private static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) { + final ScriptFunction func = new ScriptFunction(name, methodHandle, null, specs, flags); + func.setPrototype(UNDEFINED); + // Non-constructor built-in functions do not have "prototype" property + func.deleteOwnProperty(func.getMap().findProperty("prototype")); + + return func; + } - super(map); + /** + * Factory method for non-constructor built-in functions + * + * @param name function name + * @param methodHandle handle for invocation + * @param specs specialized versions of function if available, null + * otherwise + * @return new ScriptFunction + */ + public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle, final Specialization[] specs) { + return ScriptFunction.createBuiltin(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN); + } - if (Context.DEBUG) { - constructorCount++; + /** + * Factory method for non-constructor built-in functions + * + * @param name function name + * @param methodHandle handle for invocation + * @return new ScriptFunction + */ + public static ScriptFunction createBuiltin(final String name, final MethodHandle methodHandle) { + return ScriptFunction.createBuiltin(name, methodHandle, null); + } + + /** + * Factory method for non-constructor built-in, strict functions + * + * @param name function name + * @param methodHandle handle for invocation + * @return new ScriptFunction + */ + public static ScriptFunction createStrictBuiltin(final String name, final MethodHandle methodHandle) { + return ScriptFunction.createBuiltin(name, methodHandle, null, ScriptFunctionData.IS_BUILTIN | ScriptFunctionData.IS_STRICT); + } + + // Subclass to represent bound functions + private static class Bound extends ScriptFunction { + private final ScriptFunction target; + + Bound(final ScriptFunctionData boundData, final ScriptFunction target) { + super(boundData, boundfunctionmap$, null, Global.instance()); + setPrototype(ScriptRuntime.UNDEFINED); + this.target = target; } - this.data = data; - this.scope = scope; + @Override + protected ScriptFunction getTargetFunction() { + return target; + } + } + + /** + * Creates a version of this function bound to a specific "self" and other + * arguments, as per {@code Function.prototype.bind} functionality in + * ECMAScript 5.1 section 15.3.4.5. + * + * @param self the self to bind to this function. Can be null (in which + * case, null is bound as this). + * @param args additional arguments to bind to this function. Can be null or + * empty to not bind additional arguments. + * @return a function with the specified self and parameters bound. + */ + public final ScriptFunction createBound(final Object self, final Object[] args) { + return new Bound(data.makeBoundFunctionData(this, self, args), getTargetFunction()); + } + + /** + * 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 final ScriptFunction createSynchronized(final Object sync) { + final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync); + return createBuiltin(getName(), mh); } @Override @@ -151,8 +410,8 @@ } /** - * ECMA 15.3.5.3 [[HasInstance]] (V) - * Step 3 if "prototype" value is not an Object, throw TypeError + * ECMA 15.3.5.3 [[HasInstance]] (V) Step 3 if "prototype" value is not an + * Object, throw TypeError */ @Override public boolean isInstance(final ScriptObject instance) { @@ -171,22 +430,25 @@ } /** - * Returns the target function for this function. If the function was not created using - * {@link #makeBoundFunction(Object, Object[])}, its target function is itself. If it is bound, its target function - * is the target function of the function it was made from (therefore, the target function is always the final, - * unbound recipient of the calls). + * Returns the target function for this function. If the function was not + * created using {@link #createBound(Object, Object[])}, its target + * function is itself. If it is bound, its target function is the target + * function of the function it was made from (therefore, the target function + * is always the final, unbound recipient of the calls). + * * @return the target function for this function. */ protected ScriptFunction getTargetFunction() { return this; } - boolean isBoundFunction() { + final boolean isBoundFunction() { return getTargetFunction() != this; } /** * Set the arity of this ScriptFunction + * * @param arity arity */ public final void setArity(final int arity) { @@ -195,125 +457,121 @@ /** * Is this a ECMAScript 'use strict' function? + * * @return true if function is in strict mode */ - public boolean isStrict() { + public final boolean isStrict() { return data.isStrict(); } /** - * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument - * according to ECMA 10.4.3. + * Returns true if this is a non-strict, non-built-in function that requires + * non-primitive this argument according to ECMA 10.4.3. + * * @return true if this argument must be an object */ - public boolean needsWrappedThis() { + public final boolean needsWrappedThis() { return data.needsWrappedThis(); } private static boolean needsWrappedThis(final Object fn) { - return fn instanceof ScriptFunction ? ((ScriptFunction)fn).needsWrappedThis() : false; + return fn instanceof ScriptFunction ? ((ScriptFunction) fn).needsWrappedThis() : false; } /** * Execute this script function. - * @param self Target object. - * @param arguments Call arguments. + * + * @param self Target object. + * @param arguments Call arguments. * @return ScriptFunction result. - * @throws Throwable if there is an exception/error with the invocation or thrown from it + * @throws Throwable if there is an exception/error with the invocation or + * thrown from it */ - Object invoke(final Object self, final Object... arguments) throws Throwable { + final Object invoke(final Object self, final Object... arguments) throws Throwable { if (Context.DEBUG) { - invokes++; + invokes.increment(); } return data.invoke(this, self, arguments); } /** * Execute this script function as a constructor. - * @param arguments Call arguments. + * + * @param arguments Call arguments. * @return Newly constructed result. - * @throws Throwable if there is an exception/error with the invocation or thrown from it + * @throws Throwable if there is an exception/error with the invocation or + * thrown from it */ - Object construct(final Object... arguments) throws Throwable { + final Object construct(final Object... arguments) throws Throwable { return data.construct(this, arguments); } /** - * Allocate function. Called from generated {@link ScriptObject} code - * for allocation as a factory method + * Allocate function. Called from generated {@link ScriptObject} code for + * allocation as a factory method * - * @return a new instance of the {@link ScriptObject} whose allocator this is + * @return a new instance of the {@link ScriptObject} whose allocator this + * is */ @SuppressWarnings("unused") private Object allocate() { if (Context.DEBUG) { - allocations++; + allocations.increment(); } assert !isBoundFunction(); // allocate never invoked on bound functions - final ScriptObject object = data.allocate(getAllocatorMap()); + final ScriptObject prototype = getAllocatorPrototype(); + final ScriptObject object = data.allocate(getAllocatorMap(prototype)); if (object != null) { - final Object prototype = getPrototype(); - if (prototype instanceof ScriptObject) { - object.setInitialProto((ScriptObject)prototype); - } - - if (object.getProto() == null) { - object.setInitialProto(getObjectPrototype()); - } + object.setInitialProto(prototype); } return object; } - private PropertyMap getAllocatorMap() { - if (allocatorMap == null) { - allocatorMap = data.getAllocatorMap(); + /** + * Get the property map used by "allocate" + * @param prototype actual prototype object + * @return property map + */ + private synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) { + if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) { + // The prototype map has changed since this function was last used as constructor. + // Get a new allocator map. + allocatorMap = data.getAllocatorMap(prototype); } return allocatorMap; } /** - * Return Object.prototype - used by "allocate" - * @return Object.prototype - */ - protected abstract ScriptObject getObjectPrototype(); - - /** - * Creates a version of this function bound to a specific "self" and other arguments, as per - * {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5. - * @param self the self to bind to this function. Can be null (in which case, null is bound as this). - * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. - * @return a function with the specified self and parameters bound. + * Return the actual prototype used by "allocate" + * @return allocator prototype */ - protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) { - return makeBoundFunction(data.makeBoundFunctionData(this, self, args)); + private ScriptObject getAllocatorPrototype() { + final Object prototype = getPrototype(); + if (prototype instanceof ScriptObject) { + return (ScriptObject) prototype; + } + return Global.objectPrototype(); } - /** - * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])}, - * but using a {@link ScriptFunctionData} for the bound data. - * - * @param boundData ScriptFuntionData for the bound function - * @return a function with the bindings performed according to the given data - */ - protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData); - @Override public final String safeToString() { return toSource(); } @Override - public String toString() { + public final String toString() { return data.toString(); } /** - * Get this function as a String containing its source code. If no source code - * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} + * Get this function as a String containing its source code. If no source + * code exists in this ScriptFunction, its contents will be displayed as + * {@code [native code]} + * * @return string representation of this function's source */ public final String toSource() { @@ -322,27 +580,32 @@ /** * Get the prototype object for this function + * * @return prototype */ - public abstract Object getPrototype(); + public final Object getPrototype() { + if (prototype == LAZY_PROTOTYPE) { + prototype = new PrototypeObject(this); + } + return prototype; + } /** * Set the prototype object for this function - * @param prototype new prototype object + * + * @param newPrototype new prototype object */ - public abstract void setPrototype(Object prototype); + public synchronized final void setPrototype(final Object newPrototype) { + if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) { + // Unset allocator map to be replaced with one matching the new prototype. + allocatorMap = null; + } + this.prototype = newPrototype; + } /** - * 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 invoke handle bound to a given ScriptObject self reference. - * If callee parameter is required result is rebound to this. + * Return the invoke handle bound to a given ScriptObject self reference. If + * callee parameter is required result is rebound to this. * * @param self self reference * @return bound invoke handle @@ -352,9 +615,12 @@ } /** - * Bind the method handle to this {@code ScriptFunction} instance if it needs a callee parameter. If this function's - * method handles don't have a callee parameter, the handle is returned unchanged. - * @param methodHandle the method handle to potentially bind to this function instance. + * Bind the method handle to this {@code ScriptFunction} instance if it + * needs a callee parameter. If this function's method handles don't have a + * callee parameter, the handle is returned unchanged. + * + * @param methodHandle the method handle to potentially bind to this + * function instance. * @return the potentially bound method handle */ private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) { @@ -364,15 +630,16 @@ /** * Get the name for this function + * * @return the name */ public final String getName() { return data.getName(); } - /** * Get the scope for this function + * * @return the scope */ public final ScriptObject getScope() { @@ -383,36 +650,37 @@ * Prototype getter for this ScriptFunction - follows the naming convention * used by Nasgen and the code generator * - * @param self self reference + * @param self self reference * @return self's prototype */ public static Object G$prototype(final Object self) { - return self instanceof ScriptFunction ? - ((ScriptFunction)self).getPrototype() : - UNDEFINED; + return self instanceof ScriptFunction + ? ((ScriptFunction) self).getPrototype() + : UNDEFINED; } /** * Prototype setter for this ScriptFunction - follows the naming convention * used by Nasgen and the code generator * - * @param self self reference + * @param self self reference * @param prototype prototype to set */ public static void S$prototype(final Object self, final Object prototype) { if (self instanceof ScriptFunction) { - ((ScriptFunction)self).setPrototype(prototype); + ((ScriptFunction) self).setPrototype(prototype); } } /** * Length getter - ECMA 15.3.3.2: Function.length + * * @param self self reference * @return length */ public static int G$length(final Object self) { if (self instanceof ScriptFunction) { - return ((ScriptFunction)self).data.getArity(); + return ((ScriptFunction) self).data.getArity(); } return 0; @@ -420,12 +688,13 @@ /** * Name getter - ECMA Function.name + * * @param self self refence * @return the name, or undefined if none */ public static Object G$name(final Object self) { if (self instanceof ScriptFunction) { - return ((ScriptFunction)self).getName(); + return ((ScriptFunction) self).getName(); } return UNDEFINED; @@ -433,6 +702,7 @@ /** * Get the prototype for this ScriptFunction + * * @param constructor constructor * @return prototype, or null if given constructor is not a ScriptFunction */ @@ -440,7 +710,7 @@ if (constructor != null) { final Object proto = constructor.getPrototype(); if (proto instanceof ScriptObject) { - return (ScriptObject)proto; + return (ScriptObject) proto; } } @@ -448,29 +718,37 @@ } // These counters are updated only in debug mode. - private static int constructorCount; - private static int invokes; - private static int allocations; + private static LongAdder constructorCount; + private static LongAdder invokes; + private static LongAdder allocations; + + static { + if (Context.DEBUG) { + constructorCount = new LongAdder(); + invokes = new LongAdder(); + allocations = new LongAdder(); + } + } /** * @return the constructorCount */ - public static int getConstructorCount() { - return constructorCount; + public static long getConstructorCount() { + return constructorCount.longValue(); } /** * @return the invokes */ - public static int getInvokes() { - return invokes; + public static long getInvokes() { + return invokes.longValue(); } /** * @return the allocations */ - public static int getAllocations() { - return allocations; + public static long getAllocations() { + return allocations.longValue(); } @Override @@ -490,7 +768,6 @@ return Context.getGlobal().wrapAsObject(obj); } - @SuppressWarnings("unused") private static Object globalFilter(final Object object) { // replace whatever we get with the current global object @@ -498,14 +775,16 @@ } /** - * Some receivers are primitive, in that case, according to the Spec we create a new - * native object per callsite with the wrap filter. We can only apply optimistic builtins - * if there is no per instance state saved for these wrapped objects (e.g. currently NativeStrings), - * otherwise we can't create optimistic versions + * Some receivers are primitive, in that case, according to the Spec we + * create a new native object per callsite with the wrap filter. We can only + * apply optimistic builtins if there is no per instance state saved for + * these wrapped objects (e.g. currently NativeStrings), otherwise we can't + * create optimistic versions * - * @param self receiver - * @param linkLogicClass linkLogicClass, or null if no link logic exists - * @return link logic instance, or null if one could not be constructed for this receiver + * @param self receiver + * @param linkLogicClass linkLogicClass, or null if no link logic exists + * @return link logic instance, or null if one could not be constructed for + * this receiver */ private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) { if (linkLogicClass == null) { @@ -518,25 +797,25 @@ final Object wrappedSelf = wrapFilter(self); if (wrappedSelf instanceof OptimisticBuiltins) { - if (wrappedSelf != self && ((OptimisticBuiltins)wrappedSelf).hasPerInstanceAssumptions()) { + if (wrappedSelf != self && ((OptimisticBuiltins) wrappedSelf).hasPerInstanceAssumptions()) { return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state } - return ((OptimisticBuiltins)wrappedSelf).getLinkLogic(linkLogicClass); + return ((OptimisticBuiltins) wrappedSelf).getLinkLogic(linkLogicClass); } return null; } /** - * dyn:call call site signature: (callee, thiz, [args...]) - * generated method signature: (callee, thiz, [args...]) + * dyn:call call site signature: (callee, thiz, [args...]) generated method + * signature: (callee, thiz, [args...]) * * cases: * (a) method has callee parameter - * (1) for local/scope calls, we just bind thiz and drop the second argument. - * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. + * (1) for local/scope calls, we just bind thiz and drop the second argument. + * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. * (b) method doesn't have callee parameter (builtin functions) - * (3) for local/scope calls, bind thiz and drop both callee and thiz. - * (4) for normal this-calls, drop callee. + * (3) for local/scope calls, bind thiz and drop both callee and thiz. + * (4) for normal this-calls, drop callee. * * @return guarded invocation for call */ @@ -544,11 +823,11 @@ protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { final MethodType type = desc.getMethodType(); - final String name = getName(); + final String name = getName(); final boolean isUnstable = request.isCallSiteUnstable(); - final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); - final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name); - final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name); + final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); + final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name); + final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name); final boolean isApplyOrCall = isCall | isApply; @@ -569,7 +848,7 @@ return new GuardedInvocation( handle, null, - (SwitchPoint)null, + (SwitchPoint) null, ClassCastException.class); } @@ -672,14 +951,14 @@ this, cf.getFlags()) : guard, - spsArray, + spsArray, exceptionGuard); } private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) { final MethodType descType = desc.getMethodType(); final int paramCount = descType.parameterCount(); - if(descType.parameterType(paramCount - 1).isArray()) { + if (descType.parameterType(paramCount - 1).isArray()) { // This is vararg invocation of apply or call. This can normally only happen when we do a recursive // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader. @@ -786,7 +1065,7 @@ inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS); } else { // If the original call site doesn't pass argArray, pass in an empty array - inv = MH.insertArguments(inv, 2, (Object)ScriptRuntime.EMPTY_ARRAY); + inv = MH.insertArguments(inv, 2, (Object) ScriptRuntime.EMPTY_ARRAY); } } @@ -851,7 +1130,7 @@ final LinkRequest request, final Object[] args) { final MethodType descType = desc.getMethodType(); final int paramCount = descType.parameterCount(); - final Object[] varArgs = (Object[])args[paramCount - 1]; + final Object[] varArgs = (Object[]) args[paramCount - 1]; // -1 'cause we're not passing the vararg array itself final int copiedArgCount = args.length - 1; final int varArgCount = varArgs.length; @@ -893,7 +1172,7 @@ // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail // with ClassCastException of NativeArray to Object[]. - if(guardType.parameterType(guardParamCount - 1).isArray()) { + if (guardType.parameterType(guardParamCount - 1).isArray()) { arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS); } else { arrayConvertingGuard = guard; @@ -903,19 +1182,20 @@ } private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) { - final MethodHandle bound; - if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) { - bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER); - } else { - bound = mh; - } - return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED); - } + final MethodHandle bound; + if (fn instanceof ScriptFunction && ((ScriptFunction) fn).needsWrappedThis()) { + bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER); + } else { + bound = mh; + } + return MH.insertArguments(bound, 1, ScriptRuntime.UNDEFINED); + } /** * Used for noSuchMethod/noSuchProperty and JSAdapter hooks. * - * These don't want a callee parameter, so bind that. Name binding is optional. + * These don't want a callee parameter, so bind that. Name binding is + * optional. */ MethodHandle getCallMethodHandle(final MethodType type, final String bindName) { return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type); @@ -939,10 +1219,11 @@ } /** - * Get the guard that checks if a {@link ScriptFunction} is equal to - * a known ScriptFunction, using reference comparison + * Get the guard that checks if a {@link ScriptFunction} is equal to a known + * ScriptFunction, using reference comparison * - * @param function The ScriptFunction to check against. This will be bound to the guard method handle + * @param function The ScriptFunction to check against. This will be bound + * to the guard method handle * * @return method handle for guard */ @@ -957,11 +1238,12 @@ } /** - * Get a guard that checks if a {@link ScriptFunction} is equal to - * a known ScriptFunction using reference comparison, and whether the type of - * the second argument (this-object) is not a JavaScript primitive type. + * Get a guard that checks if a {@link ScriptFunction} is equal to a known + * ScriptFunction using reference comparison, and whether the type of the + * second argument (this-object) is not a JavaScript primitive type. * - * @param function The ScriptFunction to check against. This will be bound to the guard method handle + * @param function The ScriptFunction to check against. This will be bound + * to the guard method handle * * @return method handle for guard */ @@ -972,12 +1254,12 @@ @SuppressWarnings("unused") private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) { - return self instanceof ScriptFunction && ((ScriptFunction)self).data == data; + return self instanceof ScriptFunction && ((ScriptFunction) self).data == data; } @SuppressWarnings("unused") private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) { - return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject; + return self instanceof ScriptFunction && ((ScriptFunction) self).data == data && arg instanceof ScriptObject; } //TODO this can probably be removed given that we have builtin switchpoints in the context @@ -990,7 +1272,7 @@ @SuppressWarnings("unused") private static Object[] addZerothElement(final Object[] args, final Object value) { // extends input array with by adding new zeroth element - final Object[] src = args == null? ScriptRuntime.EMPTY_ARRAY : args; + final Object[] src = args == null ? ScriptRuntime.EMPTY_ARRAY : args; final Object[] result = new Object[src.length + 1]; System.arraycopy(src, 0, result, 1, src.length); result[0] = value; @@ -1014,4 +1296,3 @@ return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types)); } } -
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Dec 02 23:08:29 2015 -0800 @@ -151,7 +151,7 @@ * Is this a ScriptFunction generated with strict semantics? * @return true if strict, false otherwise */ - public boolean isStrict() { + public final boolean isStrict() { return (flags & IS_STRICT) != 0; } @@ -164,11 +164,11 @@ return getName(); } - boolean isBuiltin() { + final boolean isBuiltin() { return (flags & IS_BUILTIN) != 0; } - boolean isConstructor() { + final boolean isConstructor() { return (flags & IS_CONSTRUCTOR) != 0; } @@ -179,7 +179,7 @@ * according to ECMA 10.4.3. * @return true if this argument must be an object */ - boolean needsWrappedThis() { + final boolean needsWrappedThis() { return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0; } @@ -318,7 +318,7 @@ * Used to find an apply to call version that fits this callsite. * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int) * for (Object, Object, int, int, int) or we will destroy the semantics and get - * a function that, when padded with undefineds, behaves differently + * a function that, when padded with undefined values, behaves differently * @param type actual call site type * @return apply to call that perfectly fits this callsite or null if none found */ @@ -359,31 +359,13 @@ * scope is not known, but that might cause compilation of code that will need more deoptimization passes. * @return the best function for the specified call site type. */ - CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) { - assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this) - assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function - - if (isRecompilable()) { - final CompiledFunction candidate = pickFunction(callSiteType, false); - if (candidate != null) { - return candidate; - } - return pickFunction(callSiteType, true); //try vararg last - } + abstract CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden); - CompiledFunction best = null; - for (final CompiledFunction candidate: code) { - if (!forbidden.contains(candidate) && candidate.betterThanFinal(best, callSiteType)) { - best = candidate; - } - } - - return best; + boolean isValidCallSite(final MethodType callSiteType) { + return callSiteType.parameterCount() >= 2 && // Must have at least (callee, this) + callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class); // Callee must be assignable from script function } - - abstract boolean isRecompilable(); - CompiledFunction getGeneric(final ScriptObject runtimeScope) { return getBest(getGenericType(), runtimeScope, CompiledFunction.NO_FUNCTIONS); } @@ -407,15 +389,16 @@ /** * Get the property map to use for objects allocated by this function. * + * @param prototype the prototype of the allocated object * @return the property map for allocated objects. */ - PropertyMap getAllocatorMap() { + PropertyMap getAllocatorMap(final ScriptObject prototype) { return null; } /** * This method is used to create the immutable portion of a bound function. - * See {@link ScriptFunction#makeBoundFunction(Object, Object[])} + * See {@link ScriptFunction#createBound(Object, Object[])} * * @param fn the original function being bound * @param self this reference to bind. Can be null.
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Dec 02 23:08:29 2015 -0800 @@ -64,6 +64,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.LongAdder; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -148,7 +149,7 @@ /** Method handle to retrieve prototype of this object */ public static final MethodHandle GETPROTO = findOwnMH_V("getProto", ScriptObject.class); - static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class); + static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class, boolean.class); static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class); @@ -211,7 +212,7 @@ */ public ScriptObject(final PropertyMap map) { if (Context.DEBUG) { - ScriptObject.count++; + ScriptObject.count.increment(); } this.arrayData = ArrayData.EMPTY_ARRAY; this.setMap(map == null ? PropertyMap.newMap() : map); @@ -224,7 +225,7 @@ * same combination of prototype and property map. * * @param proto the prototype object - * @param map intial {@link PropertyMap} + * @param map initial {@link PropertyMap} */ protected ScriptObject(final ScriptObject proto, final PropertyMap map) { this(map); @@ -287,9 +288,10 @@ */ public void addBoundProperties(final ScriptObject source, final Property[] properties) { PropertyMap newMap = this.getMap(); + final boolean extensible = newMap.isExtensible(); for (final Property property : properties) { - newMap = addBoundProperty(newMap, source, property); + newMap = addBoundProperty(newMap, source, property, extensible); } this.setMap(newMap); @@ -302,13 +304,18 @@ * @param propMap the property map * @param source the source object * @param property the property to be added + * @param extensible whether the current object is extensible or not * @return the new property map */ - protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property) { + protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property, final boolean extensible) { PropertyMap newMap = propMap; final String key = property.getKey(); final Property oldProp = newMap.findProperty(key); if (oldProp == null) { + if (! extensible) { + throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); + } + if (property instanceof UserAccessorProperty) { // Note: we copy accessor functions to this object which is semantically different from binding. final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source)); @@ -337,11 +344,15 @@ */ public void addBoundProperties(final Object source, final AccessorProperty[] properties) { PropertyMap newMap = this.getMap(); + final boolean extensible = newMap.isExtensible(); for (final AccessorProperty property : properties) { final String key = property.getKey(); if (newMap.findProperty(key) == null) { + if (! extensible) { + throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); + } newMap = newMap.addPropertyBind(property, source); } } @@ -699,8 +710,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); final long oldLength = getArray().length(); if (longIndex >= oldLength) { - setArray(getArray().ensure(longIndex)); - doesNotHaveEnsureDelete(longIndex, oldLength, false); + setArray(getArray().ensure(longIndex).safeDelete(oldLength, longIndex - 1, false)); } setArray(getArray().set(index, value, false)); } @@ -798,9 +808,11 @@ if (deep) { final ScriptObject myProto = getProto(); - if (myProto != null) { - return myProto.findProperty(key, deep, start); - } + final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, start); + // checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate + // shared proto invalidation up the prototype chain. It also must be invoked when prototype is null. + checkSharedProtoMap(); + return find; } return null; @@ -821,7 +833,7 @@ if (deep) { final ScriptObject myProto = getProto(); if (myProto != null) { - return myProto.hasProperty(key, deep); + return myProto.hasProperty(key, true); } } @@ -1247,11 +1259,8 @@ if (oldProto != newProto) { proto = newProto; - // Let current listeners know that the protototype has changed and set our map - final PropertyListeners listeners = getMap().getListeners(); - if (listeners != null) { - listeners.protoChanged(); - } + // Let current listeners know that the prototype has changed + getMap().protoChanged(true); // Replace our current allocator map with one that is associated with the new prototype. setMap(getMap().changeProto(newProto)); } @@ -1303,7 +1312,7 @@ } p = p.getProto(); } - setProto((ScriptObject)newProto); + setProto((ScriptObject) newProto); } else { throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); } @@ -1442,7 +1451,7 @@ * in {@link ScriptFunction} for hasInstance implementation, walks * the proto chain * - * @param instance instace to check + * @param instance instance to check * @return true if 'instance' is an instance of this object */ public boolean isInstance(final ScriptObject instance) { @@ -1849,7 +1858,7 @@ * @return GuardedInvocation to be invoked at call site. */ protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { - return notAFunction(); + return notAFunction(desc); } /** @@ -1859,14 +1868,14 @@ * @param desc the call site descriptor. * @param request the link request * - * @return GuardedInvocation to be invoed at call site. + * @return GuardedInvocation to be invoked at call site. */ protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { - return notAFunction(); + return notAFunction(desc); } - private GuardedInvocation notAFunction() { - throw typeError("not.a.function", ScriptRuntime.safeToString(this)); + private GuardedInvocation notAFunction(final CallSiteDescriptor desc) { + throw typeError("not.a.function", NashornCallSiteDescriptor.getFunctionErrorMessage(desc, this)); } /** @@ -1986,11 +1995,11 @@ final ScriptObject owner = find.getOwner(); final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class; - final SwitchPoint protoSwitchPoint; + final SwitchPoint[] protoSwitchPoints; if (mh == null) { mh = Lookup.emptyGetter(returnType); - protoSwitchPoint = getProtoSwitchPoint(name, owner); + protoSwitchPoints = getProtoSwitchPoints(name, owner); } else if (!find.isSelf()) { assert mh.type().returnType().equals(returnType) : "return type mismatch for getter " + mh.type().returnType() + " != " + returnType; @@ -1998,30 +2007,30 @@ // Add a filter that replaces the self object with the prototype owning the property. mh = addProtoFilter(mh, find.getProtoChainLength()); } - protoSwitchPoint = getProtoSwitchPoint(name, owner); + protoSwitchPoints = getProtoSwitchPoints(name, owner); } else { - protoSwitchPoint = null; + protoSwitchPoints = null; } - final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception); + final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception); return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); } private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) { Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: " + desc + " " + name + " " +isMethod); - final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod); + final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, NashornCallSiteDescriptor.isScope(desc)); final MethodHandle guard = getScriptObjectGuard(desc.getMethodType(), true); return new GuardedInvocation(invoker, guard); } @SuppressWarnings("unused") - private Object megamorphicGet(final String key, final boolean isMethod) { + private Object megamorphicGet(final String key, final boolean isMethod, final boolean isScope) { final FindProperty find = findProperty(key, true); if (find != null) { return find.getObjectValue(); } - return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); + return isMethod ? getNoSuchMethod(key, isScope, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, isScope, INVALID_PROGRAM_POINT); } // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST @@ -2102,17 +2111,32 @@ * @param owner the property owner, null if property is not defined * @return a SwitchPoint or null */ - public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) { + public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) { if (owner == this || getProto() == null) { return null; } + final List<SwitchPoint> switchPoints = new ArrayList<>(); for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { final ScriptObject parent = obj.getProto(); parent.getMap().addListener(name, obj.getMap()); + final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint(); + if (sp != null && !sp.hasBeenInvalidated()) { + switchPoints.add(sp); + } } - return getMap().getSwitchPoint(name); + switchPoints.add(getMap().getSwitchPoint(name)); + return switchPoints.toArray(new SwitchPoint[switchPoints.size()]); + } + + private void checkSharedProtoMap() { + // Check if our map has an expected shared prototype property map. If it has, make sure that + // the prototype map has not been invalidated, and that it does match the actual map of the prototype. + if (getMap().isInvalidSharedMapFor(getProto())) { + // Change our own map to one that does not assume a shared prototype map. + setMap(getMap().makeUnsharedCopy()); + } } /** @@ -2194,7 +2218,7 @@ return new GuardedInvocation( Lookup.EMPTY_SETTER, NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), - getProtoSwitchPoint(name, null), + getProtoSwitchPoints(name, null), explicitInstanceOfCheck ? null : ClassCastException.class); } @@ -2291,7 +2315,7 @@ MH.dropArguments( MH.constant( ScriptFunction.class, - func.makeBoundFunction(thiz, new Object[] { name })), + func.createBound(thiz, new Object[] { name })), 0, Object.class), NashornGuards.combineGuards( @@ -2340,7 +2364,7 @@ find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), find.getProtoChainLength(), func), - getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()), + getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()), //TODO this doesn't need a ClassCastException as guard always checks script object null); } @@ -2356,20 +2380,21 @@ /** * Invoke fall back if a property is not found. * @param name Name of property. + * @param isScope is this a scope access? * @param programPoint program point * @return Result from call. */ - protected Object invokeNoSuchProperty(final String name, final int programPoint) { + protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) { final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); + final Object func = (find != null)? find.getObjectValue() : null; Object ret = UNDEFINED; - - if (find != null) { - final Object func = find.getObjectValue(); - - if (func instanceof ScriptFunction) { - ret = ScriptRuntime.apply((ScriptFunction)func, this, name); - } + if (func instanceof ScriptFunction) { + final ScriptFunction sfunc = (ScriptFunction)func; + final Object self = isScope && sfunc.isStrict()? UNDEFINED : this; + ret = ScriptRuntime.apply(sfunc, self, name); + } else if (isScope) { + throw referenceError("not.defined", name); } if (isValid(programPoint)) { @@ -2383,21 +2408,27 @@ /** * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined. * @param name the method name + * @param isScope is this a scope access? * @return the bound function, or undefined */ - private Object getNoSuchMethod(final String name, final int programPoint) { + private Object getNoSuchMethod(final String name, final boolean isScope, final int programPoint) { final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); if (find == null) { - return invokeNoSuchProperty(name, programPoint); + return invokeNoSuchProperty(name, isScope, programPoint); } final Object value = find.getObjectValue(); if (!(value instanceof ScriptFunction)) { + if (isScope) { + throw referenceError("not.defined", name); + } return UNDEFINED; } - return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name}); + final ScriptFunction func = (ScriptFunction)value; + final Object self = isScope && func.isStrict()? UNDEFINED : this; + return func.createBound(self, new Object[] {name}); } private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) { @@ -2406,7 +2437,7 @@ } return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), - NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null), + NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null), explicitInstanceOfCheck ? null : ClassCastException.class); } @@ -2646,11 +2677,7 @@ } if (newLength > arrayLength) { - data = data.ensure(newLength - 1); - if (data.canDelete(arrayLength, newLength - 1, false)) { - data = data.delete(arrayLength, newLength - 1); - } - setArray(data); + setArray(data.ensure(newLength - 1).safeDelete(arrayLength, newLength - 1, false)); return; } @@ -2712,7 +2739,7 @@ } } - return JSType.toInt32(invokeNoSuchProperty(key, programPoint)); + return JSType.toInt32(invokeNoSuchProperty(key, false, programPoint)); } @Override @@ -2794,7 +2821,7 @@ } } - return JSType.toLong(invokeNoSuchProperty(key, programPoint)); + return JSType.toLong(invokeNoSuchProperty(key, false, programPoint)); } @Override @@ -2876,7 +2903,7 @@ } } - return JSType.toNumber(invokeNoSuchProperty(key, INVALID_PROGRAM_POINT)); + return JSType.toNumber(invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT)); } @Override @@ -2957,7 +2984,7 @@ } } - return invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); + return invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT); } @Override @@ -3071,23 +3098,12 @@ return false; } - private void doesNotHaveEnsureDelete(final long longIndex, final long oldLength, final boolean strict) { - if (longIndex > oldLength) { - ArrayData array = getArray(); - if (array.canDelete(oldLength, longIndex - 1, strict)) { - array = array.delete(oldLength, longIndex - 1); - } - setArray(array); - } - } - private void doesNotHave(final int index, final int value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } } @@ -3096,8 +3112,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } } @@ -3106,8 +3121,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } } @@ -3116,8 +3130,7 @@ final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); - setArray(getArray().set(index, value, strict)); - doesNotHaveEnsureDelete(longIndex, oldLength, strict); + setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } } @@ -3786,15 +3799,20 @@ } /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */ - private static int count; - + private static LongAdder count; + + static { + if (Context.DEBUG) { + count = new LongAdder(); + } + } /** * Get number of {@code ScriptObject} instances created. If not running in debug * mode this is always 0 * * @return number of ScriptObjects created */ - public static int getCount() { - return count; + public static long getCount() { + return count.longValue(); } }
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Wed Dec 02 23:08:29 2015 -0800 @@ -685,6 +685,33 @@ } /** + * ECMA 11.4.1 - delete operator, implementation for slow scopes + * + * This implementation of 'delete' walks the scope chain to find the scope that contains the + * property to be deleted, then invokes delete on it. + * + * @param obj top scope object + * @param property property to delete + * @param strict are we in strict mode + * + * @return true if property was successfully found and deleted + */ + public static boolean SLOW_DELETE(final Object obj, final Object property, final Object strict) { + if (obj instanceof ScriptObject) { + ScriptObject sobj = (ScriptObject) obj; + final String key = property.toString(); + while (sobj != null && sobj.isScope()) { + final FindProperty find = sobj.findProperty(key, false); + if (find != null) { + return sobj.delete(key, Boolean.TRUE.equals(strict)); + } + sobj = sobj.getProto(); + } + } + return DELETE(obj, property, strict); + } + + /** * ECMA 11.4.1 - delete operator, special case * * This is 'delete' that always fails. We have to check strict mode and throw error.
--- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; @@ -70,6 +71,9 @@ /** EXIT name - special property used by $EXEC API. */ public static final String EXIT_NAME = "$EXIT"; + /** THROW_ON_ERROR name - special property of the $EXEC function used by $EXEC API. */ + public static final String THROW_ON_ERROR_NAME = "throwOnError"; + /** Names of special properties used by $ENV API. */ public static final String ENV_NAME = "$ENV"; @@ -244,6 +248,19 @@ } } + // if we got a non-zero exit code ("failure"), then we have to decide to throw error or not + if (exit != 0) { + // get the $EXEC function object from the global object + final Object exec = global.get(EXEC_NAME); + assert exec instanceof ScriptObject : EXEC_NAME + " is not a script object!"; + + // Check if the user has set $EXEC.throwOnError property to true. If so, throw RangeError + // If that property is not set or set to false, then silently proceed with the rest. + if (JSType.toBoolean(((ScriptObject)exec).get(THROW_ON_ERROR_NAME))) { + throw rangeError("exec.returned.non.zero", ScriptRuntime.safeToString(exit)); + } + } + // Return the result from stdout. return out; }
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Wed Dec 02 23:08:29 2015 -0800 @@ -186,10 +186,7 @@ private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) { final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint); - final PropertyListeners listeners = map.getListeners(); - if (listeners != null) { - listeners.propertyAdded(sm.property); - } + map.propertyAdded(sm.property, true); return sm; } @@ -204,7 +201,7 @@ //fast type specific setter final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already - //slow setter, that calls ScriptObject.set with appropraite type and key name + //slow setter, that calls ScriptObject.set with appropriate type and key name MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)]; slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.getFlags(desc)); slowSetter = MH.insertArguments(slowSetter, 1, name);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/SharedPropertyMap.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.runtime; + +import java.lang.invoke.SwitchPoint; + +/** + * This class represents a property map that can be shared among multiple prototype objects, allowing all inheriting + * top-level objects to also share one property map. This is class is only used for prototype objects, the + * top-level objects use ordinary {@link PropertyMap}s with the {@link PropertyMap#sharedProtoMap} field + * set to the expected shared prototype map. + * + * <p>When an instance of this class is evolved because a property is added, removed, or modified in an object + * using it, the {@link #invalidateSwitchPoint()} method is invoked to signal to all callsites and inheriting + * objects that the assumption of a single shared prototype map is no longer valid. The property map resulting + * from the modification will no longer be an instance of this class.</p> + */ +public final class SharedPropertyMap extends PropertyMap { + + private SwitchPoint switchPoint; + + private static final long serialVersionUID = 2166297719721778876L; + + /** + * Create a new shared property map from the given {@code map}. + * @param map property map to copy + */ + public SharedPropertyMap(final PropertyMap map) { + super(map); + this.switchPoint = new SwitchPoint(); + } + + @Override + public void propertyAdded(final Property property, final boolean isSelf) { + if (isSelf) { + invalidateSwitchPoint(); + } + super.propertyAdded(property, isSelf); + } + + @Override + public void propertyDeleted(final Property property, final boolean isSelf) { + if (isSelf) { + invalidateSwitchPoint(); + } + super.propertyDeleted(property, isSelf); + } + + @Override + public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) { + if (isSelf) { + invalidateSwitchPoint(); + } + super.propertyModified(oldProperty, newProperty, isSelf); + } + + @Override + synchronized boolean isValidSharedProtoMap() { + return switchPoint != null; + } + + @Override + synchronized SwitchPoint getSharedProtoSwitchPoint() { + return switchPoint; + } + + /** + * Invalidate the shared prototype switch point if this is a shared prototype map. + */ + synchronized void invalidateSwitchPoint() { + if (switchPoint != null) { + assert !switchPoint.hasBeenInvalidated(); + SwitchPoint.invalidateAll(new SwitchPoint[]{ switchPoint }); + switchPoint = null; + } + } +}
--- a/src/jdk/nashorn/internal/runtime/Source.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/Source.java Wed Dec 02 23:08:29 2015 -0800 @@ -971,7 +971,7 @@ return initLogger(Context.getContextTrusted()); } - private File dumpFile(final String dir) { + private File dumpFile(final File dirFile) { final URL u = getURL(); final StringBuilder buf = new StringBuilder(); // make it unique by prefixing current date & time @@ -986,11 +986,17 @@ buf.append(getName()); } - return new File(dir, buf.toString()); + return new File(dirFile, buf.toString()); } void dump(final String dir) { - final File file = dumpFile(dir); + final File dirFile = new File(dir); + final File file = dumpFile(dirFile); + if (!dirFile.exists() && !dirFile.mkdirs()) { + debug("Skipping source dump for " + name); + return; + } + try (final FileOutputStream fos = new FileOutputStream(file)) { final PrintWriter pw = new PrintWriter(fos); pw.print(data.toString());
--- a/src/jdk/nashorn/internal/runtime/StoredScript.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/StoredScript.java Wed Dec 02 23:08:29 2015 -0800 @@ -77,7 +77,7 @@ return compilationId; } - private Map<String, Class<?>> installClasses(final Source source, final CodeInstaller<ScriptEnvironment> installer) { + private Map<String, Class<?>> installClasses(final Source source, final CodeInstaller installer) { final Map<String, Class<?>> installedClasses = new HashMap<>(); final byte[] mainClassBytes = classBytes.get(mainClassName); final Class<?> mainClass = installer.install(mainClassName, mainClassBytes); @@ -96,7 +96,7 @@ return installedClasses; } - FunctionInitializer installFunction(final RecompilableScriptFunctionData data, final CodeInstaller<ScriptEnvironment> installer) { + FunctionInitializer installFunction(final RecompilableScriptFunctionData data, final CodeInstaller installer) { final Map<String, Class<?>> installedClasses = installClasses(data.getSource(), installer); assert initializers != null; @@ -124,7 +124,7 @@ * @param installer the installer * @return main script class */ - Class<?> installScript(final Source source, final CodeInstaller<ScriptEnvironment> installer) { + Class<?> installScript(final Source source, final CodeInstaller installer) { final Map<String, Class<?>> installedClasses = installClasses(source, installer);
--- a/src/jdk/nashorn/internal/runtime/Timing.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/Timing.java Wed Dec 02 23:08:29 2015 -0800 @@ -28,12 +28,14 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Function; import java.util.function.Supplier; - import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.logging.Loggable; @@ -156,11 +158,15 @@ } final class TimeSupplier implements Supplier<String> { - private final Map<String, Long> timings; - - TimeSupplier() { - timings = new LinkedHashMap<>(); - } + private final Map<String, LongAdder> timings = new ConcurrentHashMap<>(); + private final LinkedBlockingQueue<String> orderedTimingNames = new LinkedBlockingQueue<>(); + private final Function<String, LongAdder> newTimingCreator = new Function<String, LongAdder>() { + @Override + public LongAdder apply(final String s) { + orderedTimingNames.add(s); + return new LongAdder(); + } + }; String[] getStrings() { final List<String> strs = new ArrayList<>(); @@ -184,26 +190,26 @@ int maxKeyLength = 0; int maxValueLength = 0; - for (final Map.Entry<String, Long> entry : timings.entrySet()) { + for (final Map.Entry<String, LongAdder> entry : timings.entrySet()) { maxKeyLength = Math.max(maxKeyLength, entry.getKey().length()); - maxValueLength = Math.max(maxValueLength, toMillisPrint(entry.getValue()).length()); + maxValueLength = Math.max(maxValueLength, toMillisPrint(entry.getValue().longValue()).length()); } maxKeyLength++; final StringBuilder sb = new StringBuilder(); sb.append("Accumulated compilation phase timings:\n\n"); - for (final Map.Entry<String, Long> entry : timings.entrySet()) { + for (final String timingName: orderedTimingNames) { int len; len = sb.length(); - sb.append(entry.getKey()); + sb.append(timingName); len = sb.length() - len; while (len++ < maxKeyLength) { sb.append(' '); } - final Long duration = entry.getValue(); + final long duration = timings.get(timingName).longValue(); final String strDuration = toMillisPrint(duration); len = strDuration.length(); for (int i = 0; i < maxValueLength - len; i++) { @@ -233,11 +239,7 @@ } private void accumulateTime(final String module, final long duration) { - Long accumulatedTime = timings.get(module); - if (accumulatedTime == null) { - accumulatedTime = 0L; - } - timings.put(module, accumulatedTime + duration); + timings.computeIfAbsent(module, newTimingCreator).add(duration); } } }
--- a/src/jdk/nashorn/internal/runtime/Undefined.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/Undefined.java Wed Dec 02 23:08:29 2015 -0800 @@ -96,8 +96,12 @@ switch (operator) { case "new": - case "call": - throw lookupTypeError("cant.call.undefined", desc); + case "call": { + final String name = NashornCallSiteDescriptor.getFunctionDescription(desc); + final String msg = name != null? "not.a.function" : "cant.call.undefined"; + throw typeError(msg, name); + } + case "callMethod": throw lookupTypeError("cant.read.property.of.undefined", desc); // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself @@ -125,7 +129,8 @@ } private static ECMAException lookupTypeError(final String msg, final CallSiteDescriptor desc) { - return typeError(msg, desc.getNameTokenCount() > 2 ? desc.getNameToken(2) : null); + final String name = desc.getNameToken(2); + return typeError(msg, name != null && !name.isEmpty()? name : null); } private static final MethodHandle GET_METHOD = findOwnMH("get", Object.class, Object.class);
--- a/src/jdk/nashorn/internal/runtime/WithObject.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/WithObject.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -45,7 +46,7 @@ * */ public final class WithObject extends Scope { - private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint.class); + private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint[].class); private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class); private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class); private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class); @@ -209,16 +210,18 @@ } @Override - protected Object invokeNoSuchProperty(final String name, final int programPoint) { + protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) { FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true); if (find != null) { final Object func = find.getObjectValue(); if (func instanceof ScriptFunction) { - return ScriptRuntime.apply((ScriptFunction)func, expression, name); + final ScriptFunction sfunc = (ScriptFunction)func; + final Object self = isScope && sfunc.isStrict()? UNDEFINED : expression; + return ScriptRuntime.apply(sfunc, self, name); } } - return getProto().invokeNoSuchProperty(name, programPoint); + return getProto().invokeNoSuchProperty(name, isScope, programPoint); } @Override @@ -352,18 +355,29 @@ } private static Object bindToExpression(final ScriptFunction fn, final Object receiver) { - return fn.makeBoundFunction(withFilterExpression(receiver), ScriptRuntime.EMPTY_ARRAY); + return fn.createBound(withFilterExpression(receiver), ScriptRuntime.EMPTY_ARRAY); } private MethodHandle expressionGuard(final String name, final ScriptObject owner) { final PropertyMap map = expression.getMap(); - final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner); + final SwitchPoint[] sp = expression.getProtoSwitchPoints(name, owner); return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp); } @SuppressWarnings("unused") - private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint sp) { - return ((WithObject)receiver).expression.getMap() == map && (sp == null || !sp.hasBeenInvalidated()); + private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint[] sp) { + return ((WithObject)receiver).expression.getMap() == map && !hasBeenInvalidated(sp); + } + + private static boolean hasBeenInvalidated(final SwitchPoint[] switchPoints) { + if (switchPoints != null) { + for (final SwitchPoint switchPoint : switchPoints) { + if (switchPoint.hasBeenInvalidated()) { + return true; + } + } + } + return false; } /**
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Wed Dec 02 23:08:29 2015 -0800 @@ -258,7 +258,7 @@ * Factory method for unspecified array - start as int * @return ArrayData */ - public final static ArrayData initialArray() { + public static ArrayData initialArray() { return new IntArrayData(); } @@ -278,7 +278,7 @@ * @param size size required * @return size given, always >= size */ - protected final static int alignUp(final int size) { + protected static int alignUp(final int size) { return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1); } @@ -288,7 +288,7 @@ * @param length the initial length * @return ArrayData */ - public static final ArrayData allocate(final int length) { + public static ArrayData allocate(final int length) { if (length == 0) { return new IntArrayData(); } else if (length >= SparseArrayData.MAX_DENSE_LENGTH) { @@ -304,7 +304,7 @@ * @param array the array * @return ArrayData wrapping this array */ - public static final ArrayData allocate(final Object array) { + public static ArrayData allocate(final Object array) { final Class<?> clazz = array.getClass(); if (clazz == int[].class) { @@ -324,7 +324,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final int[] array) { + public static ArrayData allocate(final int[] array) { return new IntArrayData(array, array.length); } @@ -334,7 +334,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final long[] array) { + public static ArrayData allocate(final long[] array) { return new LongArrayData(array, array.length); } @@ -344,7 +344,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final double[] array) { + public static ArrayData allocate(final double[] array) { return new NumberArrayData(array, array.length); } @@ -354,7 +354,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static final ArrayData allocate(final Object[] array) { + public static ArrayData allocate(final Object[] array) { return new ObjectArrayData(array, array.length); } @@ -364,7 +364,7 @@ * @param buf the nio ByteBuffer to wrap * @return the ArrayData */ - public static final ArrayData allocate(final ByteBuffer buf) { + public static ArrayData allocate(final ByteBuffer buf) { return new ByteBufferArrayData(buf); } @@ -374,7 +374,7 @@ * @param underlying the underlying ArrayData to wrap in the freeze filter * @return the frozen ArrayData */ - public static final ArrayData freeze(final ArrayData underlying) { + public static ArrayData freeze(final ArrayData underlying) { return new FrozenArrayFilter(underlying); } @@ -384,7 +384,7 @@ * @param underlying the underlying ArrayData to wrap in the seal filter * @return the sealed ArrayData */ - public static final ArrayData seal(final ArrayData underlying) { + public static ArrayData seal(final ArrayData underlying) { return new SealedArrayFilter(underlying); } @@ -394,7 +394,7 @@ * @param underlying the underlying ArrayData to wrap in the non extensible filter * @return new array data, filtered */ - public static final ArrayData preventExtension(final ArrayData underlying) { + public static ArrayData preventExtension(final ArrayData underlying) { return new NonExtensibleArrayFilter(underlying); } @@ -404,7 +404,7 @@ * @param underlying the underlying ArrayDAta to wrap in the non extensible filter * @return new array data, filtered */ - public static final ArrayData setIsLengthNotWritable(final ArrayData underlying) { + public static ArrayData setIsLengthNotWritable(final ArrayData underlying) { return new LengthNotWritableFilter(underlying); } @@ -676,19 +676,34 @@ } /** - * Returns if element at specific index range can be deleted or not. + * Returns if element at specific index can be deleted or not. * - * @param fromIndex the start index - * @param toIndex the end index + * @param longIndex the index * @param strict are we in strict mode * * @return true if range can be deleted */ - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { + public boolean canDelete(final long longIndex, final boolean strict) { return true; } /** + * Delete a range from the array if {@code fromIndex} is less than or equal to {@code toIndex} + * and the array supports deletion. + * + * @param fromIndex the start index (inclusive) + * @param toIndex the end index (inclusive) + * @param strict are we in strict mode + * @return an array with the range deleted, or this array if no deletion took place + */ + public final ArrayData safeDelete(final long fromIndex, final long toIndex, final boolean strict) { + if (fromIndex <= toIndex && canDelete(fromIndex, strict)) { + return delete(fromIndex, toIndex); + } + return this; + } + + /** * Returns property descriptor for element at a given index * * @param global the global object
--- a/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java Wed Dec 02 23:08:29 2015 -0800 @@ -164,7 +164,7 @@ } @Override - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { + public boolean canDelete(final long longIndex, final boolean strict) { return false; }
--- a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Wed Dec 02 23:08:29 2015 -0800 @@ -191,7 +191,7 @@ /** * Return element setter for a {@link ContinuousArrayData} - * @param clazz clazz for exact type guard + * @param clazz class for exact type guard * @param setHas set has guard * @param elementType element type * @return method handle for element setter
--- a/src/jdk/nashorn/internal/runtime/arrays/InvalidArrayIndexException.java Tue Dec 01 22:55:46 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +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.runtime.arrays; - -/** - * Mechanism for communicating that something isn't a plain - * numeric integer array index. This enables things like - * array getters for the fast case in a try, basically - * just consisting of an "array[index]" access without - * any checks of boundary conditions that rarely happen - */ -@SuppressWarnings("serial") -class InvalidArrayIndexException extends Exception { - - private final Object index; - - InvalidArrayIndexException(final Object index) { - super(index == null ? "null" : index.toString()); - this.index = index; - } - - InvalidArrayIndexException(final int index) { - this(Integer.valueOf(index)); - } - - InvalidArrayIndexException(final long index) { - this(Long.valueOf(index)); - } - - InvalidArrayIndexException(final double index) { - this(Double.valueOf(index)); - } - - @Override - public String toString() { - return index.toString(); - } - - Object getIndex() { - return index; - } - -}
--- a/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java Wed Dec 02 23:08:29 2015 -0800 @@ -50,15 +50,15 @@ @Override public boolean canDelete(final int index, final boolean strict) { - if (strict) { - throw typeError("cant.delete.property", Integer.toString(index), "sealed array"); - } - return false; + return canDelete(ArrayIndex.toLongIndex(index), strict); } @Override - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { - return canDelete((int) fromIndex, strict); + public boolean canDelete(final long longIndex, final boolean strict) { + if (strict) { + throw typeError("cant.delete.property", Long.toString(longIndex), "sealed array"); + } + return false; } @Override
--- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Wed Dec 02 23:08:29 2015 -0800 @@ -36,7 +36,7 @@ * Handle arrays where the index is very large. */ class SparseArrayData extends ArrayData { - static final int MAX_DENSE_LENGTH = 8 * 1024 * 1024; + static final int MAX_DENSE_LENGTH = 1024 * 1024; /** Underlying array. */ private ArrayData underlying; @@ -166,8 +166,9 @@ @Override public ArrayData set(final int index, final Object value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); @@ -181,8 +182,9 @@ @Override public ArrayData set(final int index, final int value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); @@ -195,8 +197,9 @@ @Override public ArrayData set(final int index, final long value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); @@ -209,8 +212,9 @@ @Override public ArrayData set(final int index, final double value, final boolean strict) { if (index >= 0 && index < maxDenseLength) { + final long oldLength = underlying.length(); ensure(index); - underlying = underlying.set(index, value, strict); + underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict); setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index);
--- a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Wed Dec 02 23:08:29 2015 -0800 @@ -83,7 +83,7 @@ } @Override - public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { + public boolean canDelete(final long longIndex, final boolean strict) { return false; }
--- a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java Wed Dec 02 23:08:29 2015 -0800 @@ -34,7 +34,7 @@ * This filter handles the presence of undefined array elements. */ final class UndefinedArrayFilter extends ArrayFilter { - /** Bit vector tracking undefines. */ + /** Bit vector tracking undefined slots. */ private final BitVector undefined; UndefinedArrayFilter(final ArrayData underlying) {
--- a/src/jdk/nashorn/internal/runtime/linker/AdaptationException.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/AdaptationException.java Wed Dec 02 23:08:29 2015 -0800 @@ -30,6 +30,7 @@ private final AdaptationResult adaptationResult; AdaptationException(final AdaptationResult.Outcome outcome, final String classList) { + super(null, null, false, false); this.adaptationResult = new AdaptationResult(outcome, classList); }
--- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Wed Dec 02 23:08:29 2015 -0800 @@ -50,7 +50,6 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.lookup.MethodHandleFactory; import jdk.nashorn.internal.lookup.MethodHandleFunctionality; -import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.OptimisticReturnFilters; @@ -70,7 +69,7 @@ private static final MethodHandle VOID_TO_OBJECT = MH.constant(Object.class, ScriptRuntime.UNDEFINED); /** - * The default dynalink relink threshold for megamorphisism is 8. In the case + * The default dynalink relink threshold for megamorphism is 8. In the case * of object fields only, it is fine. However, with dual fields, in order to get * performance on benchmarks with a lot of object instantiation and then field * reassignment, it can take slightly more relinks to become stable with type @@ -190,7 +189,7 @@ * @return true if the obj is an instance of @FunctionalInterface interface */ public static boolean isFunctionalInterfaceObject(final Object obj) { - return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethod(obj.getClass()) != null); + return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethodName(obj.getClass()) != null); } /** @@ -214,7 +213,7 @@ * @param type method type * @param programPoint program point to bind to callsite * - * @return callsite for a math instrinic node + * @return callsite for a math intrinsic node */ public static CallSite mathBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int programPoint) { final MethodHandle mh; @@ -397,8 +396,8 @@ * @throws ECMAException with {@code TypeError} if the object is not a callable. */ public static Object bindCallable(final Object callable, final Object boundThis, final Object[] boundArgs) { - if (callable instanceof ScriptFunctionImpl) { - return ((ScriptFunctionImpl)callable).makeBoundFunction(boundThis, boundArgs); + if (callable instanceof ScriptFunction) { + return ((ScriptFunction)callable).createBound(boundThis, boundArgs); } else if (callable instanceof BoundCallable) { return ((BoundCallable)callable).bind(boundArgs); } else if (isCallable(callable)) {
--- a/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Wed Dec 02 23:08:29 2015 -0800 @@ -99,9 +99,10 @@ return null; } - final GuardedInvocation inv; + GuardedInvocation inv; if (jsObjectClass.isInstance(self)) { inv = lookup(desc, request, linkerServices); + inv = inv.replaceMethods(linkerServices.filterInternalObjects(inv.getInvocation()), inv.getGuard()); } else { throw new AssertionError(); // Should never reach here. }
--- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Wed Dec 02 23:08:29 2015 -0800 @@ -77,9 +77,10 @@ return null; } - final GuardedInvocation inv; + GuardedInvocation inv; if (self instanceof JSObject) { inv = lookup(desc, request, linkerServices); + inv = inv.replaceMethods(linkerServices.filterInternalObjects(inv.getInvocation()), inv.getGuard()); } else if (self instanceof Map || self instanceof Bindings) { // guard to make sure the Map or Bindings does not turn into JSObject later! final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Wed Dec 02 23:08:29 2015 -0800 @@ -203,6 +203,8 @@ // This is the superclass for our generated adapter. private final Class<?> superClass; + // Interfaces implemented by our generated adapter. + private final List<Class<?>> interfaces; // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the // Nashorn classes. @@ -254,6 +256,7 @@ assert interfaces != null; this.superClass = superClass; + this.interfaces = interfaces; this.classOverride = classOverride; this.commonLoader = commonLoader; cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { @@ -571,7 +574,7 @@ mv.visitVarInsn(ALOAD, 0); if (fromFunction && !mi.getName().equals(samName)) { // Constructors initializing from a ScriptFunction only initialize methods with the SAM name. - // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This + // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overridden too. This // is a deliberate design choice. All other method handles are initialized to null. mv.visitInsn(ACONST_NULL); } else { @@ -1031,6 +1034,24 @@ endMethod(mv); } + // find the appropriate super type to use for invokespecial on the given interface + private Class<?> findInvokespecialOwnerFor(final Class<?> cl) { + assert Modifier.isInterface(cl.getModifiers()) : cl + " is not an interface"; + + if (cl.isAssignableFrom(superClass)) { + return superClass; + } + + for (final Class<?> iface : interfaces) { + if (cl.isAssignableFrom(iface)) { + return iface; + } + } + + // we better that interface that extends the given interface! + throw new AssertionError("can't find the class/interface that extends " + cl); + } + private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) { mv.visitVarInsn(ALOAD, 0); int nextParam = 1; @@ -1042,7 +1063,9 @@ // default method - non-abstract, interface method if (Modifier.isInterface(owner.getModifiers())) { - mv.invokespecial(Type.getInternalName(owner), name, methodDesc, false); + // we should call default method on the immediate "super" type - not on (possibly) + // the indirectly inherited interface class! + mv.invokespecial(Type.getInternalName(findInvokespecialOwnerFor(owner)), name, methodDesc, false); } else { mv.invokespecial(superClassName, name, methodDesc, false); }
--- a/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Wed Dec 02 23:08:29 2015 -0800 @@ -43,6 +43,7 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; import jdk.internal.dynalink.ChainedCallSite; import jdk.internal.dynalink.DynamicLinker; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -70,7 +71,7 @@ LinkerCallSite(final NashornCallSiteDescriptor descriptor) { super(descriptor); if (Context.DEBUG) { - LinkerCallSite.count++; + LinkerCallSite.count.increment(); } } @@ -173,7 +174,7 @@ * @return self reference */ public static Object increaseMissCount(final String desc, final Object self) { - ++missCount; + missCount.increment(); if (r.nextInt(100) < missSamplingPercentage) { final AtomicInteger i = missCounts.get(desc); if (i == null) { @@ -500,7 +501,7 @@ * @param desc callsite descriptor string * @param args arguments to function * - * @throws Throwable if invocation failes or throws exception/error + * @throws Throwable if invocation fails or throws exception/error */ @SuppressWarnings("unused") public void traceMiss(final String desc, final Object... args) throws Throwable { @@ -509,12 +510,19 @@ } // counters updated in debug mode - private static int count; + private static LongAdder count; private static final HashMap<String, AtomicInteger> missCounts = new HashMap<>(); - private static int missCount; + private static LongAdder missCount; private static final Random r = new Random(); private static final int missSamplingPercentage = Options.getIntProperty("nashorn.tcs.miss.samplePercent", 1); + static { + if (Context.DEBUG) { + count = new LongAdder(); + missCount = new LongAdder(); + } + } + @Override protected int getMaxChainLength() { return 8; @@ -524,16 +532,16 @@ * Get the callsite count * @return the count */ - public static int getCount() { - return count; + public static long getCount() { + return count.longValue(); } /** * Get the callsite miss count * @return the missCount */ - public static int getMissCount() { - return missCount; + public static long getMissCount() { + return missCount.longValue(); } /**
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,7 +26,6 @@ package jdk.nashorn.internal.runtime.linker; import static jdk.nashorn.internal.lookup.Lookup.MH; -import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -42,13 +41,11 @@ import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.MethodHandleTransformer; import jdk.internal.dynalink.support.DefaultInternalObjectFilter; -import jdk.internal.dynalink.support.Guards; import jdk.internal.dynalink.support.Lookup; import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.options.Options; /** @@ -79,10 +76,10 @@ } // cache of @FunctionalInterface method of implementor classes - private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() { + private static final ClassValue<String> FUNCTIONAL_IFACE_METHOD_NAME = new ClassValue<String>() { @Override - protected Method computeValue(final Class<?> type) { - return findFunctionalInterfaceMethod(type); + protected String computeValue(final Class<?> type) { + return findFunctionalInterfaceMethodName(type); } }; @@ -107,19 +104,21 @@ // annotated interface. This way Java method, constructor references or // implementations of java.util.function.* interfaces can be called as though // those are script functions. - final Method m = getFunctionalInterfaceMethod(self.getClass()); - if (m != null) { + final String name = getFunctionalInterfaceMethodName(self.getClass()); + if (name != null) { final MethodType callType = desc.getMethodType(); - // 'callee' and 'thiz' passed from script + actual arguments - if (callType.parameterCount() != m.getParameterCount() + 2) { - throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); - } - return new GuardedInvocation( - // drop 'thiz' passed from the script. - MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1, - callType.parameterType(1)), Guards.getInstanceOfGuard( - m.getDeclaringClass())).asTypeSafeReturn( - new NashornBeansLinkerServices(linkerServices), callType); + // drop callee (Undefined ScriptFunction) and change the request to be dyn:callMethod:<name> + final NashornCallSiteDescriptor newDesc = NashornCallSiteDescriptor.get(desc.getLookup(), + "dyn:callMethod:" + name, desc.getMethodType().dropParameterTypes(1, 2), + NashornCallSiteDescriptor.getFlags(desc)); + final GuardedInvocation gi = getGuardedInvocation(beansLinker, + linkRequest.replaceArguments(newDesc, linkRequest.getArguments()), + new NashornBeansLinkerServices(linkerServices)); + + // drop 'thiz' passed from the script. + return gi.replaceMethods( + MH.dropArguments(linkerServices.filterInternalObjects(gi.getInvocation()), 1, callType.parameterType(1)), + gi.getGuard()); } } return getGuardedInvocation(beansLinker, linkRequest, linkerServices); @@ -163,13 +162,13 @@ return arg instanceof ConsString ? arg.toString() : arg; } - private static Method findFunctionalInterfaceMethod(final Class<?> clazz) { + private static String findFunctionalInterfaceMethodName(final Class<?> clazz) { if (clazz == null) { return null; } for (final Class<?> iface : clazz.getInterfaces()) { - // check accessiblity up-front + // check accessibility up-front if (! Context.isAccessibleClass(iface)) { continue; } @@ -179,20 +178,20 @@ // return the first abstract method for (final Method m : iface.getMethods()) { if (Modifier.isAbstract(m.getModifiers())) { - return m; + return m.getName(); } } } } // did not find here, try super class - return findFunctionalInterfaceMethod(clazz.getSuperclass()); + return findFunctionalInterfaceMethodName(clazz.getSuperclass()); } // Returns @FunctionalInterface annotated interface's single abstract - // method. If not found, returns null. - static Method getFunctionalInterfaceMethod(final Class<?> clazz) { - return FUNCTIONAL_IFACE_METHOD.get(clazz); + // method name. If not found, returns null. + static String getFunctionalInterfaceMethodName(final Class<?> clazz) { + return FUNCTIONAL_IFACE_METHOD_NAME.get(clazz); } static MethodHandleTransformer createHiddenObjectFilter() {
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Wed Dec 02 23:08:29 2015 -0800 @@ -27,6 +27,8 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; +import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED; +import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import java.lang.invoke.MethodHandle; @@ -92,7 +94,7 @@ if(BeansLinker.isDynamicMethod(self)) { throw typeError("method.not.constructor", ScriptRuntime.safeToString(self)); } - throw typeError("not.a.function", ScriptRuntime.safeToString(self)); + throw typeError("not.a.function", desc.getFunctionErrorMessage(self)); case "call": if(BeansLinker.isDynamicConstructor(self)) { throw typeError("constructor.requires.new", ScriptRuntime.safeToString(self)); @@ -100,10 +102,12 @@ if(BeansLinker.isDynamicMethod(self)) { throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); } - throw typeError("not.a.function", ScriptRuntime.safeToString(self)); + throw typeError("not.a.function", desc.getFunctionErrorMessage(self)); case "callMethod": + throw typeError("no.such.function", getArgument(linkRequest), ScriptRuntime.safeToString(self)); case "getMethod": - throw typeError("no.such.function", getArgument(linkRequest), ScriptRuntime.safeToString(self)); + // evaluate to undefined, later on Undefined will take care of throwing TypeError + return getInvocation(MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class), self, linkerServices, desc); case "getProp": case "getElem": if(NashornCallSiteDescriptor.isOptimistic(desc)) {
--- a/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed Dec 02 23:08:29 2015 -0800 @@ -34,6 +34,7 @@ import jdk.internal.dynalink.support.AbstractCallSiteDescriptor; import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.nashorn.internal.ir.debug.NashornTextifier; +import jdk.nashorn.internal.runtime.ScriptRuntime; /** * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that @@ -150,7 +151,7 @@ public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name, final MethodType methodType, final int flags) { final String[] tokenizedName = CallSiteDescriptorFactory.tokenizeName(name); - assert tokenizedName.length == 2 || tokenizedName.length == 3; + assert tokenizedName.length >= 2; assert "dyn".equals(tokenizedName[0]); assert tokenizedName[1] != null; // TODO: see if we can move mangling/unmangling into Dynalink @@ -248,6 +249,54 @@ } /** + * If this is a dyn:call or dyn:new, this returns function description from callsite. + * Caller has to make sure this is a dyn:call or dyn:new call site. + * + * @return function description if available (or null) + */ + public String getFunctionDescription() { + assert getFirstOperator().equals("call") || getFirstOperator().equals("new"); + return getNameTokenCount() > 2? getNameToken(2) : null; + } + + /** + * If this is a dyn:call or dyn:new, this returns function description from callsite. + * Caller has to make sure this is a dyn:call or dyn:new call site. + * + * @param desc call site descriptor + * @return function description if available (or null) + */ + public static String getFunctionDescription(final CallSiteDescriptor desc) { + return desc instanceof NashornCallSiteDescriptor ? + ((NashornCallSiteDescriptor)desc).getFunctionDescription() : null; + } + + + /** + * Returns the error message to be used when dyn:call or dyn:new is used on a non-function. + * + * @param obj object on which dyn:call or dyn:new is used + * @return error message + */ + public String getFunctionErrorMessage(final Object obj) { + final String funcDesc = getFunctionDescription(); + return funcDesc != null? funcDesc : ScriptRuntime.safeToString(obj); + } + + /** + * Returns the error message to be used when dyn:call or dyn:new is used on a non-function. + * + * @param desc call site descriptor + * @param obj object on which dyn:call or dyn:new is used + * @return error message + */ + public static String getFunctionErrorMessage(final CallSiteDescriptor desc, final Object obj) { + return desc instanceof NashornCallSiteDescriptor ? + ((NashornCallSiteDescriptor)desc).getFunctionErrorMessage(obj) : + ScriptRuntime.safeToString(obj); + } + + /** * Returns the Nashorn-specific flags for this call site descriptor. * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code @@ -274,7 +323,7 @@ * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code * generated outside of Nashorn. * @param flag the tested flag - * @return true if the flag is set, false otherwise (it will be false if the decriptor is not a Nashorn call site + * @return true if the flag is set, false otherwise (it will be false if the descriptor is not a Nashorn call site * descriptor). */ private static boolean isFlag(final CallSiteDescriptor desc, final int flag) {
--- a/src/jdk/nashorn/internal/runtime/logging/DebugLogger.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/logging/DebugLogger.java Wed Dec 02 23:08:29 2015 -0800 @@ -99,10 +99,10 @@ final StringBuilder sb = new StringBuilder(); sb.append('[') - .append(record.getLoggerName()) - .append("] ") - .append(record.getMessage()) - .append('\n'); + .append(record.getLoggerName()) + .append("] ") + .append(record.getMessage()) + .append('\n'); return sb.toString(); } @@ -194,7 +194,7 @@ */ public void indent(final int pos) { if (isEnabled) { - indent += pos * INDENT_SPACE; + indent += pos * INDENT_SPACE; } } @@ -227,57 +227,14 @@ } /** - * Check if the logger is above the level of detail given + * Check if the event of given level will be logged. * @see java.util.logging.Level * - * The higher the level, the more severe the warning - * - * @param level logging level - * @return true if level is above the given one - */ - public boolean levelCoarserThan(final Level level) { - return getLevel().intValue() > level.intValue(); - } - - /** - * Check if the logger is above or equal to the level - * of detail given - * @see java.util.logging.Level - * - * The higher the level, the more severe the warning - * * @param level logging level - * @return true if level is above the given one - */ - public boolean levelCoarserThanOrEqual(final Level level) { - return getLevel().intValue() >= level.intValue(); - } - - /** - * Check if the logger is below the level of detail given - * @see java.util.logging.Level - * - * The higher the level, the more severe the warning - * - * @param level logging level - * @return true if level is above the given one + * @return true if event of given level will be logged. */ - public boolean levelFinerThan(final Level level) { - return getLevel().intValue() < level.intValue(); - } - - /** - * Check if the logger is below or equal to the level - * of detail given - * @see java.util.logging.Level - * - * The higher the level, the more severe the warning - * - * @param level logging level - * @return true if level is above the given one - */ - public boolean levelFinerThanOrEqual(final Level level) { - return getLevel().intValue() <= level.intValue(); + public boolean isLoggable(final Level level) { + return logger.isLoggable(level); } /** @@ -566,7 +523,7 @@ * @param str string to log */ public void log(final Level level, final String str) { - if (isEnabled && !isQuiet) { + if (isEnabled && !isQuiet && logger.isLoggable(level)) { final StringBuilder sb = new StringBuilder(); for (int i = 0 ; i < indent ; i++) { sb.append(' '); @@ -584,7 +541,7 @@ * @param objs objects for which to invoke toString and concatenate to log */ public void log(final Level level, final Object... objs) { - if (isEnabled && !isQuiet) { + if (isEnabled && !isQuiet && logger.isLoggable(level)) { final StringBuilder sb = new StringBuilder(); for (final Object obj : objs) { sb.append(obj);
--- a/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Wed Dec 02 23:08:29 2015 -0800 @@ -163,7 +163,7 @@ /** * Does this option automatically enable another option, i.e. a dependency. - * @return the dependecy or null if non exists + * @return the dependency or null if none exists */ public String getDependency() { return this.dependency; @@ -304,8 +304,8 @@ } } - boolean matches(final String key0) { - return key0.equals(this.shortName) || key0.equals(this.name); + boolean nameMatches(final String aName) { + return aName.equals(this.shortName) || aName.equals(this.name); } private static final int LINE_BREAK = 64;
--- a/src/jdk/nashorn/internal/runtime/options/Options.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/options/Options.java Wed Dec 02 23:08:29 2015 -0800 @@ -519,9 +519,25 @@ } } - private static OptionTemplate getOptionTemplate(final String key) { + /** + * Retrieves an option template identified by key. + * @param shortKey the short (that is without the e.g. "nashorn.option." part) key + * @return the option template identified by the key + * @throws IllegalArgumentException if the key doesn't specify an existing template + */ + public OptionTemplate getOptionTemplateByKey(final String shortKey) { + final String fullKey = key(shortKey); + for(final OptionTemplate t: validOptions) { + if(t.getKey().equals(fullKey)) { + return t; + } + } + throw new IllegalArgumentException(shortKey); + } + + private static OptionTemplate getOptionTemplateByName(final String name) { for (final OptionTemplate t : Options.validOptions) { - if (t.matches(key)) { + if (t.nameMatches(name)) { return t; } } @@ -681,7 +697,7 @@ } final String token = st.nextToken(); - this.template = Options.getOptionTemplate(token); + this.template = getOptionTemplateByName(token); if (this.template == null) { throw new IllegalArgumentException(argument); }
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/Config.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Config.java Wed Dec 02 23:08:29 2015 -0800 @@ -65,7 +65,7 @@ final boolean DONT_OPTIMIZE = false; - final boolean USE_STRING_TEMPLATES = true; // use embeded string templates in Regex object as byte arrays instead of compiling them into int bytecode array + final boolean USE_STRING_TEMPLATES = true; // use embedded string templates in Regex object as byte arrays instead of compiling them into int bytecode array final boolean NON_UNICODE_SDW = true;
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/exception/JOniException.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/regexp/joni/exception/JOniException.java Wed Dec 02 23:08:29 2015 -0800 @@ -24,6 +24,6 @@ private static final long serialVersionUID = -6027192180014164667L; public JOniException(final String message) { - super(message); + super(message, null, false, false); } }
--- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties Wed Dec 02 23:08:29 2015 -0800 @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2015, 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 @@ -31,6 +31,7 @@ lexer.error.json.invalid.number=Invalid JSON number format lexer.error.invalid.escape.char=Invalid escape character lexer.error.illegal.identifier.character=Illegal character in identifier +lexer.error.here.non.matching.delimiter=Quoted here string end marker must have matching delimiters parser.error.illegal.continue.stmt=Illegal continue statement parser.error.illegal.break.stmt=Illegal break statement @@ -78,6 +79,7 @@ type.error.not.a.regexp={0} is not a RegExp type.error.not.a.string={0} is not a String type.error.not.a.function={0} is not a function +type.error.not.a.function.value={0}, which has value {1}, is not a function type.error.not.a.constructor={0} is not a constructor function type.error.not.a.file={0} is not a File type.error.not.a.numeric.array={0} is not a numeric array @@ -148,6 +150,7 @@ type.error.method.not.constructor=Java method {0} cannot be used as a constructor. type.error.env.not.object=$ENV must be an Object. type.error.unsupported.java.to.type=Unsupported Java.to target type {0}. +type.error.java.array.conversion.failed=Java.to conversion to array type {0} failed type.error.constructor.requires.new=Constructor {0} requires "new". type.error.new.on.nonpublic.javatype=new cannot be used with non-public java type {0}. @@ -161,6 +164,7 @@ range.error.invalid.date=Invalid Date range.error.too.many.errors=Script contains too many errors: {0} errors range.error.concat.string.too.big=Concatenated String is too big +range.error.exec.returned.non.zero=$EXEC returned non-zero exit code: {0} reference.error.not.defined="{0}" is not defined reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side of assignment @@ -171,7 +175,9 @@ syntax.error.unprotected.switch.declaration=Unsupported {0} declaration in unprotected switch statement io.error.cant.write=cannot write "{0}" + config.error.no.dest=no destination directory supplied +config.error.eagerCompilationConflictsWithOptimisticTypes={0}=false (eager compilation) is not compatible with {1}=true. uri.error.bad.uri=Bad URI "{0}" near offset {1} list.adapter.null.global=Attempted to create the adapter from outside a JavaScript execution context.
--- a/src/jdk/nashorn/internal/runtime/resources/parser.js Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/internal/runtime/resources/parser.js Wed Dec 02 23:08:29 2015 -0800 @@ -55,7 +55,7 @@ // do not start with '/'. If regexp, then eval it to make RegExp object return value.startsWith('/')? eval(value) : value.substring(1); } else { - // anythin else is returned "as is"" + // anything else is returned "as is" return value; } });
--- a/src/jdk/nashorn/tools/Shell.java Tue Dec 01 22:55:46 2015 -0800 +++ b/src/jdk/nashorn/tools/Shell.java Wed Dec 02 23:08:29 2015 -0800 @@ -54,7 +54,6 @@ import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptRuntime; -import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.options.Options; /** @@ -255,12 +254,9 @@ return COMPILATION_ERROR; } - new Compiler( + Compiler.forNoInstallerCompilation( context, - env, - null, //null - pass no code installer - this is compile only functionNode.getSource(), - context.getErrorManager(), env._strict | functionNode.isStrict()). compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL);
--- a/test/script/basic/JDK-8026016.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8026016.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -1,182 +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 _ +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +no such property _ +TypeError: o._ is not a function +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: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +TypeError: o._ is not a function +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 _
--- a/test/script/basic/JDK-8043232.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8043232.js Wed Dec 02 23:08:29 2015 -0800 @@ -29,14 +29,14 @@ */ // call explicit constructor -print(new (java.awt["Color(int,int,int)"])(255,0,255)); +print(new (java.lang["String(char[],int,int)"])(['a','b', 'c', 'd'], 1, 3)); // print the constructor itself -print(java.awt["Color(int,int,int)"]); +print(java.lang["String(char[],int,int)"]); // store constructor to call later -var Color = java.awt["Color(int,int,int)"]; +var Color = java.lang["String(char[],int,int)"]; // call stored constructor -print(new Color(33, 233, 2)) +print(new Color(['r','r', 'e', 'd'], 1, 3)) // check if default constructor works var obj = new (java.lang["Object()"])();
--- a/test/script/basic/JDK-8043232.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8043232.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -1,14 +1,28 @@ -java.awt.Color[r=255,g=0,b=255] -[jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] -java.awt.Color[r=33,g=233,b=2] +bcd +[jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] +red TypeError: No such Java class: java.lang.NonExistent TypeError: No such Java constructor: Object(String) TypeError: Java constructor signature invalid: Object()xxxxx TypeError: Java constructor signature invalid: Object( TypeError: Java constructor signature invalid: Object) -TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.lang.System.getProperty] cannot be used as a constructor. -TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] cannot be used as a constructor. -TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] requires "new". +TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod + String java.lang.System.getProperty(String,String) + String java.lang.System.getProperty(String) +] cannot be used as a constructor. +TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod + void java.io.PrintStream.println() + void java.io.PrintStream.println(boolean) + void java.io.PrintStream.println(char) + void java.io.PrintStream.println(char[]) + void java.io.PrintStream.println(double) + void java.io.PrintStream.println(float) + void java.io.PrintStream.println(int) + void java.io.PrintStream.println(long) + void java.io.PrintStream.println(Object) + void java.io.PrintStream.println(String) +] cannot be used as a constructor. +TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new". TypeError: No such Java constructor: Runnable() TypeError: No such Java constructor: Runnable(int) java.lang.InstantiationException: java.io.InputStream
--- a/test/script/basic/JDK-8044750.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8044750.js Wed Dec 02 23:08:29 2015 -0800 @@ -25,6 +25,8 @@ * JDK-8044750: megamorphic getter for scope objects does not call __noSuchProperty__ hook * * @test + * @fork + * @option -Dnashorn.unstable.relink.threshold=16 * @run */ @@ -40,7 +42,9 @@ } } -for (var i = 0; i < 20; i++) { +var LIMIT = 20; // should be more than megamorphic threshold set via @option + +for (var i = 0; i < LIMIT; i++) { var obj = {}; obj.foo = i; obj[i] = i; @@ -51,3 +55,30 @@ // callsite inside func should see __noSuchProperty__ // hook on global scope object. func({}); + +function checkFoo() { + with({}) { + try { + foo; + return true; + } catch (e) { + return false; + } + } +} + +var oldNoSuchProperty = this.__noSuchProperty__; +delete this.__noSuchProperty__; + +// keep deleting/restorting __noSuchProperty__ alternatively +// to make "foo" access in checkFoo function megamorphic! + +for (var i = 0; i < LIMIT; i++) { + // no __noSuchProperty__ and 'with' scope object has no 'foo' + delete __noSuchProperty__; + Assert.assertFalse(checkFoo(), "Expected false in iteration " + i); + + // __noSuchProperty__ is exists but 'with' scope object has no 'foo' + this.__noSuchProperty__ = oldNoSuchProperty; + Assert.assertTrue(checkFoo(), "Expected true in iteration " + i); +}
--- a/test/script/basic/JDK-8049086.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8049086.js Wed Dec 02 23:08:29 2015 -0800 @@ -58,7 +58,7 @@ // (a) Java methods (b) Java classes (as these respond to new) // (c) FunctionalInterface objects (d) JSObjects that are 'functions' -print("java.awt.Color is java function? " + Java.isJavaFunction(java.awt.Color)); +print("java.lang.String is java function? " + Java.isJavaFunction(java.lang.String)); print("java.lang.Runnable instance is java function? " + Java.isJavaFunction(new java.lang.Runnable(function() {}))); print("eval is java function? " + Java.isJavaFunction(eval));
--- a/test/script/basic/JDK-8049086.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8049086.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -13,7 +13,7 @@ Object is script object? true {} is script object? true /foo/ is script object? true -java.awt.Color is java function? true +java.lang.String is java function? true java.lang.Runnable instance is java function? true eval is java function? false println is java function? true
--- a/test/script/basic/JDK-8049242.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8049242.js Wed Dec 02 23:08:29 2015 -0800 @@ -29,14 +29,14 @@ */ // call explicit constructor -print(new (Java.type("java.awt.Color")["(int,int,int)"])(255,0,255)); +print(new (Java.type("java.lang.String")["(char[],int,int)"])(['a', 'b', 'c'],0, 3)); // print the constructor itself -print(Java.type("java.awt.Color")["(int,int,int)"]); +print(Java.type("java.lang.String")["(char[],int,int)"]); // store constructor to call later -var Color = Java.type("java.awt.Color")["(int,int,int)"]; +var Color = Java.type("java.lang.String")["(char[],int,int)"]; // call stored constructor -print(new Color(33, 233, 2)) +print(new Color(['j', 'a', 'v', 'a'], 1, 3)) // check if default constructor works var obj = new (Java.type("java.lang.Object")["()"])();
--- a/test/script/basic/JDK-8049242.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8049242.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -1,10 +1,10 @@ -java.awt.Color[r=255,g=0,b=255] -[jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] -java.awt.Color[r=33,g=233,b=2] +abc +[jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] +ava TypeError: null is not a function TypeError: null is not a function TypeError: null is not a function -TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] requires "new". +TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new". TypeError: null is not a function TypeError: null is not a function java.lang.InstantiationException: java.io.InputStream
--- a/test/script/basic/JDK-8053905.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8053905.js Wed Dec 02 23:08:29 2015 -0800 @@ -28,6 +28,7 @@ * @runif external.octane * @fork * @option -Dnashorn.compiler.splitter.threshold=1000 + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @option -scripting * @option --lazy-compilation=false */
--- a/test/script/basic/JDK-8058561.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8058561.js Wed Dec 02 23:08:29 2015 -0800 @@ -26,7 +26,9 @@ * * @test * @run + * @fork * @option --lazy-compilation=false + * @option -Dnashorn.options.allowEagerCompilationSilentOverride */ // Just attempting to compile this caused the NPE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8068901.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 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-8068901: Surprising behavior with more than one functional interface on a class + * + * @test + * @run + */ + +var Consumer = java.util.function.Consumer; +var JFunction = java.util.function.Function; + +var fc = new (Java.extend(JFunction, Consumer))({ + apply: function(x) { print("fc invoked as a function") }, + accept: function(x) { print("fc invoked as a consumer") } +}); + +var c = new Consumer(function(x) { print("c invoked as a consumer") }); + +var cf = new (Java.extend(Consumer, JFunction))({ + apply: function(x) { print("cf invoked as a function") }, + accept: function(x) { print("cf invoked as a consumer") } +}); + +var f = new JFunction(function(x) { print("f invoked as a function") }); + +for each(x in [fc, c, fc, cf, f, cf, c, fc, f, cf]) { x(null); } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8068901.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,10 @@ +fc invoked as a function +c invoked as a consumer +fc invoked as a function +cf invoked as a consumer +f invoked as a function +cf invoked as a consumer +c invoked as a consumer +fc invoked as a function +f invoked as a function +cf invoked as a consumer
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8068903.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, 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-8068903: Can't invoke vararg @FunctionalInterface methods + * + * @test + * @run + */ + +var vc = new (Java.type("jdk.nashorn.test.models.VarArgConsumer"))( + function(x) { + Assert.assertTrue(x.length == 3); + Assert.assertTrue(x[0] == 1); + Assert.assertTrue(x[1] == 2); + Assert.assertTrue(x[2] == 3); + } +); + +vc(1, 2, 3);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8073733.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 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-8073733: TypeError messages with "call" and "new" could be improved + * + * @test + * @run + */ + +var func = undefined; +try { + func(); +} catch (e) { + print(e); +} + +var obj = {}; +try { + obj.foo(); +} catch (e) { + print(e); +} + +try { + new func(); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8073733.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,3 @@ +TypeError: func is not a function +TypeError: obj.foo is not a function +TypeError: func is not a function
--- a/test/script/basic/JDK-8078612_eager_1a.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8078612_eager_1a.js Wed Dec 02 23:08:29 2015 -0800 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8078612_eager_1b.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8078612_eager_1b.js Wed Dec 02 23:08:29 2015 -0800 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8078612_eager_2a.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8078612_eager_2a.js Wed Dec 02 23:08:29 2015 -0800 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8078612_eager_2b.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8078612_eager_2b.js Wed Dec 02 23:08:29 2015 -0800 @@ -29,6 +29,7 @@ * @option -pcc * @option --lazy-compilation=false * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache + * @option -Dnashorn.options.allowEagerCompilationSilentOverride * @fork */
--- a/test/script/basic/JDK-8079470.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/JDK-8079470.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -1,2 +1,2 @@ -TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod File java.io.File.java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures. -TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures. +TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.io.File(String,String)] with the passed arguments; they do not match any of its method signatures. +TypeError: Can not create new object with constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod java.awt.Color(int,int,int)] with the passed arguments; they do not match any of its method signatures.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8087312.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 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-8087312: PropertyMapWrapper.equals should compare className + * + * @test + * @run + * @fork + * @option -Dnashorn.debug=true + */ + +function createObject(type) { + // we want to make sure two different object literals with the same keys and types share the same property map. + if (type) { + return { + a: "a", + b: 1, + c: 0.1 + } + } else { + return { + a: "x", + b: 10, + c: 3.4 + } + } +} + +var o1 = createObject(false); +var o2 = createObject(true); +Assert.assertTrue(Debug.map(o1) === Debug.map(o2)); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8114838.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 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-8114838: Anonymous functions escape to surrounding scope when defined under "with" statement + * + * @test + * @run + */ + +// do *not* introduce new lines! The next line should be 32 +with({}) { function () {} } +if (typeof this["L:32"] != 'undefined') { + throw new Error("anonymous name spills into global scope"); +} + +var func = eval("function() {}"); +if (typeof func != 'function') { + throw new Error("eval of anonymous function does not work!"); +} + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +var engine = new ScriptEngineManager().getEngineByName("nashorn"); +var func2 = engine.eval("function() {}"); +if (typeof func2 != 'function') { + throw new Error("eval of anonymous function does not work from script engine!"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8130853.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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-8130853: Non-extensible global is not handled property + * + * @test + * @run + */ + +// don't allow extensions to global +Object.preventExtensions(this); +try { + eval("var x = 34;"); + throw new Error("should have thrown TypeError"); +} catch (e) { + if (! (e instanceof TypeError)) { + throw e; + } +} + +try { + eval("function func() {}"); + throw new Error("should have thrown TypeError"); +} catch (e) { + if (! (e instanceof TypeError)) { + throw e; + } +} + +function checkLoad(code) { + try { + load({ name: "test", script: code }); + throw new Error("should have thrown TypeError for load: " + code); + } catch (e) { + if (! (e instanceof TypeError)) { + throw e; + } + } +} + +checkLoad("var y = 55"); +checkLoad("function f() {}"); + +// check script engine eval +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +var e = new ScriptEngineManager().getEngineByName("nashorn"); +var global = e.eval("this"); +e.eval("Object.preventExtensions(this);"); +try { + e.eval("var myVar = 33;"); + throw new Error("should have thrown TypeError"); +} catch (e) { + if (! (e.cause.ecmaError instanceof global.TypeError)) { + throw e; + } +} + +// Object.bindProperties on arbitrary non-extensible object +var obj = {}; +Object.preventExtensions(obj); +try { + Object.bindProperties(obj, { foo: 434 }); + throw new Error("should have thrown TypeError"); +} catch (e) { + if (! (e instanceof TypeError)) { + throw e; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8131039.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 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-8131039: after adding a function property to Object.prototype, JSON.parse with reviver function goes into infinite loop + * + * @test + * @run + */ + +Object.prototype.func = function() {} + +function identity(k, v) { return v }; +var obj = JSON.parse('{\"name\" : \"nashorn\"}', identity); +Assert.assertTrue(obj.name, "nashorn");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8131340.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, 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-8131340: Varargs function is recompiled each time it is linked + * + * @test + * @run + */ + +// This is an indirect test. If repeated calls were to cause recompilation +// this would trigger an assertion in RecompilableScriptFunctionData. + +function varargs() { + return arguments; +} + +varargs(1); +varargs(2); +varargs(3); +varargs(4); +varargs(5); +varargs(6);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8131683.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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-8131683: Delete fails over multiple scopes + * + * @test + * @run + */ + +a = 1; +b = 2; +c = 3; + +var A = 1; +var B = 2; +var C = 3; +function D() {} + +print((function() { + var x; // force creation of scope + (function() { x; })(); + return delete a; +})()); + +print((function() { + eval(""); + return delete b; +})()); + +print((function() { + return eval("delete c"); +})()); + +print((function() { + eval("d = 4"); + return eval("delete d"); +})()); + +print(typeof a); +print(typeof b); +print(typeof c); +print(typeof d); + +print((function() { + var x; // force creation of scope + (function() { x; })(); + return delete A; +})()); + +print((function() { + eval(""); + return delete B; +})()); + +print((function() { + return eval("delete C"); +})()); + +print((function() { + eval(""); + return delete D; +})()); + +print(typeof A); +print(typeof B); +print(typeof C); +print(typeof D); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8131683.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,16 @@ +true +true +true +true +undefined +undefined +undefined +undefined +false +false +false +false +number +number +number +function
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8133119.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, 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-8133119: Error message associated with TypeError for call and new should include stringified Node + * + * @test + * @run + */ + +var obj = {} +try { + obj.func(); +} catch (e) { + print(e); +} + +var arr = [33]; +try { + arr[0].func(); +} catch (e) { + print(e); +} + +try { + new obj.func(); +} catch (e) { + print(e); +} + +try { + new arr[0].func(); +} catch (e) { + print(e); +} + +obj.foo = {} +try { + new obj.foo(); +} catch (e) { + print(e); +} + +try { + obj.foo(); +} catch (e) { + print(e); +} + +var v = new java.util.Vector(); +try { + v(); +} catch (e) { + print(e); +} + +try { + new v(); +} catch (e) { + print(e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8133119.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,8 @@ +TypeError: obj.func is not a function +TypeError: arr[0].func is not a function +TypeError: obj.func is not a function +TypeError: arr[0].func is not a function +TypeError: obj.foo is not a function +TypeError: obj.foo is not a function +TypeError: v is not a function +TypeError: v is not a function
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134488.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015 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-8134488: var statement in if(false) block incorrectly evacuated into enclosing function + * + * @test + * @run + */ + +var x = "string"; +print(x); + +(function f1() { + (function f2() { + // If it finds both 'print' and 'x', it'll print 'string'. + print(x); + })(); + + if (false) { + (function f3() { + var x; + })(); + } + +})();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134488.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,2 @@ +string +string
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134490.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 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-8134490: Dead var statement evacuation incorrectly descends into nested functions + * + * @test + * @run + */ + +var v1; + +function f1() +{ +v1 = 1; +return true; +(function () { var v1; })(); +} + +f1(); +// If it executes without throwing an exception in code generator, it's working.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134569.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2015, 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-8134569: Add tests for prototype callsites + * + * @test + * @run + */ + +function create() { + function C() { + this.i1 = 1; + this.i2 = 2; + this.i3 = 3; + return this; + } + return new C(); +} + +function createEmpty() { + function C() { + return this; + } + return new C(); +} + +function createDeep() { + function C() { + this.i1 = 1; + this.i2 = 2; + this.i3 = 3; + return this; + } + function D() { + this.p1 = 1; + this.p2 = 2; + this.p3 = 3; + return this; + } + C.prototype = new D(); + return new C(); +} + +function createDeeper() { + function C() { + this.i1 = 1; + this.i2 = 2; + this.i3 = 3; + return this; + } + function D() { + this.p1 = 1; + this.p2 = 2; + this.p3 = 3; + return this; + } + function E() { + this.e1 = 1; + this.e2 = 2; + this.e3 = 3; + return this; + } + D.prototype = new E(); + C.prototype = new D(); + return new C(); +} + +function createEval() { + return eval("Object.create({})"); +} + +function p(o) { print(o.x) } + +function e(o) { print(o.e1) } + +var a, b, c; + +create(); +a = create(); +b = create(); +c = create(); +a.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = create(); +b = create(); +c = create(); +b.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createEmpty(); +b = createEmpty(); +c = createEmpty(); +a.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createEmpty(); +b = createEmpty(); +c = createEmpty(); +b.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeep(); +b = createDeep(); +c = createDeep(); +a.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeep(); +b = createDeep(); +c = createDeep(); +b.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +a.__proto__.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +b.__proto__.__proto__.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +a.__proto__.__proto__ = null; + +e(a); +e(b); +e(c); + +a = createDeeper(); +b = createDeeper(); +c = createDeeper(); +b.__proto__.__proto__ = null; + +e(a); +e(b); +e(c); + + +a = createEval(); +b = createEval(); +c = createEval(); +a.__proto__.x = 123; + +p(a); +p(b); +p(c); + +a = createEval(); +b = createEval(); +c = createEval(); +b.__proto__.x = 123; + +p(a); +p(b); +p(c);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134569.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,36 @@ +123 +undefined +undefined +undefined +123 +undefined +123 +undefined +undefined +undefined +123 +undefined +123 +undefined +undefined +undefined +123 +undefined +123 +undefined +undefined +undefined +123 +undefined +undefined +1 +1 +1 +undefined +1 +123 +undefined +undefined +undefined +123 +undefined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134609.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, 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-8134609: Allow constructors with same prototoype map to share the allocator map + * + * @test + * @run + * @fork + * @option -Dnashorn.debug + */ + +function createProto(members) { + function P() { + for (var id in members) { + if (members.hasOwnProperty(id)) { + this[id] = members[id]; + } + } + return this; + } + return new P(); +} + +function createSubclass(prototype, members) { + function C() { + for (var id in members) { + if (members.hasOwnProperty(id)) { + this[id] = members[id]; + } + } + return this; + } + + C.prototype = prototype; + + return new C(); +} + +function assertP1(object, value) { + Assert.assertTrue(object.p1 === value); +} + +// First prototype will have non-shared proto-map. Second and third will be shared. +var proto0 = createProto({p1: 0, p2: 1}); +var proto1 = createProto({p1: 1, p2: 2}); +var proto2 = createProto({p1: 2, p2: 3}); + +Assert.assertTrue(Debug.map(proto1) === Debug.map(proto2)); + +assertP1(proto1, 1); +assertP1(proto2, 2); + +// First instantiation will have a non-shared prototype map, from the second one +// maps will be shared until a different proto map comes along. +var child0 = createSubclass(proto1, {c1: 1, c2: 2}); +var child1 = createSubclass(proto2, {c1: 2, c2: 3}); +var child2 = createSubclass(proto1, {c1: 3, c2: 4}); +var child3 = createSubclass(proto2, {c1: 1, c2: 2}); +var child4 = createSubclass(proto0, {c1: 3, c2: 2}); + +Assert.assertTrue(Debug.map(child1) === Debug.map(child2)); +Assert.assertTrue(Debug.map(child1) === Debug.map(child3)); +Assert.assertTrue(Debug.map(child3) !== Debug.map(child4)); + +assertP1(child1, 2); +assertP1(child2, 1); +assertP1(child3, 2); +assertP1(child4, 0); + +Assert.assertTrue(delete proto2.p1); + +assertP1(child3, undefined); +assertP1(child2, 1); +Assert.assertTrue(Debug.map(child1) !== Debug.map(child3));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134731.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, 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-8134731: `Function.prototype.apply` interacts incorrectly with `arguments` + * + * @test + * @run + */ + +function func() { + return (function(f){ + return function(a1, a2, a3, a4){ + return (f.apply(this, arguments)); + } + })(function(){ + return arguments.length; + }) +} + +Assert.assertTrue(func()() == 0); +Assert.assertTrue(func()(33) == 1); +Assert.assertTrue(func()(33, true) == 2); +Assert.assertTrue(func()(33, true, "hello") == 3); +Assert.assertTrue(func()(33, true, "hello", "world") == 4); +Assert.assertTrue(func()(33, true, "hello", "world", 42) == 5);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8134939.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 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-8134939: Improve toString method of Dynalink OverloadedDynamicMethod + * + * @test + * @run + */ + +var overloadedSetter = new (Java.type("jdk.nashorn.test.models.OverloadedSetter")); + +Assert.assertEquals(String(overloadedSetter.foo), + "[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" + + " String jdk.nashorn.test.models.OverloadedSetter.foo(String)\n" + + " void jdk.nashorn.test.models.OverloadedSetter.foo(int)\n" + + "]"); + +Assert.assertEquals(String(overloadedSetter.setColor), + "[jdk.internal.dynalink.beans.OverloadedDynamicMethod\n" + + " void jdk.nashorn.test.models.OverloadedSetter.setColor(int)\n" + + " void jdk.nashorn.test.models.OverloadedSetter.setColor(String)\n" + + "]");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8135000.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, 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-8135000: Number.prototype.toFixed returns wrong string for 0.5 and -0.5 + * + * @test + * @run + */ + +print(-2.6.toFixed()); +print(-2.5.toFixed()); +print(-2.4.toFixed()); +print(-1.6.toFixed()); +print(-1.5.toFixed()); +print(-1.4.toFixed()); +print(-0.6.toFixed()); +print(-0.5.toFixed()); +print(-0.4.toFixed()); +print(0.4.toFixed()); +print(0.5.toFixed()); +print(0.6.toFixed()); +print(1.4.toFixed()); +print(1.5.toFixed()); +print(1.6.toFixed()); +print(2.4.toFixed()); +print(2.5.toFixed()); +print(2.6.toFixed());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8135000.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,18 @@ +-3 +-3 +-2 +-2 +-2 +-1 +-1 +-1 +0 +0 +1 +1 +1 +2 +2 +2 +3 +3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8135190.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 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-8135190: Method code too large in Babel browser.js script + * + * @test + * @run + */ + +// Make sure huge object literals are parsed correctly and don't throw +// (using buildObject -> JSON.stringify -> eval -> testObject) + +function buildObject(n, d) { + if (n < 2) { + return {name: "property", type: "identifier"}; + } + var obj = {}; + for (var i = 0; i < n; i++) { + obj["expr" + i] = buildObject(Math.floor(n / d), d); + } + return obj; +} + +function testObject(obj, n, d) { + var keys = Object.keys(obj); + if (n < 2) { + Assert.assertTrue(keys.length === 2); + Assert.assertTrue(keys[0] === "name"); + Assert.assertTrue(keys[1] === "type"); + } else { + Assert.assertTrue(keys.length === n); + for (var i = 0; i < n; i++) { + Assert.assertTrue(keys[i] === "expr" + i); + } + } + if (n >= 2) { + for (var k in keys) { + testObject(obj[keys[k]], Math.floor(n / d), d) + } + } +} + +var fieldObject = (eval("(" + JSON.stringify(buildObject(25, 2)) + ")")); +testObject(fieldObject, 25, 2); +var spillObject = (eval("(" + JSON.stringify(buildObject(1000, 100)) + ")")); +testObject(spillObject, 1000, 100);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8136544.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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-8136544: Call site switching to megamorphic causes incorrect property read + * + * @test + * @fork + * @option -Dnashorn.unstable.relink.threshold=8 + * @run + */ + +var ScriptContext = Java.type("javax.script.ScriptContext"); +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +var m = new ScriptEngineManager(); +var e = m.getEngineByName("nashorn"); + +var scope = e.getBindings(ScriptContext.ENGINE_SCOPE); +var MYVAR = "myvar"; + +function loopupVar() { + try { + e.eval(MYVAR); + return true; + } catch (e) { + return false; + } +} + +// make sure we exercise callsite beyond megamorphic threshold we set +// in this test via nashorn.unstable.relink.threshold property +// In each iteration, callsite is exercised twice (two evals) +// So, LIMIT should be more than 4 to exercise megamorphic callsites. + +var LIMIT = 5; // This LIMIT should be more than 4 + +for (var i = 0; i < LIMIT; i++) { + // remove the variable and lookup + delete scope[MYVAR]; + Assert.assertFalse(loopupVar(), "Expected true in iteration " + i); + + // set that variable and check again + scope[MYVAR] = "foo"; + Assert.assertTrue(loopupVar(), "Expected false in iteration " + i); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8136694.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, 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-8136694: Megemorphic scope access does not throw ReferenceError when property is missing + * + * @test + * @fork + * @option -Dnashorn.unstable.relink.threshold=16 + * @run + */ + +function checkFoo() { + try { + // The 'foo' access becomes megamorphic + foo; + return true; + } catch (e) { + return false; + } +} + + +// Similar check for 'with' blocks as well. +function checkFooInWith() { + with({}) { + try { + // The 'foo' access becomes megamorphic + foo; + return true; + } catch (e) { + return false; + } + } +} + +function loop(checker) { + // LIMIT has to be more than the megamorphic threashold + // set via @option in this test header! + var LIMIT = 20; + for (var i = 0; i < LIMIT; i++) { + // make sure global has no "foo" + delete foo; + Assert.assertFalse(checker(), "Expected false in interation " + i); + + // now add 'foo' in global + foo = 44; + Assert.assertTrue(checker(), "Expected true in interation " + i); + } +} + + +loop(checkFoo); +loop(checkFooInWith);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8137134.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, 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-8137134: invokespecial on indirect super interface is generated by Java adapter generator + * + * @test + * @run + */ + +var B = Java.type("jdk.nashorn.test.models.B"); +var b1 = new B() {} +print(b1.a()); +print(b1.b()); + +var b2 = new B() { + b: function() { + return "from B.b in script"; + } +}; + +print(b2.a()); +print(b2.b()); + +var b3 = new B() { + a: function() { + return "from A.a in script"; + }, + b: function() { + return "from B.b in script"; + } +}; + +print(b3.a()); +print(b3.b());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8137134.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,6 @@ +from A.a +from B.b +from A.a +from B.b in script +from A.a in script +from B.b in script
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8137281.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 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-8137281: OutOfMemoryError with large numeric keys in JSON.parse + * + * @test + * @run + */ + +function createObject(startKey, level1, level2) { + var root = {}; + var key = startKey; + for (var i = 0; i < level1; i++) { + var child = {}; + for (var j = 0; j < level2; j++) { + child[key++] = {}; + } + root[key++] = child; + } + return root; +} + +JSON.parse(JSON.stringify(createObject(500000, 20, 20))); +JSON.parse(JSON.stringify(createObject(1000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(2000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(4000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(8000000, 20, 20))); +JSON.parse(JSON.stringify(createObject(16000000, 20, 20)));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8138632.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 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-8138632: Sparse array does not handle growth of underlying dense array + * + * @test + * @run + */ + +var x = []; +x[10000000] = 1; +x[10] = 1; +x[20] = 1; +print(Object.keys(x));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8138632.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,1 @@ +10,20,10000000
--- a/test/script/basic/NASHORN-75.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/NASHORN-75.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -1,3 +1,3 @@ -TypeError: [RegExp /a|b/g] is not a function -TypeError: [String hello] is not a function -TypeError: [object Object] is not a function +TypeError: RegExp("a|b", "g") is not a function +TypeError: new String("hello") is not a function +TypeError: new Object() is not a function
--- a/test/script/basic/errors.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/errors.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -1,31 +1,31 @@ -Error is a function -EvalError is a function -RangeError is a function -ReferenceError is a function -SyntaxError is a function -TypeError is a function -URIError is a function -Error.arity 1 -EvalError.arity 1 -RangeError.arity 1 -ReferenceError.arity 1 -SyntaxError.arity 1 -TypeError.arity 1 -URIError.arity 1 -true -my error -Error -thrown @ 49 -true -ReferenceError -"foo" is not defined -true -TypeError -Cannot call undefined -Error -EvalError -RangeError -ReferenceError -SyntaxError -TypeError -URIError +Error is a function +EvalError is a function +RangeError is a function +ReferenceError is a function +SyntaxError is a function +TypeError is a function +URIError is a function +Error.arity 1 +EvalError.arity 1 +RangeError.arity 1 +ReferenceError.arity 1 +SyntaxError.arity 1 +TypeError.arity 1 +URIError.arity 1 +true +my error +Error +thrown @ 49 +true +ReferenceError +"foo" is not defined +true +TypeError +Object.foo_method is not a function +Error +EvalError +RangeError +ReferenceError +SyntaxError +TypeError +URIError
--- a/test/script/basic/javaarrayconversion.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/basic/javaarrayconversion.js Wed Dec 02 23:08:29 2015 -0800 @@ -128,24 +128,32 @@ // Converting to string, toString takes precedence over valueOf test({ valueOf: function() { return "42"; }, toString: function() { return "43"; } }, "java.lang.String", "43") +function assertCanConvert(sourceType, targetType) { + Java.to([new (Java.type(sourceType))()], targetType + "[]") + ++testCount; +} + function assertCantConvert(sourceType, targetType) { try { - Java.to([new Java.type(sourceType)()], targetType + "[]") + Java.to([new (Java.type(sourceType))()], targetType + "[]") throw "no TypeError encountered" } catch(e) { - if(!(e instanceof TypeError)) { + if(!(e instanceof TypeError) || + !e.message.startsWith("Java.to conversion to array type")) { throw e; } ++testCount; } } +// Arbitrary POJOs to JS Primitive type should work +assertCanConvert("java.util.BitSet", "int") +assertCanConvert("java.util.BitSet", "double") +assertCanConvert("java.util.BitSet", "long") +assertCanConvert("java.util.BitSet", "boolean") +assertCanConvert("java.util.BitSet", "java.lang.String") + // Arbitrary POJOs can't be converted to Java values -assertCantConvert("java.util.BitSet", "int") -assertCantConvert("java.util.BitSet", "double") -assertCantConvert("java.util.BitSet", "long") -assertCantConvert("java.util.BitSet", "boolean") -assertCantConvert("java.util.BitSet", "java.lang.String") assertCantConvert("java.util.BitSet", "java.lang.Double") assertCantConvert("java.util.BitSet", "java.lang.Long")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/nosecurity/JDK-8073613.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, 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-8073613: Here documents: how to avoid string interpolation? + * + * @test + * @option -scripting + * @run + */ + +var a = 2, + b = 3 + +print(<<EOD) +${a}${b} +EOD + +print(<<"EOD") +${a}${b} +EOD + +print(<<'EOM') +${a}${b} +EOM + +print(<<"EOM") +$\{a} +EOM +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/nosecurity/JDK-8073613.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,4 @@ +23 +${a}${b} +${a}${b} +$\{a}
--- a/test/script/trusted/JDK-8006529.js Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/trusted/JDK-8006529.js Wed Dec 02 23:08:29 2015 -0800 @@ -120,7 +120,7 @@ var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class) var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class) -var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, ErrorManager.class, boolean.class); +var CompilerConstructor = Compiler.class.getMethod("forNoInstallerCompilation", Context.class, Source.class, boolean.class); // compile(script) -- compiles a script specified as a string with its // source code, returns a jdk.nashorn.internal.ir.FunctionNode object @@ -134,7 +134,7 @@ var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance()); var func = parseMethod.invoke(parser); - var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false); + var compiler = CompilerConstructor.invoke(null, ctxt, source, false); return compileMethod.invoke(compiler, func, phases); };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/trusted/JDK-8087292.js Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 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-8087292: nashorn should have a "fail-fast" option for scripting, analog to bash "set -e" + * + * @test + * @option -scripting + * @run + */ + +function tryExec() { + try { + `java` + } catch (e) { + print(e); + } + + // make sure we got non-zero ("failure") exit code! + if ($EXIT == 0) { + print("Error: expected $EXIT code to be non-zero"); + } +} + +// no exception now! +tryExec(); + +// turn on error with non-zero exit code +$EXEC.throwOnError = true; +tryExec(); + +// no exception after this +$EXEC.throwOnError = false; +tryExec();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/trusted/JDK-8087292.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,1 @@ +RangeError: $EXEC returned non-zero exit code: 1
--- a/test/script/trusted/classfilter.js.EXPECTED Tue Dec 01 22:55:46 2015 -0800 +++ b/test/script/trusted/classfilter.js.EXPECTED Wed Dec 02 23:08:29 2015 -0800 @@ -4,7 +4,18 @@ typeof java.util.Map evalutes to function typeof java.util.HashMap evalutes to function var m = new java.util.HashMap(); m.put('foo', 42); m evalutes to {foo=42} -java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] +java.lang.System.out.println evalutes to [jdk.internal.dynalink.beans.OverloadedDynamicMethod + void java.io.PrintStream.println() + void java.io.PrintStream.println(boolean) + void java.io.PrintStream.println(char) + void java.io.PrintStream.println(char[]) + void java.io.PrintStream.println(double) + void java.io.PrintStream.println(float) + void java.io.PrintStream.println(int) + void java.io.PrintStream.println(long) + void java.io.PrintStream.println(Object) + void java.io.PrintStream.println(String) +] java.lang.System.exit evalutes to [jdk.internal.dynalink.beans.SimpleDynamicMethod void java.lang.System.exit(int)] new javax.script.SimpleBindings throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.SimpleBindings Java.type('javax.script.ScriptContext') throws java.lang.RuntimeException: java.lang.ClassNotFoundException: javax.script.ScriptContext
--- a/test/src/jdk/nashorn/api/scripting/JSONCompatibleTest.java Tue Dec 01 22:55:46 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2015, 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 static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import javax.script.ScriptEngine; -import javax.script.ScriptException; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class JSONCompatibleTest { - - /** - * Wrap a top-level array as a list. - */ - @Test - public void testWrapArray() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("Java.asJSONCompatible([1, 2, 3])"); - assertEquals(asList(val), Arrays.asList(1, 2, 3)); - } - - /** - * Wrap an embedded array as a list. - */ - @Test - public void testWrapObjectWithArray() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("Java.asJSONCompatible({x: [1, 2, 3]})"); - assertEquals(asList(asMap(val).get("x")), Arrays.asList(1, 2, 3)); - } - - /** - * Check it all works transitively several more levels down. - */ - @Test - public void testDeepWrapping() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("Java.asJSONCompatible({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); - final Map<String, Object> root = asMap(val); - final List<Object> x = asList(root.get("x")); - assertEquals(x.get(0), 1); - final Map<String, Object> x1 = asMap(x.get(1)); - final List<Object> y = asList(x1.get("y")); - assertEquals(y.get(0), 2); - final Map<String, Object> y1 = asMap(y.get(1)); - assertEquals(asList(y1.get("z")), Arrays.asList(3)); - assertEquals(asList(x.get(2)), Arrays.asList(4, 5)); - } - - /** - * Ensure that the old behaviour (every object is a Map) is unchanged. - */ - @Test - public void testNonWrapping() throws ScriptException { - final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); - final Object val = engine.eval("({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); - final Map<String, Object> root = asMap(val); - final Map<String, Object> x = asMap(root.get("x")); - assertEquals(x.get("0"), 1); - final Map<String, Object> x1 = asMap(x.get("1")); - final Map<String, Object> y = asMap(x1.get("y")); - assertEquals(y.get("0"), 2); - final Map<String, Object> y1 = asMap(y.get("1")); - final Map<String, Object> z = asMap(y1.get("z")); - assertEquals(z.get("0"), 3); - final Map<String, Object> x2 = asMap(x.get("2")); - assertEquals(x2.get("0"), 4); - assertEquals(x2.get("1"), 5); - } - - @SuppressWarnings("unchecked") - private static List<Object> asList(final Object obj) { - assertJSObject(obj); - Assert.assertTrue(obj instanceof List); - return (List)obj; - } - - @SuppressWarnings("unchecked") - private static Map<String, Object> asMap(final Object obj) { - assertJSObject(obj); - Assert.assertTrue(obj instanceof Map); - return (Map)obj; - } - - private static void assertJSObject(final Object obj) { - assertTrue(obj instanceof JSObject); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/api/scripting/test/JSONCompatibleTest.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, 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.test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import jdk.nashorn.api.scripting.JSObject; +import jdk.nashorn.api.scripting.NashornScriptEngine; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class JSONCompatibleTest { + + /** + * Wrap a top-level array as a list. + */ + @Test + public void testWrapArray() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("Java.asJSONCompatible([1, 2, 3])"); + assertEquals(asList(val), Arrays.asList(1, 2, 3)); + } + + /** + * Wrap an embedded array as a list. + */ + @Test + public void testWrapObjectWithArray() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("Java.asJSONCompatible({x: [1, 2, 3]})"); + assertEquals(asList(asMap(val).get("x")), Arrays.asList(1, 2, 3)); + } + + /** + * Check it all works transitively several more levels down. + */ + @Test + public void testDeepWrapping() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("Java.asJSONCompatible({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); + final Map<String, Object> root = asMap(val); + final List<Object> x = asList(root.get("x")); + assertEquals(x.get(0), 1); + final Map<String, Object> x1 = asMap(x.get(1)); + final List<Object> y = asList(x1.get("y")); + assertEquals(y.get(0), 2); + final Map<String, Object> y1 = asMap(y.get(1)); + assertEquals(asList(y1.get("z")), Arrays.asList(3)); + assertEquals(asList(x.get(2)), Arrays.asList(4, 5)); + } + + /** + * Ensure that the old behaviour (every object is a Map) is unchanged. + */ + @Test + public void testNonWrapping() throws ScriptException { + final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); + final Object val = engine.eval("({x: [1, {y: [2, {z: [3]}]}, [4, 5]]})"); + final Map<String, Object> root = asMap(val); + final Map<String, Object> x = asMap(root.get("x")); + assertEquals(x.get("0"), 1); + final Map<String, Object> x1 = asMap(x.get("1")); + final Map<String, Object> y = asMap(x1.get("y")); + assertEquals(y.get("0"), 2); + final Map<String, Object> y1 = asMap(y.get("1")); + final Map<String, Object> z = asMap(y1.get("z")); + assertEquals(z.get("0"), 3); + final Map<String, Object> x2 = asMap(x.get("2")); + assertEquals(x2.get("0"), 4); + assertEquals(x2.get("1"), 5); + } + + @SuppressWarnings("unchecked") + private static List<Object> asList(final Object obj) { + assertJSObject(obj); + Assert.assertTrue(obj instanceof List); + return (List)obj; + } + + @SuppressWarnings("unchecked") + private static Map<String, Object> asMap(final Object obj) { + assertJSObject(obj); + Assert.assertTrue(obj instanceof Map); + return (Map)obj; + } + + private static void assertJSObject(final Object obj) { + assertTrue(obj instanceof JSObject); + } +}
--- a/test/src/jdk/nashorn/api/scripting/test/PluggableJSObjectTest.java Tue Dec 01 22:55:46 2015 -0800 +++ b/test/src/jdk/nashorn/api/scripting/test/PluggableJSObjectTest.java Wed Dec 02 23:08:29 2015 -0800 @@ -27,6 +27,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.nio.IntBuffer; @@ -34,9 +35,11 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Set; +import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import jdk.nashorn.api.scripting.AbstractJSObject; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import org.testng.annotations.Test; /** @@ -286,4 +289,23 @@ fail(exp.getMessage()); } } + + // @bug 8137258: JSObjectLinker and BrowserJSObjectLinker should not expose internal JS objects + @Test + public void hidingInternalObjectsForJSObjectTest() throws Exception { + final ScriptEngineManager engineManager = new ScriptEngineManager(); + final ScriptEngine e = engineManager.getEngineByName("nashorn"); + + final String code = "function func(obj) { obj.foo = [5, 5]; obj.bar = {} }"; + e.eval(code); + + // call the exposed function but pass user defined JSObject impl as argument + ((Invocable)e).invokeFunction("func", new AbstractJSObject() { + @Override + public void setMember(final String name, final Object value) { + // make sure that wrapped objects are passed (and not internal impl. objects) + assertTrue(value.getClass() == ScriptObjectMirror.class); + } + }); + } }
--- a/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Tue Dec 01 22:55:46 2015 -0800 +++ b/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Wed Dec 02 23:08:29 2015 -0800 @@ -26,9 +26,11 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import javax.script.Bindings; +import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; @@ -820,4 +822,93 @@ public void recursiveEvalCallScriptContextTest() throws ScriptException { new RecursiveEval().program(); } + + private static final String VAR_NAME = "myvar"; + + private static boolean lookupVar(final ScriptEngine engine, final String varName) { + try { + engine.eval(varName); + return true; + } catch (final ScriptException se) { + return false; + } + } + + // @bug 8136544: Call site switching to megamorphic causes incorrect property read + @Test + public void megamorphicPropertyReadTest() throws ScriptException { + final NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); + final ScriptEngine engine = factory.getScriptEngine(); + final Bindings scope = engine.getBindings(ScriptContext.ENGINE_SCOPE); + boolean ret; + + // Why 16 is the upper limit of this loop? The default nashorn dynalink megamorphic threshold is 16. + // See jdk.nashorn.internal.runtime.linker.Bootstrap.NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD + // We do, 'eval' of the same in this loop twice. So, 16*2 = 32 times that callsite in the script + // is exercised - much beyond the default megamorphic threshold. + + for (int i = 0; i < 16; i++) { + scope.remove(VAR_NAME); + ret = lookupVar(engine, VAR_NAME); + assertFalse(ret, "Expected false in iteration " + i); + scope.put(VAR_NAME, "foo"); + ret = lookupVar(engine, VAR_NAME); + assertTrue(ret, "Expected true in iteration " + i); + } + } + + // @bug 8138616: invokeFunction fails if function calls a function defined in GLOBAL_SCOPE + @Test + public void invokeFunctionInGlobalScopeTest() throws Exception { + final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + final ScriptContext ctxt = engine.getContext(); + + // define a function called "func" + engine.eval("func = function() { return 42 }"); + + // move ENGINE_SCOPE Bindings to GLOBAL_SCOPE + ctxt.setBindings(ctxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE); + + // create a new Bindings and set as ENGINE_SCOPE + ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + + // define new function that calls "func" now in GLOBAL_SCOPE + engine.eval("newfunc = function() { return func() }"); + + // call "newfunc" and check the return value + Object value = ((Invocable)engine).invokeFunction("newfunc"); + assertTrue(((Number)value).intValue() == 42); + } + + + // @bug 8138616: invokeFunction fails if function calls a function defined in GLOBAL_SCOPE + // variant of above that replaces default ScriptContext of the engine with a fresh instance! + @Test + public void invokeFunctionInGlobalScopeTest2() throws Exception { + final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + + // create a new ScriptContext instance + final ScriptContext ctxt = new SimpleScriptContext(); + // set it as 'default' ScriptContext + engine.setContext(ctxt); + + // create a new Bindings and set as ENGINE_SCOPE + ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + + // define a function called "func" + engine.eval("func = function() { return 42 }"); + + // move ENGINE_SCOPE Bindings to GLOBAL_SCOPE + ctxt.setBindings(ctxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE); + + // create a new Bindings and set as ENGINE_SCOPE + ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + + // define new function that calls "func" now in GLOBAL_SCOPE + engine.eval("newfunc = function() { return func() }"); + + // call "newfunc" and check the return value + Object value = ((Invocable)engine).invokeFunction("newfunc"); + assertTrue(((Number)value).intValue() == 42); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/A.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, 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.test.models; + +public interface A { + default String a() { + return "from A.a"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/B.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, 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.test.models; + +public interface B extends A { + default String b() { + return "from B.b"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/VarArgConsumer.java Wed Dec 02 23:08:29 2015 -0800 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 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.test.models; + +/** + * Simple function interface with a varargs SAM method. + */ +@FunctionalInterface +public interface VarArgConsumer { + public void apply(Object... o); +} +