diff options
author | Edward Nevill edward.nevill@linaro.org <Edward Nevill edward.nevill@linaro.org> | 2014-06-05 13:09:06 +0100 |
---|---|---|
committer | Edward Nevill edward.nevill@linaro.org <Edward Nevill edward.nevill@linaro.org> | 2014-06-05 13:09:06 +0100 |
commit | 59d2cf8b7db9dc6cbb1577bc91cf5e82b3a59c0f (patch) | |
tree | 70ce8c2934116609e8729bbc1f812b8409f283e5 | |
parent | ea93f0f19abb82825dab4fcd3f4f40ba35a36b29 (diff) | |
parent | a5b595082859007405aa5158aa86ac3eee00984f (diff) |
Merge up to jdk8u20-b16
267 files changed, 11554 insertions, 2455 deletions
@@ -236,7 +236,9 @@ c3343930c73c58a22c1d58719bb988aeb25a871f jdk8-b119 32631eed0fad2b31346eb41b29a50227bd29e2ec jdk8-b121 9d112a0e7df737f3b1fa2a021d5bb350ed56c1b7 jdk8-b122 688f4167f92188482b0d80e315c72f726c6d5ff6 jdk8-b123 +0b4301c792254c890811e5b8e32c23338d715928 jdk8u20-b00 7346abe2ea03134e1aee3b3d0fccb047235bd221 jdk8-b124 +d39eb6ab87581becb8efbb5e3a95c99d5e228328 jdk8u20-b01 7346abe2ea03134e1aee3b3d0fccb047235bd221 jdk8-b125 095263db862da23fa04d57c7e93e553831132449 jdk8-b126 fdfbb745caf0e54775a44e66e39d3025785e0528 jdk8-b127 @@ -264,3 +266,21 @@ f0b7b8b5e29a3a4e481fbeb9b346552c9819675e jdk8u5-b08 bc23b19e08eb1575663044902b6442ceaafa924f jdk8u5-b11 18b007062905dac9304605955a4b84eaf2a08553 jdk8u5-b12 e4fb85d69d6b33c9561b932ec5515f44c53c1017 jdk8u5-b13 +43a1183d2ab0ee3dbffd8bc47606e88dbe0c6116 jdk8u20-b02 +9d69311869d513deecfebe767cc5f01502c9c01e jdk8u20-b03 +e70dd55986e085185d976f2a78843a7d7eb87afd jdk8u20-b04 +4ceb9789a6a57bef69b2c7391ac16ea27b6c4f93 jdk8u20-b05 +f87eba70e9ee96255137c389db7cb4e2480e53e7 jdk8-b130 +cca9748cfec7025ac0ddcdede9e5724fa676ad13 jdk8-b131 +5dbdae28a6f3dae3913b118c128bcb1f803317ac jdk8-b132 +4268cd11c2411064ac30dee7a668055ce226c268 jdk8u20-b06 +7e89db817ed094766a039762a8061c3a600c7284 jdk8u20-b07 +2282c86cb1a954efd2fc5b7f22c173be19087c55 jdk8u20-b08 +41f588adeb7a397d395233f00bd3402d0989934a jdk8u20-b09 +fdcdffd5b5b1eb7d442096433d17466f023207f1 jdk8u20-b10 +c116e9229e096ffe841f2b4f79067378288d0d1d jdk8u20-b11 +c720454d2435be052fd941a789ece9468d1e8f74 jdk8u20-b12 +2ca464cf3093444f73e27ace78993629d2f15cb9 jdk8u20-b13 +2f6add5fefb37cfeeb9a7745e7144f0b6d96bbea jdk8u20-b14 +bb2d116675479fb2c9deaeeab6d4c41d41060693 jdk8u20-b15 +c89a4945404ce80e26cb94c90fc13adad6b114bf jdk8u20-b16 @@ -81,13 +81,13 @@ TestNG library and placing its jar file into the lib subdirectory: After that, you can run the tests using: cd make - ant test + ant clean test You can also run the ECMA-262 test suite with Nashorn. In order to do that, you will need to get a copy of it and put it in test/script/external/test262 directory. A convenient way to do it is: - hg clone http://hg.ecmascript.org/tests/test262/ test/script/external/test262 + git clone https://github.com/tc39/test262 test/script/external/test262 Alternatively, you can check it out elsewhere and make test/script/external/test262 a symbolic link to that directory. After @@ -95,6 +95,11 @@ you've done this, you can run the ECMA-262 tests using: cd nashorn~jdk8/nashorn/make ant test262 + +Ant target to get/update external test suites: + + ant externals + ant update-externals These tests take time, so we have a parallelized runner for them that takes advantage of all processor cores on the computer: diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java index 69c28b29..bb6abbc0 100644 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java @@ -50,8 +50,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_TYPE import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_EMPTY_LIST; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_SETISSHARED; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_SETISSHARED_DESC; 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; @@ -191,8 +189,6 @@ public class ClassGenerator { // stack: Collection // pmap = PropertyMap.newMap(Collection<Property>); mi.invokeStatic(PROPERTYMAP_TYPE, PROPERTYMAP_NEWMAP, PROPERTYMAP_NEWMAP_DESC); - // pmap.setIsShared(); - mi.invokeVirtual(PROPERTYMAP_TYPE, PROPERTYMAP_SETISSHARED, PROPERTYMAP_SETISSHARED_DESC); // $nasgenmap$ = pmap; mi.putStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC); mi.returnVoid(); diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java index d46f78c4..67cbde44 100644 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java @@ -33,10 +33,7 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.CONSTRUCTOR_SUFF import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE; import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC; @@ -171,9 +168,6 @@ public class ConstructorGenerator extends ClassGenerator { private void loadMap(final MethodGenerator mi) { if (memberCount > 0) { mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC); - // make sure we use duplicated PropertyMap so that original map - // stays intact and so can be used for many globals. - mi.invokeVirtual(PROPERTYMAP_TYPE, PROPERTYMAP_DUPLICATE, PROPERTYMAP_DUPLICATE_DESC); } } diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java index 3fdd7c68..e47f8b16 100644 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java @@ -22,40 +22,62 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.nashorn.internal.tools.nasgen; import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_DESC; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.STRING_DESC; import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.org.objectweb.asm.Type; import jdk.nashorn.internal.objects.annotations.Where; +import jdk.nashorn.internal.runtime.ScriptObject; /** * Details about a Java method or field annotated with any of the field/method * annotations from the jdk.nashorn.internal.objects.annotations package. */ public final class MemberInfo implements Cloneable { + // class loader of this class + private static ClassLoader myLoader = MemberInfo.class.getClassLoader(); + /** * The different kinds of available class annotations */ public static enum Kind { - /** This is a script class */ + + /** + * This is a script class + */ SCRIPT_CLASS, - /** This is a constructor */ + /** + * This is a constructor + */ CONSTRUCTOR, - /** This is a function */ + /** + * This is a function + */ FUNCTION, - /** This is a getter */ + /** + * This is a getter + */ GETTER, - /** This is a setter */ + /** + * This is a setter + */ SETTER, - /** This is a property */ + /** + * This is a property + */ PROPERTY, - /** This is a specialized version of a function */ + /** + * This is a specialized version of a function + */ SPECIALIZED_FUNCTION, - /** This is a specialized version of a constructor */ + /** + * This is a specialized version of a constructor + */ SPECIALIZED_CONSTRUCTOR } @@ -194,6 +216,7 @@ public final class MemberInfo implements Cloneable { /** * Check whether this MemberInfo is a getter that resides in the instance + * * @return true if instance setter */ boolean isInstanceSetter() { @@ -245,96 +268,201 @@ public final class MemberInfo implements Cloneable { } void verify() { - if (kind == Kind.CONSTRUCTOR) { - final Type returnType = Type.getReturnType(javaDesc); - if (! returnType.toString().equals(OBJECT_DESC)) { - error("return value should be of Object type, found" + returnType); - } - final Type[] argTypes = Type.getArgumentTypes(javaDesc); - if (argTypes.length < 2) { - error("constructor methods should have at least 2 args"); - } - if (! argTypes[0].equals(Type.BOOLEAN_TYPE)) { - error("first argument should be of boolean type, found" + argTypes[0]); - } - if (! argTypes[1].toString().equals(OBJECT_DESC)) { - error("second argument should be of Object type, found" + argTypes[0]); - } + switch (kind) { + case CONSTRUCTOR: { + final Type returnType = Type.getReturnType(javaDesc); + if (!isJSObjectType(returnType)) { + error("return value of a @Constructor method should be of Object type, found " + returnType); + } + final Type[] argTypes = Type.getArgumentTypes(javaDesc); + if (argTypes.length < 2) { + error("@Constructor methods should have at least 2 args"); + } + if (!argTypes[0].equals(Type.BOOLEAN_TYPE)) { + error("first argument of a @Constructor method should be of boolean type, found " + argTypes[0]); + } + if (!isJavaLangObject(argTypes[1])) { + error("second argument of a @Constructor method should be of Object type, found " + argTypes[0]); + } - if (argTypes.length > 2) { - for (int i = 2; i < argTypes.length - 1; i++) { - if (! argTypes[i].toString().equals(OBJECT_DESC)) { - error(i + "'th argument should be of Object type, found " + argTypes[i]); + if (argTypes.length > 2) { + for (int i = 2; i < argTypes.length - 1; i++) { + if (!isJavaLangObject(argTypes[i])) { + error(i + "'th argument of a @Constructor method should be of Object type, found " + argTypes[i]); + } } - } - final String lastArgType = argTypes[argTypes.length - 1].toString(); - final boolean isVarArg = lastArgType.equals(OBJECT_ARRAY_DESC); - if (!lastArgType.equals(OBJECT_DESC) && !isVarArg) { - error("last argument is neither Object nor Object[] type: " + lastArgType); - } + final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); + final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); + if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { + error("last argument of a @Constructor method is neither Object nor Object[] type: " + lastArgTypeDesc); + } - if (isVarArg && argTypes.length > 3) { - error("vararg constructor has more than 3 arguments"); + if (isVarArg && argTypes.length > 3) { + error("vararg of a @Constructor method has more than 3 arguments"); + } } } - } else if (kind == Kind.FUNCTION) { - final Type returnType = Type.getReturnType(javaDesc); - if (! returnType.toString().equals(OBJECT_DESC)) { - error("return value should be of Object type, found" + returnType); - } - final Type[] argTypes = Type.getArgumentTypes(javaDesc); - if (argTypes.length < 1) { - error("function methods should have at least 1 arg"); - } - if (! argTypes[0].toString().equals(OBJECT_DESC)) { - error("first argument should be of Object type, found" + argTypes[0]); + break; + case SPECIALIZED_CONSTRUCTOR: { + final Type returnType = Type.getReturnType(javaDesc); + if (!isJSObjectType(returnType)) { + error("return value of a @SpecializedConstructor method should be a valid JS type, found " + returnType); + } + final Type[] argTypes = Type.getArgumentTypes(javaDesc); + for (int i = 0; i < argTypes.length; i++) { + if (!isValidJSType(argTypes[i])) { + error(i + "'th argument of a @SpecializedConstructor method is not valid JS type, found " + argTypes[i]); + } + } } + break; + case FUNCTION: { + final Type returnType = Type.getReturnType(javaDesc); + if (!isValidJSType(returnType)) { + error("return value of a @Function method should be a valid JS type, found " + returnType); + } + final Type[] argTypes = Type.getArgumentTypes(javaDesc); + if (argTypes.length < 1) { + error("@Function methods should have at least 1 arg"); + } + if (!isJavaLangObject(argTypes[0])) { + error("first argument of a @Function method should be of Object type, found " + argTypes[0]); + } - if (argTypes.length > 1) { - for (int i = 1; i < argTypes.length - 1; i++) { - if (! argTypes[i].toString().equals(OBJECT_DESC)) { - error(i + "'th argument should be of Object type, found " + argTypes[i]); + if (argTypes.length > 1) { + for (int i = 1; i < argTypes.length - 1; i++) { + if (!isJavaLangObject(argTypes[i])) { + error(i + "'th argument of a @Function method should be of Object type, found " + argTypes[i]); + } } - } - final String lastArgType = argTypes[argTypes.length - 1].toString(); - final boolean isVarArg = lastArgType.equals(OBJECT_ARRAY_DESC); - if (!lastArgType.equals(OBJECT_DESC) && !isVarArg) { - error("last argument is neither Object nor Object[] type: " + lastArgType); - } + final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); + final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); + if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { + error("last argument of a @Function method is neither Object nor Object[] type: " + lastArgTypeDesc); + } - if (isVarArg && argTypes.length > 2) { - error("vararg function has more than 2 arguments"); + if (isVarArg && argTypes.length > 2) { + error("vararg @Function method has more than 2 arguments"); + } } } - } else if (kind == Kind.GETTER) { - final Type[] argTypes = Type.getArgumentTypes(javaDesc); - if (argTypes.length != 1) { - error("getter methods should have one argument"); - } - if (! argTypes[0].toString().equals(OBJECT_DESC)) { - error("first argument of getter should be of Object type, found: " + argTypes[0]); + break; + case SPECIALIZED_FUNCTION: { + final Type returnType = Type.getReturnType(javaDesc); + if (!isValidJSType(returnType)) { + error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType); + } + final Type[] argTypes = Type.getArgumentTypes(javaDesc); + for (int i = 0; i < argTypes.length; i++) { + if (!isValidJSType(argTypes[i])) { + error(i + "'th argument of a @SpecializedFunction method is not valid JS type, found " + argTypes[i]); + } + } } - if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) { - error("return type of getter should not be void"); + break; + case GETTER: { + final Type[] argTypes = Type.getArgumentTypes(javaDesc); + if (argTypes.length != 1) { + error("@Getter methods should have one argument"); + } + if (!isJavaLangObject(argTypes[0])) { + error("first argument of a @Getter method should be of Object type, found: " + argTypes[0]); + } + + final Type returnType = Type.getReturnType(javaDesc); + if (!isJavaLangObject(returnType)) { + error("return type of a @Getter method should be Object, found: " + javaDesc); + } } - } else if (kind == Kind.SETTER) { - final Type[] argTypes = Type.getArgumentTypes(javaDesc); - if (argTypes.length != 2) { - error("setter methods should have two arguments"); + break; + case SETTER: { + final Type[] argTypes = Type.getArgumentTypes(javaDesc); + if (argTypes.length != 2) { + error("@Setter methods should have two arguments"); + } + if (!isJavaLangObject(argTypes[0])) { + error("first argument of a @Setter method should be of Object type, found: " + argTypes[0]); + } + if (!Type.getReturnType(javaDesc).toString().equals("V")) { + error("return type of of a @Setter method should be void, found: " + Type.getReturnType(javaDesc)); + } } - if (! argTypes[0].toString().equals(OBJECT_DESC)) { - error("first argument of setter should be of Object type, found: " + argTypes[0]); + break; + case PROPERTY: { + if (where == Where.CONSTRUCTOR) { + if (isStatic()) { + if (!isFinal()) { + error("static Where.CONSTRUCTOR @Property should be final"); + } + + if (!isJSPrimitiveType(Type.getType(javaDesc))) { + error("static Where.CONSTRUCTOR @Property should be a JS primitive"); + } + } + } else if (where == Where.PROTOTYPE) { + if (isStatic()) { + if (!isFinal()) { + error("static Where.PROTOTYPE @Property should be final"); + } + + if (!isJSPrimitiveType(Type.getType(javaDesc))) { + error("static Where.PROTOTYPE @Property should be a JS primitive"); + } + } + } } - if (!Type.getReturnType(javaDesc).toString().equals("V")) { - error("return type of setter should be void, found: " + Type.getReturnType(javaDesc)); + } + } + + private static boolean isValidJSType(final Type type) { + return isJSPrimitiveType(type) || isJSObjectType(type); + } + + private static boolean isJSPrimitiveType(final Type type) { + switch (type.getSort()) { + case Type.BOOLEAN: + case Type.INT: + case Type.LONG: + case Type.DOUBLE: + return true; + default: + return false; + } + } + + private static boolean isJSObjectType(final Type type) { + return isJavaLangObject(type) || isJavaLangString(type) || isScriptObject(type); + } + + private static boolean isJavaLangObject(final Type type) { + return type.getDescriptor().equals(OBJECT_DESC); + } + + private static boolean isJavaLangString(final Type type) { + return type.getDescriptor().equals(STRING_DESC); + } + + private static boolean isScriptObject(final Type type) { + if (type.getDescriptor().equals(SCRIPTOBJECT_DESC)) { + return true; + } + + if (type.getSort() == Type.OBJECT) { + try { + final Class clazz = Class.forName(type.getClassName(), false, myLoader); + return ScriptObject.class.isAssignableFrom(clazz); + } catch (final ClassNotFoundException cnfe) { + return false; } } + + return false; } private void error(final String msg) { - throw new RuntimeException(javaName + javaDesc + " : " + msg); + throw new RuntimeException(javaName + " of type " + javaDesc + " : " + msg); } /** diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java index 475d7328..479d1d31 100644 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java @@ -349,19 +349,19 @@ public class MethodGenerator extends MethodVisitor { // invokes, field get/sets void invokeInterface(final String owner, final String method, final String desc) { - super.visitMethodInsn(INVOKEINTERFACE, owner, method, desc); + super.visitMethodInsn(INVOKEINTERFACE, owner, method, desc, true); } void invokeVirtual(final String owner, final String method, final String desc) { - super.visitMethodInsn(INVOKEVIRTUAL, owner, method, desc); + super.visitMethodInsn(INVOKEVIRTUAL, owner, method, desc, false); } void invokeSpecial(final String owner, final String method, final String desc) { - super.visitMethodInsn(INVOKESPECIAL, owner, method, desc); + super.visitMethodInsn(INVOKESPECIAL, owner, method, desc, false); } void invokeStatic(final String owner, final String method, final String desc) { - super.visitMethodInsn(INVOKESTATIC, owner, method, desc); + super.visitMethodInsn(INVOKESTATIC, owner, method, desc, false); } void putStatic(final String owner, final String field, final String desc) { @@ -413,7 +413,7 @@ public class MethodGenerator extends MethodVisitor { super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", - "(Ljava/lang/String;)V"); + "(Ljava/lang/String;)V", false); } // print the object on the top of the stack @@ -426,6 +426,6 @@ public class MethodGenerator extends MethodVisitor { super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", - "(Ljava/lang/Object;)V"); + "(Ljava/lang/Object;)V", false); } } diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java index 8bb1de5c..7b1fff76 100644 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java @@ -32,10 +32,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.V1_7; import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE; import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE_SUFFIX; @@ -129,7 +126,6 @@ public class PrototypeGenerator extends ClassGenerator { mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC); // make sure we use duplicated PropertyMap so that original map // stays intact and so can be used for many global. - mi.invokeVirtual(PROPERTYMAP_TYPE, PROPERTYMAP_DUPLICATE, PROPERTYMAP_DUPLICATE_DESC); mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC); // initialize Function type fields initFunctionFields(mi); diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java index 72250de7..0ec233ae 100644 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java @@ -146,16 +146,16 @@ public class ScriptClassInstrumentor extends ClassVisitor { // call $clinit$ just before return from <clinit> if (isStaticInit && opcode == RETURN) { super.visitMethodInsn(INVOKESTATIC, scriptClassInfo.getJavaName(), - $CLINIT$, DEFAULT_INIT_DESC); + $CLINIT$, DEFAULT_INIT_DESC, false); } super.visitInsn(opcode); } @Override - public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { + public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) { if (isConstructor && opcode == INVOKESPECIAL && INIT.equals(name) && SCRIPTOBJECT_TYPE.equals(owner)) { - super.visitMethodInsn(opcode, owner, name, desc); + super.visitMethodInsn(opcode, owner, name, desc, false); if (memberCount > 0) { // initialize @Property fields if needed @@ -166,7 +166,7 @@ public class ScriptClassInstrumentor extends ClassVisitor { super.visitTypeInsn(NEW, clazz); super.visitInsn(DUP); super.visitMethodInsn(INVOKESPECIAL, clazz, - INIT, DEFAULT_INIT_DESC); + INIT, DEFAULT_INIT_DESC, false); super.visitFieldInsn(PUTFIELD, scriptClassInfo.getJavaName(), memInfo.getJavaName(), memInfo.getJavaDesc()); } @@ -180,7 +180,7 @@ public class ScriptClassInstrumentor extends ClassVisitor { } } } else { - super.visitMethodInsn(opcode, owner, name, desc); + super.visitMethodInsn(opcode, owner, name, desc, itf); } } diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java index c4c1ab8d..1d724187 100644 --- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java +++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java @@ -45,11 +45,9 @@ import jdk.nashorn.internal.runtime.ScriptObject; @SuppressWarnings("javadoc") public interface StringConstants { // standard jdk types, methods - static final Type TYPE_METHOD = Type.getType(Method.class); static final Type TYPE_METHODHANDLE = Type.getType(MethodHandle.class); static final Type TYPE_METHODHANDLE_ARRAY = Type.getType(MethodHandle[].class); static final Type TYPE_OBJECT = Type.getType(Object.class); - static final Type TYPE_CLASS = Type.getType(Class.class); static final Type TYPE_STRING = Type.getType(String.class); static final Type TYPE_COLLECTION = Type.getType(Collection.class); static final Type TYPE_COLLECTIONS = Type.getType(Collections.class); @@ -63,6 +61,8 @@ public interface StringConstants { static final String METHODHANDLE_TYPE = TYPE_METHODHANDLE.getInternalName(); static final String OBJECT_TYPE = TYPE_OBJECT.getInternalName(); static final String OBJECT_DESC = TYPE_OBJECT.getDescriptor(); + static final String STRING_TYPE = TYPE_STRING.getInternalName(); + static final String STRING_DESC = TYPE_STRING.getDescriptor(); static final String OBJECT_ARRAY_DESC = Type.getDescriptor(Object[].class); static final String ARRAYLIST_TYPE = TYPE_ARRAYLIST.getInternalName(); static final String COLLECTION_TYPE = TYPE_COLLECTION.getInternalName(); @@ -104,10 +104,6 @@ public interface StringConstants { static final String PROPERTYMAP_DESC = TYPE_PROPERTYMAP.getDescriptor(); static final String PROPERTYMAP_NEWMAP = "newMap"; static final String PROPERTYMAP_NEWMAP_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_COLLECTION); - static final String PROPERTYMAP_DUPLICATE = "duplicate"; - static final String PROPERTYMAP_DUPLICATE_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP); - static final String PROPERTYMAP_SETISSHARED = "setIsShared"; - static final String PROPERTYMAP_SETISSHARED_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP); // PrototypeObject static final String PROTOTYPEOBJECT_TYPE = TYPE_PROTOTYPEOBJECT.getInternalName(); @@ -135,6 +131,7 @@ public interface StringConstants { // ScriptObject static final String SCRIPTOBJECT_TYPE = TYPE_SCRIPTOBJECT.getInternalName(); + static final String SCRIPTOBJECT_DESC = TYPE_SCRIPTOBJECT.getDescriptor(); static final String SCRIPTOBJECT_INIT_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_PROPERTYMAP); static final String GETTER_PREFIX = "G$"; diff --git a/make/BuildNashorn.gmk b/make/BuildNashorn.gmk index fc870442..a987874c 100644 --- a/make/BuildNashorn.gmk +++ b/make/BuildNashorn.gmk @@ -77,7 +77,7 @@ $(NASHORN_OUTPUTDIR)/classes/_the.nasgen.run: $(BUILD_NASGEN) $(RM) -rf $(@D)/jdk $(@D)/netscape $(CP) -R -p $(NASHORN_OUTPUTDIR)/nashorn_classes/* $(@D)/ $(FIXPATH) $(JAVA) \ - -cp "$(NASHORN_OUTPUTDIR)/nasgen_classes$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes" \ + -Xbootclasspath/p:"$(NASHORN_OUTPUTDIR)/nasgen_classes$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes" \ jdk.nashorn.internal.tools.nasgen.Main $(@D) jdk.nashorn.internal.objects $(@D) $(TOUCH) $@ diff --git a/make/build.xml b/make/build.xml index 7d1f42ae..4d23f228 100644 --- a/make/build.xml +++ b/make/build.xml @@ -42,6 +42,9 @@ <condition property="hg.executable" value="/usr/local/bin/hg" else="hg"> <available file="/usr/local/bin/hg"/> </condition> + <condition property="git.executable" value="/usr/local/bin/git" else="git"> + <available file="/usr/local/bin/git"/> + </condition> <!-- check if JDK already has ASM classes --> <available property="asm.available" classname="jdk.internal.org.objectweb.asm.Type"/> <!-- check if testng.jar is avaiable --> @@ -122,6 +125,7 @@ <compilerarg value="-Xlint:unchecked"/> <compilerarg value="-Xlint:deprecation"/> <compilerarg value="-XDignore.symbol.file"/> + <compilerarg value="-Xdiags:verbose"/> </javac> <copy todir="${build.classes.dir}/META-INF/services"> <fileset dir="${meta.inf.dir}/services/"/> @@ -240,6 +244,7 @@ <compilerarg value="-J-Djava.ext.dirs="/> <compilerarg value="-Xlint:unchecked"/> <compilerarg value="-Xlint:deprecation"/> + <compilerarg value="-Xdiags:verbose"/> </javac> <copy todir="${build.test.classes.dir}/META-INF/services"> @@ -250,6 +255,10 @@ <fileset dir="${test.src.dir}/jdk/nashorn/internal/runtime/resources"/> </copy> + <copy todir="${build.test.classes.dir}/jdk/nashorn/api/scripting/resources"> + <fileset dir="${test.src.dir}/jdk/nashorn/api/scripting/resources"/> + </copy> + <!-- tests that check nashorn internals and internal API --> <jar jarfile="${nashorn.internal.tests.jar}"> <fileset dir="${build.test.classes.dir}" excludes="**/api/**"/> @@ -279,6 +288,11 @@ grant codeBase "file:/${basedir}/test/script/trusted/*" { permission java.security.AllPermission; }; +grant codeBase "file:/${basedir}/test/script/maptests/*" { + permission java.io.FilePermission "${basedir}/test/script/maptests/*","read"; + permission java.lang.RuntimePermission "nashorn.debugMode"; +}; + grant codeBase "file:/${basedir}/test/script/basic/*" { permission java.io.FilePermission "${basedir}/test/script/-", "read"; permission java.io.FilePermission "$${user.dir}", "read"; @@ -459,18 +473,17 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" { <!-- test262 test suite --> <target name="get-test262" depends="init" unless="${test-sys-prop.external.test262}"> - <!-- clone test262 mercurial repo --> - <exec executable="${hg.executable}"> + <!-- clone test262 git repo --> + <exec executable="${git.executable}"> <arg value="clone"/> - <arg value="http://hg.ecmascript.org/tests/test262"/> + <arg value="https://github.com/tc39/test262"/> <arg value="${test.external.dir}/test262"/> </exec> </target> <target name="update-test262" depends="init" if="${test-sys-prop.external.test262}"> - <!-- update test262 mercurial repo --> - <exec executable="${hg.executable}" dir="${test.external.dir}/test262"> + <!-- update test262 git repo --> + <exec executable="${git.executable}" dir="${test.external.dir}/test262"> <arg value="pull"/> - <arg value="-u"/> </exec> </target> diff --git a/make/project.properties b/make/project.properties index c3ffca4e..8c5dd4f4 100644 --- a/make/project.properties +++ b/make/project.properties @@ -112,6 +112,7 @@ run.classpath=\ test.dir=test test.script.dir=test/script test.basic.dir=test/script/basic +test.maptests.dir=test/script/maptests test.error.dir=test/script/error test.sandbox.dir=test/script/sandbox test.trusted.dir=test/script/trusted @@ -121,7 +122,7 @@ test262.suite.dir=${test262.dir}/test/suite testjfx.dir=${test.script.dir}/jfx test-sys-prop.test.dir=${test.dir} -test-sys-prop.test.js.roots=${test.basic.dir} ${test.error.dir} ${test.sandbox.dir} ${test.trusted.dir} +test-sys-prop.test.js.roots=${test.basic.dir} ${test.maptests.dir} ${test.error.dir} ${test.sandbox.dir} ${test.trusted.dir} test-sys-prop.test262.suite.dir=${test262.suite.dir} test-sys-prop.es5conform.testcases.dir=${test.external.dir}/ES5Conform/TestCases test-sys-prop.test.basic.dir=${test.basic.dir} @@ -201,7 +202,7 @@ test262-test-sys-prop.test.failed.list.file=${build.dir}/test/failedTests # test262 test frameworks test262-test-sys-prop.test.js.framework=\ - --class-cache-size=0 \ + --class-cache-size=10 \ --no-java \ --no-typed-arrays \ -timezone=PST \ @@ -264,7 +265,7 @@ run.test.jvmargs.octane.main=${run.test.jvmargs.common} run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy # VM options for script tests with @fork option -test-sys-prop.test.fork.jvm.options=${run.test.jvmargs.main} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} +test-sys-prop.test.fork.jvm.options=${run.test.jvmargs.main} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -cp ${run.test.classpath} # path of rhino.jar for benchmarks rhino.jar= diff --git a/samples/BufferArray.java b/samples/BufferArray.java new file mode 100644 index 00000000..00d66023 --- /dev/null +++ b/samples/BufferArray.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014, 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 jdk.nashorn.api.scripting.AbstractJSObject; +import java.nio.DoubleBuffer; + +/** + * Simple class demonstrating pluggable script object + * implementation. By implementing jdk.nashorn.api.scripting.JSObject + * (or extending AbstractJSObject which implements it), you + * can supply a friendly script object. Nashorn will call + * 'magic' methods on such a class on 'obj.foo, obj.foo = 33, + * obj.bar()' etc. from script. + * + * In this example, Java nio DoubleBuffer object is wrapped + * as a friendly script object that provides indexed acces + * to buffer content and also support array-like "length" + * readonly property to retrieve buffer's capacity. This class + * also demonstrates a function valued property called "buf". + * On 'buf' method, we return the underlying nio buffer object + * that is being wrapped. + */ +public class BufferArray extends AbstractJSObject { + // underlying nio buffer + private final DoubleBuffer buf; + + public BufferArray(int size) { + buf = DoubleBuffer.allocate(size); + } + + public BufferArray(DoubleBuffer buf) { + this.buf = buf; + } + + // called to check if indexed property exists + @Override + public boolean hasSlot(int index) { + return index > 0 && index < buf.capacity(); + } + + // get the value from that index + @Override + public Object getSlot(int index) { + return buf.get(index); + } + + // set the value at that index + @Override + public void setSlot(int index, Object value) { + buf.put(index, ((Number)value).doubleValue()); + } + + // do you have a property of that given name? + @Override + public boolean hasMember(String name) { + return "length".equals(name) || "buf".equals(name); + } + + // get the value of that named property + @Override + public Object getMember(String name) { + switch (name) { + case "length": + return buf.capacity(); + case "buf": + // return a 'function' value for this property + return new AbstractJSObject() { + @Override + public Object call(Object thiz, Object... args) { + return BufferArray.this.buf; + } + + // yes, I'm a function ! + @Override + public boolean isFunction() { + return true; + } + }; + } + return null; + } +} diff --git a/samples/CastExample.java b/samples/CastExample.java new file mode 100644 index 00000000..d876498d --- /dev/null +++ b/samples/CastExample.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, 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 java example with type casts. +// see javacastcounter.js. + +class CastExample { + public final static int I = (int)23.33; + public final String str = (String)"hello"; +} diff --git a/samples/README b/samples/README new file mode 100644 index 00000000..7dc23299 --- /dev/null +++ b/samples/README @@ -0,0 +1 @@ +Simple Nashorn examples that can be run with "jjs" tool. diff --git a/samples/array_mapreduce.js b/samples/array_mapreduce.js new file mode 100644 index 00000000..5335f35d --- /dev/null +++ b/samples/array_mapreduce.js @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, 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. + */ + +// Usage: jjs array_mapreduce.js + +// Many Array.prototype functions such as map, +// filter, reduce, reduceRight, every, some are generic. +// These functions accept ECMAScript array as well as +// many array-like objects including java arrays. +// So, you can do map/filter/reduce with Java streams or +// you can also use Array.prototype functions as below. +// See also http://en.wikipedia.org/wiki/MapReduce + +var DoubleArray = Java.type("double[]"); +var StringArray = Java.type("java.lang.String[]"); + +var map = Array.prototype.map; +var filter = Array.prototype.filter; +var reduce = Array.prototype.reduce; + +var jarr = new StringArray(5); +jarr[0] = "nashorn"; +jarr[1] = "ecmascript"; +jarr[2] = "javascript"; +jarr[3] = "js"; +jarr[4] = "scheme"; + +// sum of word lengths +print("Sum word length:", + reduce.call( + map.call(jarr, function(x) x.length), + function(x, y) x + y) +); + +// another array example involving numbers +jarr = new DoubleArray(10); +// make random array of numbers +for (var i = 0; i < jarr.length; i++) + jarr[i] = Math.random(); + +var forEach = Array.prototype.forEach; +// print numbers in the array +forEach.call(jarr, function(x) print(x)); + +// print sum of squares of the random numbers +print("Square sum:", + reduce.call( + map.call(jarr, function(x) x*x), + function(x, y) x + y) +); diff --git a/samples/astviewer.js b/samples/astviewer.js new file mode 100644 index 00000000..090e50ee --- /dev/null +++ b/samples/astviewer.js @@ -0,0 +1,98 @@ +#// Usage: jjs -scripting -fx astviewer.js -- <scriptfile> + +/* + * Copyright (c) 2014, 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. + */ + +if (!$OPTIONS._fx) { + print("Usage: jjs -scripting -fx astviewer.js -- <.js file>"); + exit(1); +} + +// Using JavaFX from Nashorn. See also: +// http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/javafx.html + +// This example shows AST of a script file as a JavaFX +// tree view in a window. If no file is specified, AST of +// this script file is shown. This script demonstrates +// 'load' function, JavaFX support by -fx, readFully function +// in scripting mode. + +// JavaFX classes used +var StackPane = Java.type("javafx.scene.layout.StackPane"); +var Scene = Java.type("javafx.scene.Scene"); +var TreeItem = Java.type("javafx.scene.control.TreeItem"); +var TreeView = Java.type("javafx.scene.control.TreeView"); + +// Create a javafx TreeItem to view a AST node +function treeItemForASTNode(ast, name) { + var item = new TreeItem(name); + for (var prop in ast) { + var node = ast[prop]; + if (typeof node == 'object') { + if (node == null) { + // skip nulls + continue; + } + + if (Array.isArray(node) && node.length == 0) { + // skip empty arrays + continue; + } + + var subitem = treeItemForASTNode(node, prop); + } else { + var subitem = new TreeItem(prop + ": " + node); + } + item.children.add(subitem); + } + return item; +} + +// do we have a script file passed? if not, use current script +var sourceName = arguments.length == 0? __FILE__ : arguments[0]; + +// load parser.js from nashorn resources +load("nashorn:parser.js"); + +// read the full content of the file and parse it +// to get AST of the script specified +var ast = parse(readFully(sourceName)); + +// JavaFX start method +function start(stage) { + stage.title = "AST Viewer"; + var rootItem = treeItemForASTNode(ast, sourceName); + var tree = new TreeView(rootItem); + var root = new StackPane(); + root.children.add(tree); + stage.scene = new Scene(root, 300, 450); + stage.show(); +} diff --git a/samples/barchart_weather.js b/samples/barchart_weather.js new file mode 100644 index 00000000..8574f0a2 --- /dev/null +++ b/samples/barchart_weather.js @@ -0,0 +1,116 @@ +#// Usage: jjs -fx barchart_weather.js + +/* + * Copyright (c) 2014, 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. + */ + +// Example that retrieves weather data from a URL in JSON +// format and draws bar chart using JavaFX + +// -fx check +if (! $OPTIONS._fx) { + print("Usage: jjs -fx barchart_weather.js"); + exit(1); +} + +// Java classes used +var URL = Java.type("java.net.URL"); +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(); + } +} + +// change URL for your city here! +var url = "http://api.openweathermap.org/data/2.5/forecast?q=chennai,india&units=metric&mode=json"; + +// download JSON document and parse +var json = readTextFromURL(url); +var weather = JSON.parse(json); + +// View JSON of this using site such as http://www.jsoneditoronline.org/ to know +// about the JSON data format used by this site + +// Extracted data from the json object +var temp = weather.list.map(function(x) x.main.temp); +var temp_min = weather.list.map(function(x) x.main.temp_min); +var temp_max = weather.list.map(function(x) x.main.temp_max); +var date = weather.list.map(function(x) x.dt_txt); + +// JavaFX classes used +var Scene = Java.type("javafx.scene.Scene"); +var BarChart = Java.type("javafx.scene.chart.BarChart"); +var CategoryAxis = Java.type("javafx.scene.chart.CategoryAxis"); +var NumberAxis = Java.type("javafx.scene.chart.NumberAxis"); +var XYChart = Java.type("javafx.scene.chart.XYChart"); + +function start(stage) { + stage.title="Chennai Weather Bar Chart"; + var xAxis = new CategoryAxis(); + xAxis.label = "date/time"; + var yAxis = new NumberAxis(); + yAxis.label = "temp in C"; + var bc = new BarChart(xAxis, yAxis); + + // 3 bars per datetime item - temp, min temp and max temp + var s1 = new XYChart.Series(); + s1.name = "temp"; + for (d in date) { + s1.data.add(new XYChart.Data(date[d], temp[d])); + } + + var s2 = new XYChart.Series(); + s2.name = "min temp"; + for (d in date) { + s2.data.add(new XYChart.Data(date[d], temp_min[d])); + } + + var s3 = new XYChart.Series(); + s3.name = "max temp"; + for (d in date) { + s3.data.add(new XYChart.Data(date[d], temp_max[d])); + } + + bc.data.addAll(s1, s2, s3); + + stage.scene = new Scene(bc, 800, 600); + stage.show(); +} diff --git a/samples/call_lambda.js b/samples/call_lambda.js new file mode 100644 index 00000000..d865164f --- /dev/null +++ b/samples/call_lambda.js @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 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. + */ + +// nashorn allows you treat every Java8 lambda as a function + +var JFunction = Java.type("java.util.function.Function"); +var obj = new JFunction() { + apply: function(x) { + print(x + ", lambda"); + } +}; + +// prints 'function' +print(typeof obj); + +// call it! +obj("hello"); diff --git a/samples/counters.js b/samples/counters.js index 226ec3de..d89ad581 100644 --- a/samples/counters.js +++ b/samples/counters.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -33,7 +33,11 @@ * This file can be run along with any script you want to run * to print aggregate stat counters from nashorn. * - * Usage: jjs <your-file.js> counters.js + * Usage: jjs -J-Dnashorn.debug <your-file.js> counters.js */ -Debug.dumpCounters(); +if (java.lang.System.getProperty("nashorn.debug") == null) { + print("Usage: jjs -J-Dnashorn.debug <your-file.js> counters.js"); +} else { + Debug.dumpCounters(); +} diff --git a/samples/dirname.js b/samples/dirname.js new file mode 100644 index 00000000..119d4f33 --- /dev/null +++ b/samples/dirname.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014, 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. + */ + +// __DIR__ variable is equivalent of `dirname $0` in +// shell scripts - expands to the directory where +// the current script is located. + +print("This script is in the directory: " + __DIR__); diff --git a/samples/disassemble.js b/samples/disassemble.js new file mode 100644 index 00000000..654b9dd8 --- /dev/null +++ b/samples/disassemble.js @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, 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. + */ + +// Usage: jjs disassemble.js -- <.class-file-path> + +// Simple .class disassembler that uses bundled ObjectWeb ASM +// classes in jdk8. WARNING: Bundled ObjectWeb ASM classes are +// not part of official jdk8 API. It can be changed/removed +// without notice. So, this script is brittle by design! + +// This example demonstrates passing arguments to script +// from jjs command line, nio and ASM usage. + +// classes used +var FileSystems = Java.type("java.nio.file.FileSystems"); +var Files = Java.type("java.nio.file.Files"); +var System = Java.type("java.lang.System"); +var PrintWriter = Java.type("java.io.PrintWriter"); + +// WARNING: uses non-API classes of jdk8! +var ClassReader = Java.type("jdk.internal.org.objectweb.asm.ClassReader"); +var TraceClassVisitor = Java.type("jdk.internal.org.objectweb.asm.util.TraceClassVisitor"); + +// convert file name to Path instance +function path(file) { + return FileSystems.default.getPath(file); +} + +// read all file content as a byte[] +function readAllBytes(file) { + return Files.readAllBytes(path(file)); +} + +// disassemble .class byte[] and prints output to stdout +function disassemble(bytecode) { + var pw = new PrintWriter(System.out); + new ClassReader(bytecode).accept(new TraceClassVisitor(pw), 0); +} + +// check for command line arg (for .class file name) +if (arguments.length == 0 || !arguments[0].endsWith('.class')) { + print("Usage: jjs disassemble -- <.class file>"); + exit(1); +} + +// disassemble the given .class file +disassemble(readAllBytes(arguments[0])); diff --git a/samples/engine/README b/samples/engine/README new file mode 100644 index 00000000..88584955 --- /dev/null +++ b/samples/engine/README @@ -0,0 +1 @@ +Using javax.script engine API of nashorn from script! diff --git a/samples/engine/accessvar.js b/samples/engine/accessvar.js new file mode 100644 index 00000000..ae9ec7de --- /dev/null +++ b/samples/engine/accessvar.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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 example showing global variable access from caller + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// eval code! +engine.eval("x = 'hello'"); + +// access global var from engine +print(engine.get('x')); diff --git a/samples/engine/callfunc.js b/samples/engine/callfunc.js new file mode 100644 index 00000000..76108fd1 --- /dev/null +++ b/samples/engine/callfunc.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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 example showing how to call a global script +// function from caller + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// eval code! +engine.eval("function func(name) { print('I am func, hello ' + name) }"); + +// invoke functions, methods of code evaluated by engine +// from javax.script.Invocable interface. But, hey, +// calling code is JavaScript and don't worry about types :) + +engine.invokeFunction("func", "Nashorn"); diff --git a/samples/engine/callmethod.js b/samples/engine/callmethod.js new file mode 100644 index 00000000..8199dcd6 --- /dev/null +++ b/samples/engine/callmethod.js @@ -0,0 +1,64 @@ +#// Usage: jjs -scripting callmethod.js + +/* + * Copyright (c) 2014, 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 example demonstrating calling a script object +// method from script engine user code + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// eval code - too many script escapes? +// use heredoc ! +engine.eval(<<CODE + var obj = { + func: function() { + print("I am func of " + this); + }, + + toString: function() { + return "Object 'obj'"; + } + }; +CODE); + +// invoke methods of an object in script world +// from javax.script.Invocable interface. But, hey, +// calling code is JavaScript and don't worry about types :) + +// get that script object on which to call a method +var scriptObj = engine.get("obj"); +// call 'func' method on object 'obj' +engine.invokeMethod(scriptObj, "func"); diff --git a/samples/engine/exposevar.js b/samples/engine/exposevar.js new file mode 100644 index 00000000..93f57ed8 --- /dev/null +++ b/samples/engine/exposevar.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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. + */ + +// Example showing how to expose a script global var from caller + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// expose variable to engine +engine.put("name", "Nashorn"); + +// access it from script +engine.eval("print('Hello, ' + name)"); diff --git a/samples/engine/foreignobject.js b/samples/engine/foreignobject.js new file mode 100644 index 00000000..3850089d --- /dev/null +++ b/samples/engine/foreignobject.js @@ -0,0 +1,71 @@ +#// Usage: jjs -scripting foreignobject.js + +/* + * Copyright (c) 2014, 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. + */ + +// cross nashorn engine scripting +// access script objects from other engines in natural syntax + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// eval code! +engine.eval(<<CODE + var obj = { + foo: 42, + func: function() { + print("func: " + this.foo); + } + }; +CODE); + +// Nashorn engine returns script objects as instance of +// the class jdk.nashorn.api.scripting.ScriptObjectMirror +// But nashorn's dynalink linker can treat these objects +// specially to support natural script syntax to access.. +// In Java code, you need to use ScriptObjectMirror's +// methods though. Once again, script world is simpler :-) + +var foreignObj = engine.get("obj"); +// access properties, functions of it +// with natural syntax +print(foreignObj.foo); +foreignObj.func(); +print(typeof foreignObj.func); + +// access engine's global +var foreignGlobal = engine.eval("this"); +// create objects in engine's world from here! +print(new foreignGlobal.Object()); +print(new foreignGlobal.Date()); diff --git a/samples/engine/hello.js b/samples/engine/hello.js new file mode 100644 index 00000000..37fabbfe --- /dev/null +++ b/samples/engine/hello.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, 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 hello world example showing create engine +// and eval simple script + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); +// eval code! +engine.eval("print('hello world')"); diff --git a/samples/engine/interface.js b/samples/engine/interface.js new file mode 100644 index 00000000..75b7aa21 --- /dev/null +++ b/samples/engine/interface.js @@ -0,0 +1,60 @@ +#// Usage: jjs -scripting interface.js + +/* + * Copyright (c) 2014, 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. + */ + +// Example demonstrating how to implement a Java interface +// whose methods are backed by global script functions + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// eval code - too many script escapes? +// use heredoc ! +engine.eval(<<CODE +function run() { + print("run global function called"); +} +CODE); + +// create Java interface object whose methods are +// implemented by script functions. This is from +// javax.script.Invocable. But we are in JS world, +// don't worry about types :) + +var Runnable = Java.type("java.lang.Runnable"); +var r = engine.getInterface(Runnable.class); +print(r.class); + +r.run(); diff --git a/samples/engine/interface2.js b/samples/engine/interface2.js new file mode 100644 index 00000000..771b1101 --- /dev/null +++ b/samples/engine/interface2.js @@ -0,0 +1,63 @@ +#// Usage: jjs -scripting interface2.js + +/* + * Copyright (c) 2014, 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 example demonstrating how to implement java interface +// whose methods are backed by script methods of a script object + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// eval code - too many script escapes? +// use heredoc ! +engine.eval(<<CODE + var obj = { + run: function() { + print("I am run method of 'obj'"); + } + }; +CODE); + +// create Java interface object whose methods are +// implemented by script methods of a script object +// This is from javax.script.Invocable. But we are +// in JS world, don't worry about types :) + +var Runnable = Java.type("java.lang.Runnable"); + +var scriptObj = engine.get("obj"); +var r = engine.getInterface(scriptObj, Runnable.class); +print(r.class); +r.run(); diff --git a/samples/engine/lambda_as_func.js b/samples/engine/lambda_as_func.js new file mode 100644 index 00000000..2600f17a --- /dev/null +++ b/samples/engine/lambda_as_func.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 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 example demonstrating how to expose 'function's +// from embedding code. Any lambda object exposed to engine +// can be called as 'function' in script. + +var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager"); +// create manager +var manager = new ScriptEngineManager(); +// create engine +var engine = manager.getEngineByName("js"); + +// Any lambda (@FunctionalInterface annotated type) object can be +// be exposed from script embedding code. Script can call +// it as a function +engine.put("upper", new java.util.function.Function() { + apply: function(x) x.toUpperCase() +}); + +print(engine.eval("upper('hello')")); diff --git a/samples/env.js b/samples/env.js new file mode 100644 index 00000000..74377ebd --- /dev/null +++ b/samples/env.js @@ -0,0 +1,43 @@ +#// Usage: jjs -scripting env.js + +/* + * Copyright (c) 2014, 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. + */ + +// In nashorn -scripting mode, +// "$ENV" object exposes process +// environment variables + +print($ENV.PATH); +print($ENV.JAVA_HOME); + +for (i in $ENV) { + print(i, "->", $ENV[i]); +} diff --git a/samples/expression_closure.js b/samples/expression_closure.js new file mode 100644 index 00000000..1efb1842 --- /dev/null +++ b/samples/expression_closure.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, 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. + */ + +// nashorn supports expression closures extension of +// Mozilla JavaScript 1.8. See also +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.8 + +// leave {, } and 'return' keyword + +function sqr(x) x*x; + +// prints 289 (= 17*17) +print(sqr(17)); diff --git a/samples/fileline.js b/samples/fileline.js new file mode 100644 index 00000000..05c99759 --- /dev/null +++ b/samples/fileline.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014, 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. + */ + +// nashorn supports pseudo global variables __FILE__ +// and __LINE__ that expands to currently executed +// script file name and current script line number + +// prints current file and line number +print("executing " + __FILE__ + " @ " + __LINE__); diff --git a/samples/fizzbuzz.js b/samples/fizzbuzz.js new file mode 100644 index 00000000..ba8d83d4 --- /dev/null +++ b/samples/fizzbuzz.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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. + */ + +// What is FizzBuzz? http://c2.com/cgi/wiki?FizzBuzzTest + +// Yet another FizzBuzz impl. using Java 8 lambda and stream +// but using Nashorn. This is ECMAScript port of @stuartmarks' +// Java implementation + +var IntStream = Java.type("java.util.stream.IntStream"); + +function ifmod(m, r, f) { + return function(i) { return i % m == 0? r : f(i); } +} + +// pass script function for lambda +IntStream.rangeClosed(1, 100). + mapToObj( + ifmod(15, "FizzBuzz", ifmod(5, "Buzz", ifmod(3, "Fizz", String)))) + .forEach(print); diff --git a/samples/for_each.js b/samples/for_each.js new file mode 100644 index 00000000..10fc39a1 --- /dev/null +++ b/samples/for_each.js @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, 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. + */ + +// nashorn supports for..each extension supported +// by Mozilla. See also +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for_each...in + +var strs = [ "hello", "world" ]; +for each (str in strs) + print(str); + +// create a java int[] object +var JArray = Java.type("int[]"); +var arr = new JArray(10); + +// store squares as values +for (i in arr) + arr[i] = i*i; + +// for .. each on java arrays +print("squares"); +for each (i in arr) + print(i); + +var System = Java.type("java.lang.System"); + +// for..each on java Iterables +// print System properties as name = value pairs +print("System properties"); +for each (p in System.properties.entrySet()) { + print(p.key, "=", p.value); +} + +// print process environment vars as name = value pairs +print("Process environment"); +for each (e in System.env.entrySet()) { + print(e.key, "=", e.value); +} diff --git a/samples/gaussian_random.js b/samples/gaussian_random.js new file mode 100644 index 00000000..f52f49f3 --- /dev/null +++ b/samples/gaussian_random.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, 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 100 Guassian distributed numbers + +var Random = Java.type("java.util.Random"); +var DoubleStream = Java.type("java.util.stream.DoubleStream"); + +var r = new Random(); + +// expression closure (see expression_closure.js as well) +// passed as lambda double generator. "print" passed as +// double consumer lambda to 'forEach' method. + +DoubleStream + .generate(function() r.nextGaussian()) + .limit(100) + .forEach(print); diff --git a/samples/gaussian_random_bind.js b/samples/gaussian_random_bind.js new file mode 100644 index 00000000..052520cd --- /dev/null +++ b/samples/gaussian_random_bind.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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 100 Guassian distributed numbers + +var Random = Java.type("java.util.Random"); +var DoubleStream = Java.type("java.util.stream.DoubleStream"); + +// function as lambda double generator. "print" passed as +// double consumer lambda to 'forEach' method. +// Function.prototype.bind used to attach 'state' for the +// generator function. + +DoubleStream + .generate( + function(r) { + return r.nextGaussian() + }.bind(this, new Random())) + .limit(100) + .forEach(print); diff --git a/samples/gutenberg.js b/samples/gutenberg.js new file mode 100644 index 00000000..4d83a573 --- /dev/null +++ b/samples/gutenberg.js @@ -0,0 +1,142 @@ +#// Usage: jjs -scripting gutenberg.js + +/* + * Copyright (c) 2014, 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 example that demonstrates reading XML Rss feed +// to generate a HTML file from script and show it by browser + +// Java classes used +var Characters = Java.type("javax.xml.stream.events.Characters"); +var Factory = Java.type("javax.xml.stream.XMLInputFactory"); +var File = Java.type("java.io.File"); +var FileWriter = Java.type("java.io.FileWriter"); +var PrintWriter = Java.type("java.io.PrintWriter"); +var URL = Java.type("java.net.URL"); + +// read Rss feed from a URL. Returns an array +// of objects having only title and link properties +function readRssFeed(url) { + var fac = Factory.newInstance(); + var reader = fac.createXMLEventReader(url.openStream()); + + // get text content from next event + function getChars() { + var result = ""; + var e = reader.nextEvent(); + if (e instanceof Characters) { + result = e.getData(); + } + return result; + } + + var items = []; + var title, link; + var inItem = false; + while (reader.hasNext()) { + var evt = reader.nextEvent(); + if (evt.isStartElement()) { + var local = evt.name.localPart; + if (local == "item") { + // capture title, description now + inItem = true; + } + + if (inItem) { + switch (local) { + case 'title': + title = getChars(); + break; + case 'link': + link = getChars(); + break; + } + } + } else if (evt.isEndElement()) { + var local = evt.name.localPart; + if (local == "item") { + // one item done, save it in result array + items.push({ title: title, link: link }); + inItem = false; + } + } + } + + return items; +} + +// generate simple HTML for an RSS feed +function getBooksHtml() { + var url = new URL("http://www.gutenberg.org/cache/epub/feeds/today.rss"); + var items = readRssFeed(url); + + var str = "<ul>"; + + // Nashorn's string interpolation and heredoc + // support is very handy in generating text content + // that is filled with elements from runtime objects. + // We insert title and link in <li> elements here. + for each (i in items) { + str += <<EOF +<li> + <a href="${i.link}">${i.title}</a> +</li> +EOF + } + str += "</ul>"; + return str; +} + +// write the string to the given file +function writeTo(file, str) { + var w = new PrintWriter(new FileWriter(file)); + try { + w.print(str); + } finally { + w.close(); + } +} + +// generate books HTML +var str = getBooksHtml(); + +// write to file. __DIR__ is directory where +// this script is stored. +var file = new File(__DIR__ + "books.html"); +writeTo(file, str); + +// show it by desktop browser +try { + var Desktop = Java.type("java.awt.Desktop"); + Desktop.desktop.browse(file.toURI()); +} catch (e) { + print(e); +} diff --git a/samples/heredoc.js b/samples/heredoc.js new file mode 100644 index 00000000..0c52ca93 --- /dev/null +++ b/samples/heredoc.js @@ -0,0 +1,51 @@ +#// Usage: jjs -scripting heredoc.js + +/* + * Copyright (c) 2014, 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. + */ + +// Nashorn supports Shell script like here-documents +// in -scripting mode. Here-docs are multi-line strings +// that are possibly interpolated with ${} expressions +// See also http://en.wikipedia.org/wiki/Here_document + +var sender = "Buffy the Vampire Slayer"; +var recipient = "Spike"; + +print(<<END + +Dear ${recipient}, + +I wish you to leave Sunnydale and never return. + +Not Quite Love, +${sender} + +END); diff --git a/samples/interface_impl.js b/samples/interface_impl.js new file mode 100644 index 00000000..5e498166 --- /dev/null +++ b/samples/interface_impl.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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. + */ + +// nashorn supports Java interface implementation +// by script using anonymous class-like syntax + +var Runnable = Java.type("java.lang.Runnable"); +var Thread = Java.type("java.lang.Thread"); +// use anonymous class-like new syntax +var r = new Runnable() { + run: function() { + print("I am a runnable " + Thread.currentThread()); + } +} + +r.run(); + +var t = new Thread(r); +t.start(); +t.join(); diff --git a/samples/javaastviewer.js b/samples/javaastviewer.js new file mode 100644 index 00000000..c446abe5 --- /dev/null +++ b/samples/javaastviewer.js @@ -0,0 +1,202 @@ +#// Usage: jjs -fx javaastviewer.js -- <.java files> + +/* + * Copyright (c) 2014, 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 also uses +// -fx and javafx TreeView to visualize Java AST as TreeView + +if (!$OPTIONS._fx || arguments.length == 0) { + print("Usage: jjs -fx javaastviewer.js -- <.java files>"); + exit(1); +} + +// Java types used +var Enum = Java.type("java.lang.Enum"); +var HashSet = Java.type("java.util.HashSet"); +var Name = Java.type("javax.lang.model.element.Name"); +var List = Java.type("java.util.List"); +var Set = Java.type("java.util.Set"); +var SimpleTreeVisitor = Java.type("com.sun.source.util.SimpleTreeVisitor"); +var StringArray = Java.type("java.lang.String[]"); +var ToolProvider = Java.type("javax.tools.ToolProvider"); +var Tree = Java.type("com.sun.source.tree.Tree"); + +function javaASTToScriptObject(args) { + // properties ignored (javac implementation class properties) in AST view. + // may not be exhaustive - any getAbc would become "abc" property or + // public field becomes a property of same name. + var ignoredProps = new HashSet(); + for each (var word in + ['extending', 'implementing', 'init', 'mods', 'clazz', 'defs', + 'expr', 'tag', 'preferredPosition', 'qualid', 'recvparam', + 'restype', 'params', 'startPosition', 'thrown', + 'tree', 'typarams', 'typetag', 'vartype']) { + ignoredProps.add(word); + } + + // get the system compiler tool + var compiler = ToolProvider.systemJavaCompiler; + + // get standard file manager + var fileMgr = compiler.getStandardFileManager(null, null, null); + + // make a list of compilation unit from command line argument file names + // Using Java.to convert script array (arguments) to a Java String[] + var compUnits = fileMgr.getJavaFileObjects(Java.to(args, StringArray)); + + // create a new compilation task + var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); + + // subclass SimpleTreeVisitor - converts Java AST node to + // a simple script object by walking through it + var ConverterVisitor = Java.extend(SimpleTreeVisitor); + + var visitor = new ConverterVisitor() { + // convert java AST node to a friendly script object + // which can be viewed. Every node ends up in defaultAction + // method of SimpleTreeVisitor method. + + defaultAction: function (node, p) { + var resultObj = {}; + // Nashorn does not iterate properties and methods of Java objects + // But, we can bind properties of any object (including java objects) + // to a script object and iterate it! + var obj = {}; + Object.bindProperties(obj, node); + + // we don't want every property, method of java object + for (var prop in obj) { + var val = obj[prop]; + var type = typeof val; + // ignore 'method' members + if (type == 'function' || type == 'undefined') { + continue; + } + + // ignore properties from Javac implementation + // classes - hack by name!! + if (ignoredProps.contains(prop)) { + continue; + } + + // subtree - recurse it + if (val instanceof Tree) { + resultObj[prop] = visitor.visit(val, p); + } else if (val instanceof List) { + // List of trees - recurse each and make an array + var len = val.size(); + if (len != 0) { + var arr = []; + for (var j = 0; j < len; j++) { + var e = val[j]; + if (e instanceof Tree) { + arr.push(visitor.visit(e, p)); + } + } + resultObj[prop] = arr; + } + } else if (val instanceof Set) { + // Set - used for modifier flags + // make array + var len = val.size(); + if (len != 0) { + var arr = []; + for each (var e in val) { + if (e instanceof Enum || typeof e == 'string') { + arr.push(e.toString()); + } + } + resultObj[prop] = arr; + } + } else if (val instanceof Enum || val instanceof Name) { + // make string for any Enum or Name + resultObj[prop] = val.toString(); + } else if (type != 'object') { + // primitives 'as is' + resultObj[prop] = val; + } + } + return resultObj; + } + } + + // top level object with one property for each compilation unit + var scriptObj = {}; + for each (var cu in task.parse()) { + scriptObj[cu.sourceFile.name] = cu.accept(visitor, null); + } + + return scriptObj; +} + +// JavaFX classes used +var StackPane = Java.type("javafx.scene.layout.StackPane"); +var Scene = Java.type("javafx.scene.Scene"); +var TreeItem = Java.type("javafx.scene.control.TreeItem"); +var TreeView = Java.type("javafx.scene.control.TreeView"); + +// Create a javafx TreeItem to view a script object +function treeItemForObject(obj, name) { + var item = new TreeItem(name); + for (var prop in obj) { + var node = obj[prop]; + if (typeof node == 'object') { + if (node == null) { + // skip nulls + continue; + } + var subitem = treeItemForObject(node, prop); + } else { + var subitem = new TreeItem(prop + ": " + node); + } + item.children.add(subitem); + } + + item.expanded = true; + return item; +} + +var commandArgs = arguments; + +// JavaFX start method +function start(stage) { + var obj = javaASTToScriptObject(commandArgs); + stage.title = "Java AST Viewer" + var rootItem = treeItemForObject(obj, "AST"); + rootItem.expanded = true; + var tree = new TreeView(rootItem); + var root = new StackPane(); + root.children.add(tree); + stage.scene = new Scene(root, 300, 450); + stage.show(); +} diff --git a/samples/javacastcounter.js b/samples/javacastcounter.js new file mode 100644 index 00000000..607b2f82 --- /dev/null +++ b/samples/javacastcounter.js @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014, 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. + */ + +// Usage: jjs javacastcounter.js -- <.java files> + +// This example demonstrates Nashorn Java.extend API +// to subclass a Java class from script. + +// This example uses Javac Compiler and Tree API +// to list type casts used in java source files. + +if (arguments.length == 0) { + print("Usage: jjs javacastcounter.js -- <.java files>"); + exit(1); +} + +// Java types used +var ToolProvider = Java.type("javax.tools.ToolProvider"); +var TreeScanner = Java.type("com.sun.source.util.TreeScanner"); +var Trees = Java.type("com.sun.source.util.Trees"); +var StringArray = Java.type("java.lang.String[]"); + +// get the system compiler tool +var compiler = ToolProvider.systemJavaCompiler; + +// get standard file manager +var fileMgr = compiler.getStandardFileManager(null, null, null); + +// make a list of compilation unit from command line argument file names +// 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 TreeScanner class +var CastCounter = Java.extend(TreeScanner); + +var counter = new CastCounter() { + // 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(counter).visitCompilationUnit(node, p); + }, + + visitTypeCast: function(node, p) { + // print information on this type cast node + var pos = sourcePositions.getStartPosition(this.compUnit, node); + var line = this.lineMap.getLineNumber(pos); + var col = this.lineMap.getColumnNumber(pos); + print(node + " @ " + this.fileName + ":" + line + ":" + col); + + // count one more type cast + return 1; + }, + + reduce: function(r1, r2) { + return (r1 == null ? 0 : r1) + (r2 == null ? 0 : r2); + } +}; + +// print total number of type cast nodes seen +print("Total casts:", counter.scan(task.parse(), null)); diff --git a/samples/javaimporter.js b/samples/javaimporter.js new file mode 100644 index 00000000..dd487ec1 --- /dev/null +++ b/samples/javaimporter.js @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 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. + */ + +// JavaImporter along with 'with' statement helps in +// localized Java class references + +function readTextFromURL(url) { + + // equivalent to + // + // import java.io.*; + // import java.net.*; + // import java.lang.StringBuffer; + // + // only inside the 'with' statement + with (new JavaImporter(java.io, + java.net, + java.lang.StringBuilder)) { + var buf = new StringBuilder(); + var u = new URL(url); + var reader = new BufferedReader( + new InputStreamReader(u.openStream())); + var line = null; + try { + while ((line = reader.readLine()) != null) + buf.append(line).append('\n'); + } finally { + reader.close(); + } + + return buf.toString(); + } +} + +print(readTextFromURL("https://twitter.com/")); diff --git a/samples/javalist.js b/samples/javalist.js new file mode 100644 index 00000000..8f3749b5 --- /dev/null +++ b/samples/javalist.js @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 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 List elements accessed/modified via +// array element access/update syntax + +var ArrayList = Java.type("java.util.ArrayList"); +var list = new ArrayList(); + +// add elements to list by List's add method calls +list.add("js"); +list.add("ecmascript"); +list.add("nashorn"); + +// get by List's get(int) method +print(list[0]); +print(list[1]); +print(list[2]); + +// access list elements by indexed access as well +print(list[0]); +print(list[1]); +print(list[2]); + +// assign to list elements by index as well +list[0] = list[0].toUpperCase(); +list[1] = list[1].toUpperCase(); +list[2] = list[2].toUpperCase(); + +print(list.get(0)); +print(list.get(1)); +print(list.get(2)); +print(list[0]); +print(list[1]); +print(list[2]); diff --git a/samples/javamap.js b/samples/javamap.js new file mode 100644 index 00000000..a53d0320 --- /dev/null +++ b/samples/javamap.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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 Map keys as properties + +// Demonstrating Java Map key/value can be accessed +// as property/value from script. + +var HashMap = Java.type("java.util.HashMap"); +var map = new HashMap(); + +// map key-value access by java get/put method calls +map.put('js', 'nashorn'); +print(map.get('js')); + +// access keys of map as properties +print(map['js']); +print(map.js); + +// also assign new key-value pair +// as 'property-value' +map['language'] = 'java'; +print(map.get("language")); +print(map.language); +print(map['language']); + +map.answer = 42; +print(map.get("answer")); +print(map.answer); +print(map['answer']); diff --git a/samples/javashell.js b/samples/javashell.js new file mode 100644 index 00000000..65d1a8a2 --- /dev/null +++ b/samples/javashell.js @@ -0,0 +1,146 @@ +#// Usage: jjs -scripting javashell.js + +/* + * Copyright (c) 2014, 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 Java "shell" with which you can try out +// your few liner Java code leaving imports, main etc. +// And you can leave even compilation as this script +// takes care boilerplate+compile step for you. + +// Java types used +var Arrays = Java.type("java.util.Arrays"); +var BufferedReader = Java.type("java.io.BufferedReader"); +var FileWriter = Java.type("java.io.FileWriter"); +var LocalDateTime = Java.type("java.time.LocalDateTime"); +var InputStreamReader = Java.type("java.io.InputStreamReader"); +var PrintWriter = Java.type("java.io.PrintWriter"); +var ProcessBuilder = Java.type("java.lang.ProcessBuilder"); +var System = Java.type("java.lang.System"); + +// read multiple lines of input from stdin till user +// enters an empty line +function input(endMarker, prompt) { + if (!endMarker) { + endMarker = ""; + } + + if (!prompt) { + prompt = " >> "; + } + + var str = ""; + var reader = new BufferedReader(new InputStreamReader(System.in)); + var line; + while (true) { + System.out.print(prompt); + line = reader.readLine(); + if (line == null || line == endMarker) { + break; + } + str += line + "\n"; + } + return str; +} + +// write the string to the given file +function writeTo(file, str) { + var w = new PrintWriter(new FileWriter(file)); + try { + w.print(str); + } finally { + w.close(); + } +} + +// generate Java code with user's input +// put inside generated main method +function generate(className) { + var usercode = input(); + if (usercode == "") { + return false; + } + + var fullcode = <<EOF +// userful imports, add more here if you want +// more imports. +import static java.lang.System.*; +import java.io.*; +import java.net.*; +import java.math.*; +import java.nio.file.*; +import java.time.*; +import java.time.chrono.*; +import java.time.format.*; +import java.time.temporal.*; +import java.time.zone.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.stream.*; + +public class ${className} { + public static void main(String[] args) throws Exception { + ${usercode} + } +} +EOF + + writeTo("${className}.java", fullcode); + return true; +} + +// execute code command +function exec(args) { + // build child process and start it! + new ProcessBuilder(Arrays.asList(args.split(' '))) + .inheritIO() + .start() + .waitFor(); +} + +// generate unique name +function uniqueName() { + var now = LocalDateTime.now().toString(); + // replace unsafe chars with '_' + return "JavaShell" + now.replace(/-|:|\./g, '_'); +} + +// read-compile-run loop +while(true) { + var className = uniqueName(); + if (generate(className)) { + exec("javac ${className}.java"); + exec("java ${className}"); + } else { + break; + } +} diff --git a/samples/jsadapter_dom.js b/samples/jsadapter_dom.js new file mode 100644 index 00000000..e86c6d2f --- /dev/null +++ b/samples/jsadapter_dom.js @@ -0,0 +1,189 @@ +#// Usage: jjs -scripting jsadapter_dom.js + +/* + * Copyright (c) 2014, 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 example that demonstrates reading XML Rss feed +// to generate a HTML file from script and show it by browser +// Uses XML DOM parser and DOM element wrapped by script +// "proxy" (JSAdapter constructor) + +// Java classes used +var DocBuilderFac = Java.type("javax.xml.parsers.DocumentBuilderFactory"); +var Node = Java.type("org.w3c.dom.Node"); +var File = Java.type("java.io.File"); +var FileWriter = Java.type("java.io.FileWriter"); +var PrintWriter = Java.type("java.io.PrintWriter"); + +// constants from Node class +var ELEMENT_NODE = Node.ELEMENT_NODE; +var TEXT_NODE = Node.TEXT_NODE; + +// parse XML from uri and return Document +function parseXML(uri) { + var docBuilder = DocBuilderFac.newInstance().newDocumentBuilder(); + return docBuilder["parse(java.lang.String)"](uri); +} + +// get child Elements of given name of the parent element given +function getChildElements(elem, name) { + var nodeList = elem.childNodes; + var childElems = []; + var len = nodeList.length; + for (var i = 0; i < len; i++) { + var node = nodeList.item(i); + if (node.nodeType == ELEMENT_NODE && + node.tagName == name) { + childElems.push(wrapElement(node)); + } + } + + return childElems; +} + +// get concatenated child text content of an Element +function getElemText(elem) { + var nodeList = elem.childNodes; + var len = nodeList.length; + var text = ''; + for (var i = 0; i < len; i++) { + var node = nodeList.item(i); + if (node.nodeType == TEXT_NODE) { + text += node.nodeValue; + } + } + + return text; +} + +// Wrap DOM Element object as a convenient script object +// using JSAdapter. JSAdapter is like java.lang.reflect.Proxy +// in that it allows property access, method calls be trapped +// by 'magic' methods like __get__, __call__. +function wrapElement(elem) { + if (! elem) { + return elem; + } + return new JSAdapter() { + // getter to expose child elements and attributes by name + __get__: function(name) { + if (typeof name == 'string') { + if (name.startsWith('@')) { + var attr = elem.getAttributeNode(name.substring(1)); + return !attr? undefined : attr.value; + } + + var arr = getChildElements(elem, name); + if (arr.length == 1) { + // single child element, expose as single element + return arr[0]; + } else { + // multiple children of given name, expose as array + return arr; + } + } + return undefined; + }, + + __call__: function(name) { + // toString override to get text content of this Element + if (name == 'toString' || name == 'valueOf') { + return getElemText(elem); + } + return undefined; + } + } +} + +// generate HTML using here-doc and string interpolation +function getBooksHtml() { + var doc = parseXML("http://www.gutenberg.org/cache/epub/feeds/today.rss"); + // wrap document root Element as script convenient object + var rss = wrapElement(doc.documentElement); + print("rss file version " + rss['@version']); + + var str = <<HEAD + +<html> +<title>${rss.channel.title}</title> +<body> +<h1>${rss.channel.description}</h1> +<p> +Published on ${rss.channel.pubDate} +</p> + +HEAD + + var items = rss.channel.item; + for each (var i in items) { + str += <<LIST + +<dl> +<dt><a href="${i.link}">${i.title}</a></dt> +<dd>${i.description}</dd> +</dl> + +LIST + } + str += <<END + +</body> +</html> + +END + return str; +} + +// write the string to the given file +function writeTo(file, str) { + var w = new PrintWriter(new FileWriter(file)); + try { + w.print(str); + } finally { + w.close(); + } +} + +// generate books HTML +var str = getBooksHtml(); + +// write to file. __DIR__ is directory where +// this script is stored. +var file = new File(__DIR__ + "books.html"); +writeTo(file, str); + +// show it by desktop browser +try { + var Desktop = Java.type("java.awt.Desktop"); + Desktop.desktop.browse(file.toURI()); +} catch (e) { + print(e); +} diff --git a/samples/jsobject.js b/samples/jsobject.js new file mode 100644 index 00000000..48434d7d --- /dev/null +++ b/samples/jsobject.js @@ -0,0 +1,75 @@ +#// Usage: jjs -scripting -cp . jsobject.js + +/* + * Copyright (c) 2014, 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 sample demonstrats how to expose a +// script friendly object from your java code +// by implementing jdk.nashorn.api.scripting.JSObject + +// compile the java program +`javac BufferArray.java`; + +// print error, if any and exit +if ($ERR != '') { + print($ERR); + exit($EXIT); +} + +// create BufferArray +var BufferArray = Java.type("BufferArray"); +var bb = new BufferArray(10); + +// 'magic' methods called to retrieve set/get +// properties on BufferArray instance +var len = bb.length; +print("bb.length = " + len) +for (var i = 0; i < len; i++) { + bb[i] = i*i; +} + +for (var i = 0; i < len; i++) { + print(bb[i]); +} + +// get underlying buffer by calling a method +// on BufferArray magic object + +// 'buf' is a function member +print(typeof bb.buf); +var buf = bb.buf(); + +// use retrieved underlying nio buffer +var cap = buf.capacity(); +print("buf.capacity() = " + cap); +for (var i = 0; i < cap; i++) { + print(buf.get(i)); +} diff --git a/samples/jsobject_mapreduce.js b/samples/jsobject_mapreduce.js new file mode 100644 index 00000000..ff34940b --- /dev/null +++ b/samples/jsobject_mapreduce.js @@ -0,0 +1,62 @@ +#// Usage: jjs -scripting -cp . jsobject_mapreduce.js + +/* + * Copyright (c) 2014, 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. + */ + +// Many Array.prototype functions such as map, +// filter, reduce, reduceRight, every, some are generic. +// These functions accept ECMAScript array as well as +// many array-like objects including JSObjects. +// See also http://en.wikipedia.org/wiki/MapReduce + +`javac BufferArray.java`; + +var BufferArray = Java.type("BufferArray"); +var buf = new BufferArray(10); + +var map = Array.prototype.map; +var filter = Array.prototype.filter; +var reduce = Array.prototype.reduce; + +// make random list of numbers +for (var i = 0; i < 10; i++) + buf[i] = Math.random(); + +var forEach = Array.prototype.forEach; +// print numbers in the list +forEach.call(buf, function(x) print(x)); + +// print sum of squares of the random numbers +print("Square sum:", + reduce.call( + map.call(buf, function(x) x*x), + function(x, y) x + y) +); diff --git a/samples/jsonviewer.js b/samples/jsonviewer.js new file mode 100644 index 00000000..804fef50 --- /dev/null +++ b/samples/jsonviewer.js @@ -0,0 +1,120 @@ +#// Usage: jjs -fx jsonviewer.js +// or +// jjs -fx jsonviewer.js -- <url-of-json-doc> + +/* + * Copyright (c) 2014, 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. + */ + +if (! $OPTIONS._fx) { + print("Usage: jjs -fx jsonviewer.js -- <url-of-json-doc>"); + exit(1); +} + +// This example downloads a JSON file from a URL and +// shows the same as a JavaFX tree view. + +// Using JavaFX from Nashorn. See also: +// http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/javafx.html + +// JavaFX classes used +var StackPane = Java.type("javafx.scene.layout.StackPane"); +var Scene = Java.type("javafx.scene.Scene"); +var TreeItem = Java.type("javafx.scene.control.TreeItem"); +var TreeView = Java.type("javafx.scene.control.TreeView"); + +// read text content of a URL +function readTextFromURL(url) { + // equivalent to + // + // import java.io.*; + // import java.net.*; + // import java.lang.StringBuffer; + // + // only inside the 'with' statement + with (new JavaImporter(java.io, + java.net, + java.lang.StringBuilder)) { + var buf = new StringBuilder(); + var u = new URL(url); + var reader = new BufferedReader( + new InputStreamReader(u.openStream())); + var line = null; + try { + while ((line = reader.readLine()) != null) + buf.append(line).append('\n'); + } finally { + reader.close(); + } + + return buf.toString(); + } +} + +// Create a javafx TreeItem to view a script object +function treeItemForObject(obj, name) { + var item = new TreeItem(name); + for (var prop in obj) { + var node = obj[prop]; + if (typeof node == 'object') { + if (node == null) { + // skip nulls + continue; + } + + if (Array.isArray(node) && node.length == 0) { + // skip empty arrays + continue; + } + + var subitem = treeItemForObject(node, prop); + } else { + var subitem = new TreeItem(prop + ": " + node); + } + item.children.add(subitem); + } + return item; +} + +var DEFAULT_URL = "http://api.openweathermap.org/data/2.5/forecast/daily?q=Chennai&mode=json&units=metric&cnt=7`"; + +var url = arguments.length == 0? DEFAULT_URL : arguments[0]; +var obj = JSON.parse(readTextFromURL(url)); + +// JavaFX start method +function start(stage) { + stage.title = "JSON Viewer"; + var rootItem = treeItemForObject(obj, url); + var tree = new TreeView(rootItem); + var root = new StackPane(); + root.children.add(tree); + stage.scene = new Scene(root, 300, 450); + stage.show(); +} diff --git a/samples/letter.js b/samples/letter.js index cbee92d0..304bf1fb 100644 --- a/samples/letter.js +++ b/samples/letter.js @@ -1,3 +1,5 @@ +#// Usage: jjs -scripting letter.js -- <sender> <recipient> + /* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * diff --git a/samples/list_mapreduce.js b/samples/list_mapreduce.js new file mode 100644 index 00000000..033f4d76 --- /dev/null +++ b/samples/list_mapreduce.js @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, 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. + */ + +// Usage: jjs list_mapreduce.js + +// Many Array.prototype functions such as map, +// filter, reduce, reduceRight, every, some are generic. +// These functions accept ECMAScript array as well as +// many array-like objects including java.util.ArrayLists. +// So, you can do map/filter/reduce with Java streams or +// you can also use Array.prototype functions as below. +// See also http://en.wikipedia.org/wiki/MapReduce + +var ArrayList = Java.type("java.util.ArrayList"); +var list = new ArrayList(); +list.add("nashorn"); +list.add("ecmascript"); +list.add("javascript"); +list.add("js"); +list.add("scheme"); + +var map = Array.prototype.map; +var filter = Array.prototype.filter; +var reduce = Array.prototype.reduce; + +// sum of word lengths +print("Sum word length:", + reduce.call( + map.call(list, function(x) x.length), + function(x, y) x + y) +); + +// filter use to filter out "j*" and concatenate rest with ":" +// after uppercasing all strings +print( + reduce.call( + map.call( + filter.call(list, function(x) !x.startsWith("j")), + function(x) x.toUpperCase()), + function(x, y) x + ":" + y) +); + +// another list example involving numbers +list.clear(); +// make random list of numbers +for (var i = 0; i < 10; i++) + list.add(Math.random()); + +var forEach = Array.prototype.forEach; +// print numbers in the list +forEach.call(list, function(x) print(x)); + +// print sum of squares of the random numbers +print("Square sum:", + reduce.call( + map.call(list, function(x) x*x), + function(x, y) x + y) +); diff --git a/samples/locales.js b/samples/locales.js new file mode 100644 index 00000000..ae42f969 --- /dev/null +++ b/samples/locales.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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 program that lists available locals. This is ECMAScript +// port of Java example by @brunoborges + +// Java classes used +var Arrays = Java.type("java.util.Arrays"); +var Collectors = Java.type("java.util.stream.Collectors"); +var JString = Java.type("java.lang.String"); +var Locale = Java.type("java.util.Locale"); + +var formatStr = "Country : %s \t\t\t\t:\t Country Code : %s"; + +// Nashorn allows script functions to be passed +// whereever Java8 lambdas are expected. + +// Nashorn also supports "expression closures" supported by +// Mozilla JavaScript 1.8 version. See also +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.8 + +// The following prints locales in (country) display name order +var list = Arrays.asList(Locale.getISOCountries()) + .stream() + .map(function(x) new Locale("", x)) + .sorted(function(c0, c1) c0.displayCountry.compareTo(c1.displayCountry)) + .map(function(l) JString.format(formatStr, l.displayCountry, l.country)) + .collect(Collectors.toList()); + +list.forEach(print); diff --git a/samples/logisticmap.js b/samples/logisticmap.js new file mode 100644 index 00000000..74198475 --- /dev/null +++ b/samples/logisticmap.js @@ -0,0 +1,82 @@ +#// Usage: jjs -fx -scripting logisticmap.js -- <initial_x> <R> + +/* + * Copyright (c) 2014, 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. + */ + +// Logistic map viewer using Java8 Streams and JavaFX +// See also http://en.wikipedia.org/wiki/Logistic_map + +if (!$OPTIONS._fx || arguments.length < 2) { + print("Usage: jjs -fx -scripting logisticmap.js -- <initial_x> <R>"); + exit(1); +} + +// parameters for the logistic map +var x = parseFloat(arguments[0]); +var R = parseFloat(arguments[1]); +var NUM_POINTS = arguments.length > 2? parseFloat(arguments[2]) : 20; + +// Java classes used +var DoubleStream = Java.type('java.util.stream.DoubleStream'); +var LineChart = Java.type("javafx.scene.chart.LineChart"); +var NumberAxis = Java.type("javafx.scene.chart.NumberAxis"); +var Scene = Java.type("javafx.scene.Scene"); +var Stage = Java.type("javafx.stage.Stage"); +var XYChart = Java.type("javafx.scene.chart.XYChart"); + +function start(stage) { + stage.title = "Logistic Map: initial x = ${x}, R = ${R}"; + // make chart + var xAxis = new NumberAxis(); + var yAxis = new NumberAxis(); + var lineChart = new LineChart(xAxis, yAxis); + xAxis.setLabel("iteration"); + yAxis.setLabel("x"); + // make chart data series + var series = new XYChart.Series(); + var data = series.data; + // populate data using logistic iteration + var i = 0; + DoubleStream + .generate(function() x = R*x*(1-x)) + .limit(NUM_POINTS) + .forEach( + function(value) { + data.add(new XYChart.Data(i, value)); + i++; + } + ); + // add to stage + var scene = new Scene(lineChart, 800, 600); + lineChart.data.add(series); + stage.scene = scene; + stage.show(); +} diff --git a/samples/options.js b/samples/options.js new file mode 100644 index 00000000..3a10346c --- /dev/null +++ b/samples/options.js @@ -0,0 +1,37 @@ +#// Usage: jjs -scripting options.js + +/* + * Copyright (c) 2014, 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 option names and values +for (i in $OPTIONS) { + print(i, '=', $OPTIONS[i]); +} diff --git a/samples/readLine.js b/samples/readLine.js new file mode 100644 index 00000000..9807d3b5 --- /dev/null +++ b/samples/readLine.js @@ -0,0 +1,38 @@ +#// Usage: jjs -scripting greeting.js + +/* + * Copyright (c) 2014, 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. + */ + +// readLine prints prompt and reads user response +var name = readLine("Your name please: "); + +// user name is interpolated within string +print("Hello ${name}"); diff --git a/samples/sam_function.js b/samples/sam_function.js new file mode 100644 index 00000000..84064732 --- /dev/null +++ b/samples/sam_function.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, 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. + */ + +// nashorn supports passing script functions whenever +// a SAM (single abstract method) type object is expected + +var System = Java.type("java.lang.System"); +var Timer = Java.type("java.util.Timer"); +var timer = new Timer(); + +// schedule method accepts java.util.TimerTask +// which is a single-abstract-method type. you +// can pass a script function and nashorn will +// wrap it as SAM implementor. + +timer.schedule(function() { + print("Hello World!"); +}, 1000); + +// wait for timer thread to print by +// reading from stdin. +print("press any key to exit after message from timer..."); +System.in.read(); diff --git a/samples/shell.js b/samples/shell.js index fc4ce9aa..93965387 100644 --- a/samples/shell.js +++ b/samples/shell.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -29,50 +29,53 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** - * This is a simple shell tool in JavaScript. +// Usage: jjs shell.js + +/* This is a simple shell tool in JavaScript. * * Runs any operating system command using Java "exec". When "eval" command is * used, evaluates argument(s) as JavaScript code. */ -var imports = new JavaImporter(java.io, java.lang, java.util); +(function() { + // Java classes used + var Arrays = Java.type("java.util.Arrays"); + var BufferedReader = Java.type("java.io.BufferedReader"); + var InputStreamReader = Java.type("java.io.InputStreamReader"); + var ProcessBuilder = Java.type("java.lang.ProcessBuilder"); + var System = Java.type("java.lang.System"); -function prompt() { - java.lang.System.out.print(">"); -} + // print prompt + function prompt() { + System.out.print("> "); + } -with (imports) { - var reader = new BufferedReader(new InputStreamReader(System["in"])); - var line = null; + var reader = new BufferedReader(new InputStreamReader(System.in)); prompt(); - while ((line = reader.readLine()) != null) { - if (line != "") { - var args = line.split(" "); + // read and evaluate each line from stdin + reader.lines().forEach(function(line) { + if (! line.isEmpty()) { + var args = line.split(' '); try { - if (args[0] == "eval") { - var code = line.substring("eval".length); + // special 'eval' command to evaluate JS code + if (args[0] == 'eval') { + var code = line.substring('eval'.length); var res = eval(code); if (res != undefined) { print(res); } } else { - var argList = new ArrayList(); - for (i in args) { argList.add(args[i]); } - var procBuilder = new ProcessBuilder(argList); - procBuilder.redirectErrorStream(); - var proc = procBuilder.start(); - var out = new BufferedReader(new InputStreamReader(proc.getInputStream())); - var line = null; - while ((line = out.readLine()) != null) { - System.out.println(line); - } - proc.waitFor(); + // build child process and start it! + new ProcessBuilder(Arrays.asList(args)) + .inheritIO() + .start() + .waitFor(); } } catch (e) { + // print exception, if any print(e); } } prompt(); - } -} + }) +})() diff --git a/samples/stack.js b/samples/stack.js new file mode 100644 index 00000000..295ad20f --- /dev/null +++ b/samples/stack.js @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, 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. + */ + +// nashorn supports 'stack' property on ECMAScript +// error objects. This property's value is a string +// that shows script stack trace. + +function g() { + throw new Error("wrong"); +} + +function f() { + g(); +} + +// Output looks something like: +// +// Error: wrong +// at g (stack.js:37) +// at f (stack.js:41) +// at <program> (stack.js:52) + +try { + f(); +} catch (e) { + print(e.stack); +} diff --git a/samples/test.js b/samples/test.js index ecdaf846..1a7e8061 100644 --- a/samples/test.js +++ b/samples/test.js @@ -30,4 +30,3 @@ */ print("Hello World"); - diff --git a/samples/uniform_random.js b/samples/uniform_random.js new file mode 100644 index 00000000..a82cd0bb --- /dev/null +++ b/samples/uniform_random.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 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. + */ + +// generate/print 100 uniformly distributed random values +// and print summary statistics on it + +var DoubleStream = Java.type("java.util.stream.DoubleStream"); + +// pass script function when a lambda is required +// Math.random passed here for double generator lambda +// print passed to forEach method + +DoubleStream + .generate(Math.random) + .limit(100) + .forEach(print); + +print(DoubleStream + .generate(Math.random) + .limit(100) + .summaryStatistics()); diff --git a/samples/uniq.js b/samples/uniq.js index 3da8480b..cbd2c34d 100644 --- a/samples/uniq.js +++ b/samples/uniq.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -29,27 +29,28 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** - * Prints unique lines from a given file. - */ +// Usage: jjs uniq.js +// or: jjs uniq.js -- <file> -if (arguments.length != 1) { - print("Usage: jjs uniq.js -- <file>"); - java.lang.System.exit(1); -} +// omit repeated lines and print unique lines -var imports = new JavaImporter(java.io); +var BufferedReader = Java.type("java.io.BufferedReader"); +var FileReader = Java.type("java.io.FileReader"); +var InputStreamReader = Java.type("java.io.InputStreamReader"); +var System = Java.type("java.lang.System"); +// use object as set - but insertion order preserved var uniqueLines = {}; -with (imports) { - var reader = new BufferedReader(new FileReader(arguments[0])); - while ((line = reader.readLine()) != null) { - // using a JS object as a map... - uniqueLines[line] = true; - } -} +var reader = arguments.length > 0 ? + new FileReader(arguments[0]) : + new InputStreamReader(System.in); +reader = new BufferedReader(reader); + +// add unique lines +reader.lines().forEach(function(line) { + uniqueLines[line] = true; +}) -// now print the collected lines -for (i in uniqueLines) { - print(i); +for (line in uniqueLines) { + print(line); } diff --git a/samples/uniqs.js b/samples/uniqs.js new file mode 100644 index 00000000..57eb7c52 --- /dev/null +++ b/samples/uniqs.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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. + */ + +// Usage: jjs uniqs.js -- <file> +// omit repeated lines and print unique lines +// But this version uses Stream API + +if (arguments.length < 1) { + print("Usage: jjs uniqs.js -- <file>"); + exit(1); +} + +var Files = Java.type("java.nio.file.Files"); +var FileSystems = Java.type("java.nio.file.FileSystems"); +print('Unique lines:', + Files + .lines(FileSystems.default.getPath(arguments[0])) + .distinct() + .peek(print) + .count()); diff --git a/samples/weather.js b/samples/weather.js new file mode 100644 index 00000000..75c6b265 --- /dev/null +++ b/samples/weather.js @@ -0,0 +1,63 @@ +#// usage: jjs -scripting weather.js + +/* + * Copyright (c) 2014, 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 nashorn example showing back-quote exec process, +// JSON and Java8 streams + +var Arrays = Java.type("java.util.Arrays"); + +// use curl to download JSON weather data from the net +// use backquote -scripting mode syntax to exec a process + +`curl http://api.openweathermap.org/data/2.5/forecast/daily?q=Chennai&mode=json&units=metric&cnt=7`; + +// parse JSON +var weather = JSON.parse($OUT); + +// pull out humidity as array +var humidity = weather.list.map(function(curVal) { + return curVal.humidity; +}) + +// Stream API to print stat +print("Humidity"); +print(Arrays["stream(int[])"](humidity).summaryStatistics()); + +// pull maximum day time temperature +var temp = weather.list.map(function(curVal) { + return curVal.temp.max; +}); + +// Stream API to print stat +print("Max Temperature"); +print(Arrays["stream(double[])"](temp).summaryStatistics()); diff --git a/src/jdk/internal/dynalink/beans/BeanLinker.java b/src/jdk/internal/dynalink/beans/BeanLinker.java index 519036b3..d0ad96ba 100644 --- a/src/jdk/internal/dynalink/beans/BeanLinker.java +++ b/src/jdk/internal/dynalink/beans/BeanLinker.java @@ -113,6 +113,8 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL // explicit property is beneficial for them. // REVISIT: is it maybe a code smell that "dyn:getLength" is not needed? setPropertyGetter("length", GET_ARRAY_LENGTH, ValidationType.IS_ARRAY); + } else if(List.class.isAssignableFrom(clazz)) { + setPropertyGetter("length", GET_COLLECTION_LENGTH, ValidationType.INSTANCE_OF); } } diff --git a/src/jdk/nashorn/api/scripting/NashornException.java b/src/jdk/nashorn/api/scripting/NashornException.java index d5ec5bb4..a5f8c24a 100644 --- a/src/jdk/nashorn/api/scripting/NashornException.java +++ b/src/jdk/nashorn/api/scripting/NashornException.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.runtime.ECMAErrors; +import jdk.nashorn.internal.runtime.ScriptObject; /** * This is base exception for all Nashorn exceptions. These originate from @@ -44,11 +45,13 @@ import jdk.nashorn.internal.runtime.ECMAErrors; @SuppressWarnings("serial") public abstract class NashornException extends RuntimeException { // script file name - private final String fileName; + private String fileName; // script line number - private final int line; + private int line; // script column number - private final int column; + private int column; + // underlying ECMA error object - lazily initialized + private Object ecmaError; /** script source name used for "engine.js" */ public static final String ENGINE_SCRIPT_SOURCE_NAME = "nashorn:engine/resources/engine.js"; @@ -122,6 +125,15 @@ public abstract class NashornException extends RuntimeException { } /** + * Set the source file name for this {@code NashornException} + * + * @param fileName the file name + */ + public final void setFileName(final String fileName) { + this.fileName = fileName; + } + + /** * Get the line number for this {@code NashornException} * * @return the line number @@ -131,15 +143,33 @@ public abstract class NashornException extends RuntimeException { } /** + * Set the line number for this {@code NashornException} + * + * @param line the line number + */ + public final void setLineNumber(final int line) { + this.line = line; + } + + /** * Get the column for this {@code NashornException} * - * @return the column + * @return the column number */ public final int getColumnNumber() { return column; } /** + * Set the column for this {@code NashornException} + * + * @param column the column number + */ + public final void setColumnNumber(final int column) { + this.column = column; + } + + /** * Returns array javascript stack frames from the given exception object. * * @param exception exception from which stack frames are retrieved and filtered @@ -155,6 +185,11 @@ public abstract class NashornException extends RuntimeException { if (methodName.equals(CompilerConstants.RUN_SCRIPT.symbolName())) { methodName = "<program>"; } + + if (methodName.contains(CompilerConstants.ANON_FUNCTION_PREFIX.symbolName())) { + methodName = "<anonymous>"; + } + filtered.add(new StackTraceElement(className, methodName, st.getFileName(), st.getLineNumber())); } @@ -188,4 +223,43 @@ public abstract class NashornException extends RuntimeException { } return buf.toString(); } + + protected Object getThrown() { + return null; + } + + protected NashornException initEcmaError(final ScriptObject global) { + if (ecmaError != null) { + return this; // initialized already! + } + + final Object thrown = getThrown(); + if (thrown instanceof ScriptObject) { + setEcmaError(ScriptObjectMirror.wrap(thrown, global)); + } else { + setEcmaError(thrown); + } + + return this; + } + + /** + * Return the underlying ECMA error object, if available. + * + * @return underlying ECMA Error object's mirror or whatever was thrown + * from script such as a String, Number or a Boolean. + */ + public Object getEcmaError() { + return ecmaError; + } + + /** + * Return the underlying ECMA error object, if available. + * + * @param ecmaError underlying ECMA Error object's mirror or whatever was thrown + * from script such as a String, Number or a Boolean. + */ + public void setEcmaError(final Object ecmaError) { + this.ecmaError = ecmaError; + } } diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java index 83b0bee8..7ebe5c91 100644 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java @@ -27,16 +27,14 @@ package jdk.nashorn.api.scripting; import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.Source.sourceFor; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Reader; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; -import java.nio.charset.Charset; import java.security.AccessControlContext; import java.security.AccessController; import java.security.Permissions; @@ -57,9 +55,9 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptException; import javax.script.SimpleBindings; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; -import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -99,7 +97,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C private final boolean _global_per_engine; // This is the initial default Nashorn global object. // This is used as "shared" global if above option is true. - private final ScriptObject global; + private final Global global; // initialized bit late to be made 'final'. // Property object for "context" property of global object. private volatile Property contextProperty; @@ -124,21 +122,21 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } } - // load engine.js and return content as a char[] + // load engine.js @SuppressWarnings("resource") - private static char[] loadEngineJSSource() { + private static Source loadEngineJSSource() { final String script = "resources/engine.js"; try { - final InputStream is = AccessController.doPrivileged( - new PrivilegedExceptionAction<InputStream>() { + return AccessController.doPrivileged( + new PrivilegedExceptionAction<Source>() { @Override - public InputStream run() throws Exception { + public Source run() throws IOException { final URL url = NashornScriptEngine.class.getResource(script); - return url.openStream(); + return sourceFor(NashornException.ENGINE_SCRIPT_SOURCE_NAME, url); } - }); - return Source.readFully(new InputStreamReader(is)); - } catch (final PrivilegedActionException | IOException e) { + } + ); + } catch (final PrivilegedActionException e) { if (Context.DEBUG) { e.printStackTrace(); } @@ -147,7 +145,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } // Source object for engine.js - private static final Source ENGINE_SCRIPT_SRC = new Source(NashornException.ENGINE_SCRIPT_SOURCE_NAME, loadEngineJSSource()); + private static final Source ENGINE_SCRIPT_SRC = loadEngineJSSource(); NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) { this(factory, DEFAULT_OPTIONS, appLoader); @@ -264,7 +262,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) { if (ctxt != null) { final int scope = ctxt.getAttributesScope(name); - final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt); + final Global ctxtGlobal = getNashornGlobalFrom(ctxt); if (scope != -1) { return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal); } @@ -282,19 +280,14 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException { try { - if (reader instanceof URLReader) { - final URL url = ((URLReader)reader).getURL(); - final Charset cs = ((URLReader)reader).getCharset(); - return new Source(url.toString(), url, cs); - } - return new Source(getScriptName(ctxt), Source.readFully(reader)); - } catch (final IOException e) { + return sourceFor(getScriptName(ctxt), reader); + } catch (IOException e) { throw new ScriptException(e); } } private static Source makeSource(final String src, final ScriptContext ctxt) { - return new Source(getScriptName(ctxt), src); + return sourceFor(getScriptName(ctxt), src); } private static String getScriptName(final ScriptContext ctxt) { @@ -317,7 +310,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } ScriptObject realSelf = null; - ScriptObject realGlobal = null; + Global realGlobal = null; if(thiz == null) { // making interface out of global functions realSelf = realGlobal = getNashornGlobalFrom(context); @@ -346,7 +339,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } try { - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != realGlobal); try { if (globalChanged) { @@ -371,7 +364,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } // Retrieve nashorn Global object for a given ScriptContext object - private ScriptObject getNashornGlobalFrom(final ScriptContext ctxt) { + private Global getNashornGlobalFrom(final ScriptContext ctxt) { if (_global_per_engine) { // shared single global object for all ENGINE_SCOPE Bindings return global; @@ -380,18 +373,18 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C final Bindings bindings = ctxt.getBindings(ScriptContext.ENGINE_SCOPE); // is this Nashorn's own Bindings implementation? if (bindings instanceof ScriptObjectMirror) { - final ScriptObject sobj = globalFromMirror((ScriptObjectMirror)bindings); - if (sobj != null) { - return sobj; + final Global glob = globalFromMirror((ScriptObjectMirror)bindings); + if (glob != null) { + return glob; } } // Arbitrary user Bindings implementation. Look for NASHORN_GLOBAL in it! Object scope = bindings.get(NASHORN_GLOBAL); if (scope instanceof ScriptObjectMirror) { - final ScriptObject sobj = globalFromMirror((ScriptObjectMirror)scope); - if (sobj != null) { - return sobj; + final Global glob = globalFromMirror((ScriptObjectMirror)scope); + if (glob != null) { + return glob; } } @@ -399,14 +392,14 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C // Create new global instance mirror and associate with the Bindings. final ScriptObjectMirror mirror = createGlobalMirror(ctxt); bindings.put(NASHORN_GLOBAL, mirror); - return mirror.getScriptObject(); + return mirror.getHomeGlobal(); } // Retrieve nashorn Global object from a given ScriptObjectMirror - private ScriptObject globalFromMirror(final ScriptObjectMirror mirror) { + private Global globalFromMirror(final ScriptObjectMirror mirror) { ScriptObject sobj = mirror.getScriptObject(); - if (sobj instanceof GlobalObject && isOfContext(sobj, nashornContext)) { - return sobj; + if (sobj instanceof Global && isOfContext((Global)sobj, nashornContext)) { + return (Global)sobj; } return null; @@ -414,15 +407,15 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C // Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) { - final ScriptObject newGlobal = createNashornGlobal(ctxt); + final Global newGlobal = createNashornGlobal(ctxt); return new ScriptObjectMirror(newGlobal, newGlobal); } // Create a new Nashorn Global object - private ScriptObject createNashornGlobal(final ScriptContext ctxt) { - final ScriptObject newGlobal = AccessController.doPrivileged(new PrivilegedAction<ScriptObject>() { + private Global createNashornGlobal(final ScriptContext ctxt) { + final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { @Override - public ScriptObject run() { + public Global run() { try { return nashornContext.newGlobal(); } catch (final RuntimeException e) { @@ -460,7 +453,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } // scripts should see "context" and "engine" as variables in the given global object - private void setContextVariables(final ScriptObject ctxtGlobal, final ScriptContext ctxt) { + private void setContextVariables(final Global ctxtGlobal, final ScriptContext ctxt) { // set "context" global variable via contextProperty - because this // property is non-writable contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false); @@ -470,7 +463,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } // if no arguments passed, expose it if (! (args instanceof ScriptObject)) { - args = ((GlobalObject)ctxtGlobal).wrapAsObject(args); + args = ctxtGlobal.wrapAsObject(args); ctxtGlobal.set("arguments", args, false); } } @@ -478,16 +471,19 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException { name.getClass(); // null check + Global invokeGlobal = null; ScriptObjectMirror selfMirror = null; if (selfObject instanceof ScriptObjectMirror) { selfMirror = (ScriptObjectMirror)selfObject; if (! isOfContext(selfMirror.getHomeGlobal(), nashornContext)) { throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); } + invokeGlobal = selfMirror.getHomeGlobal(); } else if (selfObject instanceof ScriptObject) { // invokeMethod called from script code - in which case we may get 'naked' ScriptObject // Wrap it with oldGlobal to make a ScriptObjectMirror for the same. - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); + invokeGlobal = oldGlobal; if (oldGlobal == null) { throw new IllegalArgumentException(getMessage("no.current.nashorn.global")); } @@ -499,7 +495,8 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal); } else if (selfObject == null) { // selfObject is null => global function call - final ScriptObject ctxtGlobal = getNashornGlobalFrom(context); + final Global ctxtGlobal = getNashornGlobalFrom(context); + invokeGlobal = ctxtGlobal; selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(ctxtGlobal, ctxtGlobal); } @@ -511,7 +508,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C if (cause instanceof NoSuchMethodException) { throw (NoSuchMethodException)cause; } - throwAsScriptException(e); + throwAsScriptException(e, invokeGlobal); throw new AssertionError("should not reach here"); } } @@ -528,11 +525,36 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt)); } - private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final ScriptObject ctxtGlobal) throws ScriptException { + private Object evalImpl(final Context.MultiGlobalCompiledScript mgcs, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException { + final Global oldGlobal = Context.getGlobal(); + final boolean globalChanged = (oldGlobal != ctxtGlobal); + try { + if (globalChanged) { + Context.setGlobal(ctxtGlobal); + } + + final ScriptFunction script = mgcs.getFunction(ctxtGlobal); + + // set ScriptContext variables if ctxt is non-null + if (ctxt != null) { + setContextVariables(ctxtGlobal, ctxt); + } + return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal)); + } catch (final Exception e) { + throwAsScriptException(e, ctxtGlobal); + throw new AssertionError("should not reach here"); + } finally { + if (globalChanged) { + Context.setGlobal(oldGlobal); + } + } + } + + private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException { if (script == null) { return null; } - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != ctxtGlobal); try { if (globalChanged) { @@ -545,7 +567,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal)); } catch (final Exception e) { - throwAsScriptException(e); + throwAsScriptException(e, ctxtGlobal); throw new AssertionError("should not reach here"); } finally { if (globalChanged) { @@ -554,7 +576,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } } - private static void throwAsScriptException(final Exception e) throws ScriptException { + private static void throwAsScriptException(final Exception e, final Global global) throws ScriptException { if (e instanceof ScriptException) { throw (ScriptException)e; } else if (e instanceof NashornException) { @@ -562,6 +584,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C final ScriptException se = new ScriptException( ne.getMessage(), ne.getFileName(), ne.getLineNumber(), ne.getColumnNumber()); + ne.initEcmaError(global); se.initCause(e); throw se; } else if (e instanceof RuntimeException) { @@ -573,18 +596,38 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } private CompiledScript asCompiledScript(final Source source) throws ScriptException { - final ScriptFunction func = compileImpl(source, context); + final Context.MultiGlobalCompiledScript mgcs; + final ScriptFunction func; + final Global oldGlobal = Context.getGlobal(); + final Global newGlobal = getNashornGlobalFrom(context); + final boolean globalChanged = (oldGlobal != newGlobal); + try { + if (globalChanged) { + Context.setGlobal(newGlobal); + } + + mgcs = nashornContext.compileScript(source); + func = mgcs.getFunction(newGlobal); + } catch (final Exception e) { + throwAsScriptException(e, newGlobal); + throw new AssertionError("should not reach here"); + } finally { + if (globalChanged) { + Context.setGlobal(oldGlobal); + } + } + return new CompiledScript() { @Override public Object eval(final ScriptContext ctxt) throws ScriptException { - final ScriptObject globalObject = getNashornGlobalFrom(ctxt); - // Are we running the script in the correct global? + final Global globalObject = getNashornGlobalFrom(ctxt); + // Are we running the script in the same global in which it was compiled? if (func.getScope() == globalObject) { return evalImpl(func, ctxt, globalObject); } - // ScriptContext with a different global. Compile again! - // Note that we may still hit per-global compilation cache. - return evalImpl(compileImpl(source, ctxt), ctxt, globalObject); + + // different global + return evalImpl(mgcs, ctxt, globalObject); } @Override public ScriptEngine getEngine() { @@ -597,8 +640,8 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C return compileImpl(source, getNashornGlobalFrom(ctxt)); } - private ScriptFunction compileImpl(final Source source, final ScriptObject newGlobal) throws ScriptException { - final ScriptObject oldGlobal = Context.getGlobal(); + private ScriptFunction compileImpl(final Source source, final Global newGlobal) throws ScriptException { + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != newGlobal); try { if (globalChanged) { @@ -607,7 +650,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C return nashornContext.compileScript(source, newGlobal); } catch (final Exception e) { - throwAsScriptException(e); + throwAsScriptException(e, newGlobal); throw new AssertionError("should not reach here"); } finally { if (globalChanged) { @@ -623,6 +666,11 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C continue; } + // skip check for default methods - non-abstract, interface methods + if (! Modifier.isAbstract(method.getModifiers())) { + continue; + } + Object obj = sobj.get(method.getName()); if (! (obj instanceof ScriptFunction)) { return false; @@ -631,8 +679,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C return true; } - private static boolean isOfContext(final ScriptObject global, final Context context) { - assert global instanceof GlobalObject: "Not a Global object"; - return ((GlobalObject)global).isOfContext(context); + private static boolean isOfContext(final Global global, final Context context) { + return global.isOfContext(context); } } diff --git a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java index 911f1663..b1255336 100644 --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java @@ -25,6 +25,7 @@ package jdk.nashorn.api.scripting; +import java.nio.ByteBuffer; import java.security.AccessControlContext; import java.security.AccessController; import java.security.Permissions; @@ -41,9 +42,10 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import javax.script.Bindings; +import jdk.nashorn.internal.objects.Global; +import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -62,7 +64,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin private static final AccessControlContext GET_CONTEXT_ACC_CTXT = getContextAccCtxt(); private final ScriptObject sobj; - private final ScriptObject global; + private final Global global; private final boolean strict; @Override @@ -93,7 +95,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin @Override public Object call(final Object thiz, final Object... args) { - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { @@ -108,6 +110,8 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin } throw new RuntimeException("not a function: " + toString()); + } catch (final NashornException ne) { + throw ne.initEcmaError(global); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { @@ -121,7 +125,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin @Override public Object newObject(final Object... args) { - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { @@ -135,6 +139,8 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin } throw new RuntimeException("not a constructor: " + toString()); + } catch (final NashornException ne) { + throw ne.initEcmaError(global); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { @@ -165,7 +171,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin public Object callMember(final String functionName, final Object... args) { functionName.getClass(); // null check - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { @@ -182,6 +188,8 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin } throw new NoSuchMethodException("No such function " + functionName); + } catch (final NashornException ne) { + throw ne.initEcmaError(global); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { @@ -253,6 +261,22 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin }); } + /** + * Nashorn extension: setIndexedPropertiesToExternalArrayData. + * set indexed properties be exposed from a given nio ByteBuffer. + * + * @param buf external buffer - should be a nio ByteBuffer + */ + public void setIndexedPropertiesToExternalArrayData(final ByteBuffer buf) { + inGlobal(new Callable<Void>() { + @Override public Void call() { + sobj.setArray(ArrayData.allocate(buf)); + return null; + } + }); + } + + @Override public boolean isInstance(final Object obj) { if (! (obj instanceof ScriptObjectMirror)) { @@ -618,7 +642,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin */ public static Object wrap(final Object obj, final Object homeGlobal) { if(obj instanceof ScriptObject) { - return homeGlobal instanceof ScriptObject ? new ScriptObjectMirror((ScriptObject)obj, (ScriptObject)homeGlobal) : obj; + return homeGlobal instanceof Global ? new ScriptObjectMirror((ScriptObject)obj, (Global)homeGlobal) : obj; } if(obj instanceof ConsString) { return obj.toString(); @@ -686,13 +710,13 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin // package-privates below this. - ScriptObjectMirror(final ScriptObject sobj, final ScriptObject global) { + ScriptObjectMirror(final ScriptObject sobj, final Global global) { assert sobj != null : "ScriptObjectMirror on null!"; - assert global instanceof GlobalObject : "global is not a GlobalObject"; + assert global != null : "home Global is null"; this.sobj = sobj; this.global = global; - this.strict = ((GlobalObject)global).isStrictContext(); + this.strict = global.isStrictContext(); } // accessors for script engine @@ -700,7 +724,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin return sobj; } - ScriptObject getHomeGlobal() { + Global getHomeGlobal() { return global; } @@ -710,13 +734,15 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin // internals only below this. private <V> V inGlobal(final Callable<V> callable) { - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); if (globalChanged) { Context.setGlobal(global); } try { return callable.call(); + } catch (final NashornException ne) { + throw ne.initEcmaError(global); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java index 177cfef4..61ccb712 100644 --- a/src/jdk/nashorn/internal/codegen/Attr.java +++ b/src/jdk/nashorn/internal/codegen/Attr.java @@ -375,10 +375,11 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { * @return Symbol for given name or null for redefinition. */ private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) { - int flags = symbolFlags; - Symbol symbol = findSymbol(block, name); // Locate symbol. + int flags = symbolFlags; + Symbol symbol = findSymbol(block, name); // Locate symbol. + boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL; - if ((flags & KINDMASK) == IS_GLOBAL) { + if (isGlobal) { flags |= IS_SCOPE; } @@ -414,6 +415,8 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> { // Determine where to create it. if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { symbolBlock = block; //internal vars are always defined in the block closest to them + } else if (isGlobal) { + symbolBlock = lc.getOutermostFunction().getBody(); } else { symbolBlock = lc.getFunctionBody(function); } diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index c3b1dbe2..882846c6 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -685,7 +685,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex private void scopeCall(final IdentNode node, final int flags) { load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. - method.loadNull(); //the 'this' + method.loadUndefined(Type.OBJECT); //the 'this' object method.dynamicCall(callNodeType, 2 + loadArgs(args), flags); } @@ -818,7 +818,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex protected boolean enterDefault(final Node node) { // Load up function. load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions - method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE + method.loadUndefined(Type.OBJECT); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE); return false; @@ -1451,7 +1451,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex if (value == null) { hasGettersSetters = true; - } else if (key.equals(ScriptObject.PROTO_PROPERTY_NAME)) { + } else if (propertyNode.getKey() instanceof IdentNode && + key.equals(ScriptObject.PROTO_PROPERTY_NAME)) { + // ES6 draft compliant __proto__ inside object literal + // Identifier key and name is __proto__ protoNode = value; continue; } @@ -2023,8 +2026,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex return false; } - method._new(ECMAException.class).dup(); - final Source source = lc.getCurrentFunction().getSource(); final Expression expression = throwNode.getExpression(); @@ -2037,7 +2038,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex method.load(source.getName()); method.load(line); method.load(column); - method.invoke(ECMAException.THROW_INIT); + method.invoke(ECMAException.CREATE); method.athrow(); @@ -3230,7 +3231,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex final String className = SCRIPTFUNCTION_IMPL_OBJECT; final int fieldCount = ObjectClassGenerator.getPaddedFieldCount(functionNode.countThisProperties()); final String allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount)); - final PropertyMap allocatorMap = PropertyMap.newMap(null, 0, fieldCount, 0); + final PropertyMap allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, fieldCount, 0); method._new(className).dup(); loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), allocatorClassName, allocatorMap)); diff --git a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java index d27bdd95..dcf1b1ba 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java +++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java @@ -158,7 +158,7 @@ final class CodeGeneratorLexicalContext extends LexicalContext { if (scopeCalls.containsKey(scopeCall)) { return scopeCalls.get(scopeCall); } - scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall")); + scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall")); scopeCalls.put(scopeCall, scopeCall); return scopeCall; } @@ -177,7 +177,7 @@ final class CodeGeneratorLexicalContext extends LexicalContext { if (scopeCalls.containsKey(scopeCall)) { return scopeCalls.get(scopeCall); } - scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall")); + scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall")); scopeCalls.put(scopeCall, scopeCall); return scopeCall; } diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java index 24173c3e..e4af587b 100644 --- a/src/jdk/nashorn/internal/codegen/Compiler.java +++ b/src/jdk/nashorn/internal/codegen/Compiler.java @@ -85,6 +85,8 @@ public final class Compiler { private Source source; + private String sourceName; + private final Map<String, byte[]> bytecode; private final Set<CompileUnit> compileUnits; @@ -267,6 +269,7 @@ public final class Compiler { append('$'). append(safeSourceName(functionNode.getSource())); this.source = functionNode.getSource(); + this.sourceName = functionNode.getSourceName(); this.scriptName = sb.toString(); } @@ -402,32 +405,8 @@ public final class Compiler { return newFunctionNode; } - private Class<?> install(final String className, final byte[] code) { - LOG.fine("Installing class ", className); - - final Class<?> clazz = installer.install(Compiler.binaryName(className), code); - - try { - final Object[] constants = getConstantData().toArray(); - // Need doPrivileged because these fields are private - AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { - @Override - public Void run() throws Exception { - //use reflection to write source and constants table to installed classes - final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); - final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); - sourceField.setAccessible(true); - constantsField.setAccessible(true); - sourceField.set(null, source); - constantsField.set(null, constants); - return null; - } - }); - } catch (final PrivilegedActionException e) { - throw new RuntimeException(e); - } - - return clazz; + private Class<?> install(final String className, final byte[] code, final Object[] constants) { + return installer.install(className, code, source, constants); } /** @@ -441,10 +420,15 @@ public final class Compiler { assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed"; final Map<String, Class<?>> installedClasses = new HashMap<>(); + final Object[] constants = getConstantData().toArray(); final String rootClassName = firstCompileUnitName(); final byte[] rootByteCode = bytecode.get(rootClassName); - final Class<?> rootClass = install(rootClassName, rootByteCode); + final Class<?> rootClass = install(rootClassName, rootByteCode, constants); + + if (!isLazy()) { + installer.storeCompiledScript(source, rootClassName, bytecode, constants); + } int length = rootByteCode.length; @@ -458,7 +442,7 @@ public final class Compiler { final byte[] code = entry.getValue(); length += code.length; - installedClasses.put(className, install(className, code)); + installedClasses.put(className, install(className, code, constants)); } for (final CompileUnit unit : compileUnits) { @@ -573,7 +557,7 @@ public final class Compiler { } private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) { - final ClassEmitter classEmitter = new ClassEmitter(env, source.getName(), unitClassName, strict); + final ClassEmitter classEmitter = new ClassEmitter(env, sourceName, unitClassName, strict); final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); classEmitter.begin(); diff --git a/src/jdk/nashorn/internal/codegen/CompilerConstants.java b/src/jdk/nashorn/internal/codegen/CompilerConstants.java index fb347c75..16f08b82 100644 --- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java @@ -41,6 +41,7 @@ import jdk.nashorn.internal.runtime.Source; */ public enum CompilerConstants { + /** the __FILE__ variable */ __FILE__, @@ -75,7 +76,7 @@ public enum CompilerConstants { DEFAULT_SCRIPT_NAME("Script"), /** function prefix for anonymous functions */ - FUNCTION_PREFIX("function$"), + ANON_FUNCTION_PREFIX("L:"), /** method name for Java method that is script entry point */ RUN_SCRIPT("runScript"), @@ -149,26 +150,31 @@ public enum CompilerConstants { ALLOCATE("allocate"), /** prefix for split methods, @see Splitter */ - SPLIT_PREFIX("$split"), + SPLIT_PREFIX(":split"), /** prefix for split array method and slot */ - SPLIT_ARRAY_ARG("split_array", 3), + SPLIT_ARRAY_ARG(":split_array", 3), /** get string from constant pool */ - GET_STRING("$getString"), + GET_STRING(":getString"), /** get map */ - GET_MAP("$getMap"), + GET_MAP(":getMap"), /** get map */ - SET_MAP("$setMap"), + SET_MAP(":setMap"), /** get array prefix */ - GET_ARRAY_PREFIX("$get"), + GET_ARRAY_PREFIX(":get"), /** get array suffix */ GET_ARRAY_SUFFIX("$array"); + /** + * Prefix used for internal methods generated in script clases. + */ + public static final String INTERNAL_METHOD_PREFIX = ":"; + private final String symbolName; private final Class<?> type; private final int slot; diff --git a/src/jdk/nashorn/internal/codegen/ConstantData.java b/src/jdk/nashorn/internal/codegen/ConstantData.java index 631cdb3e..292f1678 100644 --- a/src/jdk/nashorn/internal/codegen/ConstantData.java +++ b/src/jdk/nashorn/internal/codegen/ConstantData.java @@ -25,6 +25,9 @@ package jdk.nashorn.internal.codegen; +import jdk.nashorn.internal.runtime.Property; +import jdk.nashorn.internal.runtime.PropertyMap; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -110,6 +113,43 @@ class ConstantData { } /** + * {@link PropertyMap} wrapper class that provides implementations for the {@code hashCode} and {@code equals} + * methods that are based on the map layout. {@code PropertyMap} itself inherits the identity based implementations + * from {@code java.lang.Object}. + */ + private static class PropertyMapWrapper { + private final PropertyMap propertyMap; + private final int hashCode; + + public PropertyMapWrapper(final PropertyMap map) { + int hash = 0; + for (final Property property : map.getProperties()) { + hash = hash << 7 ^ hash >> 7; + hash ^= property.hashCode(); + } + this.hashCode = hash; + this.propertyMap = map; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(final Object other) { + if (!(other instanceof PropertyMapWrapper)) { + return false; + } + + final Property[] ownProperties = propertyMap.getProperties(); + final Property[] otherProperties = ((PropertyMapWrapper) other).propertyMap.getProperties(); + + return Arrays.equals(ownProperties, otherProperties); + } + } + + /** * Constructor */ ConstantData() { @@ -145,7 +185,14 @@ class ConstantData { * @return the index in the constant pool that the object was given */ public int add(final Object object) { - final Object entry = object.getClass().isArray() ? new ArrayWrapper(object) : object; + final Object entry; + if (object.getClass().isArray()) { + entry = new ArrayWrapper(object); + } else if (object instanceof PropertyMap) { + entry = new PropertyMapWrapper((PropertyMap) object); + } else { + entry = object; + } final Integer value = objectMap.get(entry); if (value != null) { diff --git a/src/jdk/nashorn/internal/codegen/MapCreator.java b/src/jdk/nashorn/internal/codegen/MapCreator.java index 8012adf5..436622bd 100644 --- a/src/jdk/nashorn/internal/codegen/MapCreator.java +++ b/src/jdk/nashorn/internal/codegen/MapCreator.java @@ -83,7 +83,7 @@ public class MapCreator { } } - return PropertyMap.newMap(properties, fieldCount, fieldMaximum, 0); + return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0); } PropertyMap makeSpillMap(final boolean hasArguments) { @@ -100,7 +100,7 @@ public class MapCreator { } } - return PropertyMap.newMap(properties, 0, 0, spillIndex); + return PropertyMap.newMap(properties, structure.getName(), 0, 0, spillIndex); } /** diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java index 9b6d12aa..91129ece 100644 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java @@ -1130,7 +1130,11 @@ public class MethodEmitter implements Emitter { popType(Type.OBJECT); } - method.visitMethodInsn(opcode, className, methodName, methodDescriptor); + if (opcode == INVOKEINTERFACE) { + method.visitMethodInsn(opcode, className, methodName, methodDescriptor, true); + } else { + method.visitMethodInsn(opcode, className, methodName, methodDescriptor, false); + } if (returnType != null) { pushType(returnType); diff --git a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java index c7dfb40a..da29ac05 100644 --- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java +++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java @@ -156,7 +156,7 @@ class SharedScopeCall { if (isCall) { method.convert(Type.OBJECT); // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. - method.loadNull(); + method.loadUndefined(Type.OBJECT); int slot = 2; for (final Type type : paramTypes) { method.load(type, slot++); diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java index e7c78953..4799c264 100644 --- a/src/jdk/nashorn/internal/codegen/types/Type.java +++ b/src/jdk/nashorn/internal/codegen/types/Type.java @@ -261,7 +261,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps { } static void invokeStatic(final MethodVisitor method, final Call call) { - method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor()); + method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor(), false); } /** diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java index b0998ae1..a0f6a788 100644 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; @@ -138,6 +139,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag /** Function flags. */ private final int flags; + /** //@ sourceURL or //# sourceURL for program function nodes */ + private final String sourceURL; + private final int lineNumber; /** Is anonymous function flag. */ @@ -160,11 +164,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag public static final int HAS_EVAL = 1 << 5; /** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */ - public static final int HAS_NESTED_EVAL = 1 << 6; + public static final int HAS_NESTED_EVAL = 1 << 6; /** Does this function have any blocks that create a scope? This is used to determine if the function needs to * have a local variable slot for the scope symbol. */ - public static final int HAS_SCOPE_BLOCK = 1 << 7; + public static final int HAS_SCOPE_BLOCK = 1 << 7; /** * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function @@ -193,6 +197,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag /** Can this function be specialized? */ public static final int CAN_SPECIALIZE = 1 << 14; + /** Does this function use the "this" keyword? */ + public static final int USES_THIS = 1 << 15; + /** Does this function or any nested functions contain an eval? */ private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL; @@ -223,6 +230,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag * @param parameters parameter list * @param kind kind of function as in {@link FunctionNode.Kind} * @param flags initial flags + * @param sourceURL sourceURL specified in script (optional) */ public FunctionNode( final Source source, @@ -235,7 +243,8 @@ public final class FunctionNode extends LexicalContextExpression implements Flag final String name, final List<IdentNode> parameters, final FunctionNode.Kind kind, - final int flags) { + final int flags, + final String sourceURL) { super(token, finish); this.source = source; @@ -250,6 +259,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag this.compilationState = EnumSet.of(CompilationState.INITIALIZED); this.declaredSymbols = new HashSet<>(); this.flags = flags; + this.sourceURL = sourceURL; this.compileUnit = null; this.body = null; this.snapshot = null; @@ -260,6 +270,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag final FunctionNode functionNode, final long lastToken, final int flags, + final String sourceURL, final String name, final Type returnType, final CompileUnit compileUnit, @@ -271,6 +282,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag super(functionNode); this.lineNumber = functionNode.lineNumber; this.flags = flags; + this.sourceURL = sourceURL; this.name = name; this.returnType = returnType; this.compileUnit = compileUnit; @@ -308,6 +320,38 @@ public final class FunctionNode extends LexicalContextExpression implements Flag } /** + * get source name - sourceURL or name derived from Source. + * + * @return name for the script source + */ + public String getSourceName() { + return (sourceURL != null)? sourceURL : source.getName(); + } + + /** + * get the sourceURL + * @return the sourceURL + */ + public String getSourceURL() { + return sourceURL; + } + + /** + * Set the sourceURL + * + * @param lc lexical context + * @param newSourceURL source url string to set + * @return function node or a new one if state was changed + */ + public FunctionNode setSourceURL(final LexicalContext lc, final String newSourceURL) { + if (Objects.equals(sourceURL, newSourceURL)) { + return this; + } + + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints)); + } + + /** * Returns the line number. * @return the line number. */ @@ -335,7 +379,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (this.snapshot == null) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, null, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints)); } /** @@ -351,7 +395,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (isProgram() || parameters.isEmpty()) { return this; //never specialize anything that won't be recompiled } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, this, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, this, hints)); } /** @@ -409,7 +453,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag } final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); newState.add(state); - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, newState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, snapshot, hints)); } /** @@ -430,7 +474,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (this.hints == hints) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -483,7 +527,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (this.flags == flags) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } @Override @@ -550,6 +594,15 @@ public final class FunctionNode extends LexicalContextExpression implements Flag } /** + * Return {@code true} if this function makes use of the {@code this} object. + * + * @return true if function uses {@code this} object + */ + public boolean usesThis() { + return getFlag(USES_THIS); + } + + /** * Get the identifier for this function, this is its symbol. * @return the identifier as an IdentityNode */ @@ -593,7 +646,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if(this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -688,7 +741,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (this.lastToken == lastToken) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -710,7 +763,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (this.name.equals(name)) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -760,7 +813,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (this.parameters == parameters) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** @@ -825,6 +878,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag this, lastToken, flags, + sourceURL, name, type, compileUnit, @@ -863,7 +917,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag if (this.compileUnit == compileUnit) { return this; } - return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); + return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints)); } /** diff --git a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java index 4702057a..ef7f8a1b 100644 --- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java @@ -25,6 +25,8 @@ package jdk.nashorn.internal.ir.debug; +import static jdk.nashorn.internal.runtime.Source.sourceFor; + import java.util.Arrays; import java.util.List; import java.util.ArrayList; @@ -88,7 +90,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> { * @return JSON string representation of AST of the supplied code */ public static String parse(final ScriptEnvironment env, final String code, final String name, final boolean includeLoc) { - final Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict); + final Parser parser = new Parser(env, sourceFor(name, code), new Context.ThrowErrorManager(), env._strict); final JSONWriter jsonWriter = new JSONWriter(includeLoc); try { final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName()); diff --git a/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java b/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java index b3731620..e39c5217 100644 --- a/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java +++ b/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java @@ -67,12 +67,8 @@ public final class AccessorPropertyDescriptor extends ScriptObject implements Pr // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - AccessorPropertyDescriptor(final boolean configurable, final boolean enumerable, final Object get, final Object set, final Global global) { - super(global.getObjectPrototype(), global.getAccessorPropertyDescriptorMap()); + super(global.getObjectPrototype(), $nasgenmap$); this.configurable = configurable; this.enumerable = enumerable; this.get = get; @@ -185,6 +181,18 @@ public final class AccessorPropertyDescriptor extends ScriptObject implements Pr } @Override + public boolean hasAndEquals(final PropertyDescriptor otherDesc) { + if (! (otherDesc instanceof AccessorPropertyDescriptor)) { + return false; + } + final AccessorPropertyDescriptor other = (AccessorPropertyDescriptor)otherDesc; + return (!has(CONFIGURABLE) || sameValue(configurable, other.configurable)) && + (!has(ENUMERABLE) || sameValue(enumerable, other.enumerable)) && + (!has(GET) || sameValue(get, other.get)) && + (!has(SET) || sameValue(set, other.set)); + } + + @Override public boolean equals(final Object obj) { if (this == obj) { return true; diff --git a/src/jdk/nashorn/internal/objects/ArrayBufferView.java b/src/jdk/nashorn/internal/objects/ArrayBufferView.java index ecf8bec8..c29af851 100644 --- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java +++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java @@ -42,12 +42,8 @@ abstract class ArrayBufferView extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - private ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength, final Global global) { - super(global.getArrayBufferViewMap()); + super($nasgenmap$); checkConstructorArgs(buffer, byteOffset, elementLength); this.setProto(getPrototype(global)); this.setArray(factory().createArrayData(buffer, byteOffset, elementLength)); @@ -386,7 +382,7 @@ abstract class ArrayBufferView extends ScriptObject { return (int) (length & Integer.MAX_VALUE); } - protected static Object subarrayImpl(final Object self, final Object begin0, final Object end0) { + protected static ScriptObject subarrayImpl(final Object self, final Object begin0, final Object end0) { final ArrayBufferView arrayView = ((ArrayBufferView)self); final int elementLength = arrayView.elementLength(); final int begin = NativeArrayBuffer.adjustIndex(JSType.toInt32(begin0), elementLength); diff --git a/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java b/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java index ef3f0a30..e117c7ed 100644 --- a/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java +++ b/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java @@ -64,12 +64,8 @@ public final class DataPropertyDescriptor extends ScriptObject implements Proper // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - DataPropertyDescriptor(final boolean configurable, final boolean enumerable, final boolean writable, final Object value, final Global global) { - super(global.getObjectPrototype(), global.getDataPropertyDescriptorMap()); + super(global.getObjectPrototype(), $nasgenmap$); this.configurable = configurable; this.enumerable = enumerable; this.writable = writable; @@ -172,6 +168,19 @@ public final class DataPropertyDescriptor extends ScriptObject implements Proper } @Override + public boolean hasAndEquals(final PropertyDescriptor otherDesc) { + if (! (otherDesc instanceof DataPropertyDescriptor)) { + return false; + } + + final DataPropertyDescriptor other = (DataPropertyDescriptor)otherDesc; + return (!has(CONFIGURABLE) || sameValue(configurable, other.configurable)) && + (!has(ENUMERABLE) || sameValue(enumerable, other.enumerable)) && + (!has(WRITABLE) || sameValue(writable, other.writable)) && + (!has(VALUE) || sameValue(value, other.value)); + } + + @Override public boolean equals(final Object obj) { if (this == obj) { return true; diff --git a/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java b/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java index f5662efa..19cc8b6f 100644 --- a/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java +++ b/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java @@ -55,12 +55,8 @@ public final class GenericPropertyDescriptor extends ScriptObject implements Pro // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - GenericPropertyDescriptor(final boolean configurable, final boolean enumerable, final Global global) { - super(global.getObjectPrototype(), global.getGenericPropertyDescriptorMap()); + super(global.getObjectPrototype(), $nasgenmap$); this.configurable = configurable; this.enumerable = enumerable; } @@ -149,6 +145,23 @@ public final class GenericPropertyDescriptor extends ScriptObject implements Pro } @Override + public boolean hasAndEquals(final PropertyDescriptor other) { + if (has(CONFIGURABLE) && other.has(CONFIGURABLE)) { + if (isConfigurable() != other.isConfigurable()) { + return false; + } + } + + if (has(ENUMERABLE) && other.has(ENUMERABLE)) { + if (isEnumerable() != other.isEnumerable()) { + return false; + } + } + + return true; + } + + @Override public boolean equals(final Object obj) { if (this == obj) { return true; diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java index db79f5f9..00019382 100644 --- a/src/jdk/nashorn/internal/objects/Global.java +++ b/src/jdk/nashorn/internal/objects/Global.java @@ -33,11 +33,8 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.util.Arrays; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -51,7 +48,6 @@ import jdk.nashorn.internal.objects.annotations.ScriptClass; import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.GlobalFunctions; -import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.NativeJavaPackage; import jdk.nashorn.internal.runtime.PropertyDescriptor; @@ -59,10 +55,10 @@ import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.Scope; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptFunction; +import jdk.nashorn.internal.runtime.ScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.ScriptingFunctions; -import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; @@ -73,7 +69,7 @@ import jdk.nashorn.internal.scripts.JO; * Representation of global scope. */ @ScriptClass("Global") -public final class Global extends ScriptObject implements GlobalObject, Scope { +public final class Global extends ScriptObject implements Scope { private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class); private final InvokeByName VALUE_OF = new InvokeByName("valueOf", ScriptObject.class); @@ -229,6 +225,10 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { @Property(name = "ArrayBuffer", attributes = Attribute.NOT_ENUMERABLE) public volatile Object arrayBuffer; + /** DataView object */ + @Property(name = "DataView", attributes = Attribute.NOT_ENUMERABLE) + public volatile Object dataView; + /** TypedArray (int8) */ @Property(name = "Int8Array", attributes = Attribute.NOT_ENUMERABLE) public volatile Object int8Array; @@ -355,6 +355,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { private ScriptObject builtinJavaImporter; private ScriptObject builtinJavaApi; private ScriptObject builtinArrayBuffer; + private ScriptObject builtinDataView; private ScriptObject builtinInt8Array; private ScriptObject builtinUint8Array; private ScriptObject builtinUint8ClampedArray; @@ -370,42 +371,9 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { */ private ScriptFunction typeErrorThrower; - private PropertyMap accessorPropertyDescriptorMap; - private PropertyMap arrayBufferViewMap; - private PropertyMap dataPropertyDescriptorMap; - private PropertyMap genericPropertyDescriptorMap; - private PropertyMap nativeArgumentsMap; - private PropertyMap nativeArrayMap; - private PropertyMap nativeArrayBufferMap; - private PropertyMap nativeBooleanMap; - private PropertyMap nativeDateMap; - private PropertyMap nativeErrorMap; - private PropertyMap nativeEvalErrorMap; - private PropertyMap nativeJSAdapterMap; - private PropertyMap nativeJavaImporterMap; - private PropertyMap nativeNumberMap; - private PropertyMap nativeRangeErrorMap; - private PropertyMap nativeReferenceErrorMap; - private PropertyMap nativeRegExpMap; - private PropertyMap nativeRegExpExecResultMap; - private PropertyMap nativeStrictArgumentsMap; - private PropertyMap nativeStringMap; - private PropertyMap nativeSyntaxErrorMap; - private PropertyMap nativeTypeErrorMap; - private PropertyMap nativeURIErrorMap; - private PropertyMap prototypeObjectMap; - private PropertyMap objectMap; - private PropertyMap functionMap; - private PropertyMap anonymousFunctionMap; - private PropertyMap strictFunctionMap; - private PropertyMap boundFunctionMap; - // Flag to indicate that a split method issued a return statement private int splitState = -1; - // class cache - private ClassCache classCache; - // Used to store the last RegExp result to support deprecated RegExp constructor properties private RegExpResult lastRegExpResult; @@ -456,11 +424,6 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { super(checkAndGetMap(context)); this.context = context; this.setIsScope(); - - final int cacheSize = context.getEnv()._class_cache_size; - if (cacheSize > 0) { - classCache = new ClassCache(cacheSize); - } } /** @@ -469,11 +432,9 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { * @return the global singleton */ public static Global instance() { - ScriptObject global = Context.getGlobal(); - if (! (global instanceof Global)) { - throw new IllegalStateException("no current global instance"); - } - return (Global)global; + Global global = Context.getGlobal(); + global.getClass(); // null check + return global; } /** @@ -494,19 +455,30 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { return instance().getContext(); } - // GlobalObject interface implementation + // Runtime interface to Global - @Override + /** + * Is this global of the given Context? + * @param ctxt the context + * @return true if this global belongs to the given Context + */ public boolean isOfContext(final Context ctxt) { return this.context == ctxt; } - @Override + /** + * Does this global belong to a strict Context? + * @return true if this global belongs to a strict Context + */ public boolean isStrictContext() { return context.getEnv()._strict; } - @Override + /** + * Initialize standard builtin objects like "Object", "Array", "Function" etc. + * as well as our extension builtin objects like "Java", "JSAdapter" as properties + * of the global scope object. + */ public void initBuiltinObjects() { if (this.builtinObject != null) { // already initialized, just return @@ -516,12 +488,26 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { init(); } - @Override + /** + * Create a new ScriptFunction object + * + * @param name function name + * @param handle invocation handle for function + * @param scope the scope + * @param strict are we in strict mode + * + * @return new script function + */ public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) { - return new ScriptFunctionImpl(name, handle, scope, null, strict, false, true); + return new ScriptFunctionImpl(name, handle, scope, null, strict ? ScriptFunctionData.IS_STRICT_CONSTRUCTOR : ScriptFunctionData.IS_CONSTRUCTOR); } - @Override + /** + * Wrap a Java object as corresponding script object + * + * @param obj object to wrap + * @return wrapped object + */ public Object wrapAsObject(final Object obj) { if (obj instanceof Boolean) { return new NativeBoolean((Boolean)obj, this); @@ -543,7 +529,14 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { } } - @Override + /** + * Lookup helper for JS primitive types + * + * @param request the link request for the dynamic call site. + * @param self self reference + * + * @return guarded invocation + */ public GuardedInvocation primitiveLookup(final LinkRequest request, final Object self) { if (self instanceof String || self instanceof ConsString) { return NativeString.lookupPrimitive(request, self); @@ -555,12 +548,23 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { throw new IllegalArgumentException("Unsupported primitive: " + self); } - @Override + /** + * Create a new empty script object + * + * @return the new ScriptObject + */ public ScriptObject newObject() { - return new JO(getObjectPrototype(), getObjectMap()); + return new JO(getObjectPrototype(), JO.getInitialMap()); } - @Override + /** + * Default value of given type + * + * @param sobj script object + * @param typeHint type hint + * + * @return default value + */ public Object getDefaultValue(final ScriptObject sobj, final Class<?> typeHint) { // When the [[DefaultValue]] internal method of O is called with no hint, // then it behaves as if the hint were Number, unless O is a Date object @@ -620,7 +624,12 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { return UNDEFINED; } - @Override + /** + * Is the given ScriptObject an ECMAScript Error object? + * + * @param sobj the object being checked + * @return true if sobj is an Error object + */ public boolean isError(final ScriptObject sobj) { final ScriptObject errorProto = getErrorPrototype(); ScriptObject proto = sobj.getProto(); @@ -633,52 +642,108 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { return false; } - @Override + /** + * Create a new ECMAScript Error object. + * + * @param msg error message + * @return newly created Error object + */ public ScriptObject newError(final String msg) { return new NativeError(msg, this); } - @Override + /** + * Create a new ECMAScript EvalError object. + * + * @param msg error message + * @return newly created EvalError object + */ public ScriptObject newEvalError(final String msg) { return new NativeEvalError(msg, this); } - @Override + /** + * Create a new ECMAScript RangeError object. + * + * @param msg error message + * @return newly created RangeError object + */ public ScriptObject newRangeError(final String msg) { return new NativeRangeError(msg, this); } - @Override + /** + * Create a new ECMAScript ReferenceError object. + * + * @param msg error message + * @return newly created ReferenceError object + */ public ScriptObject newReferenceError(final String msg) { return new NativeReferenceError(msg, this); } - @Override + /** + * Create a new ECMAScript SyntaxError object. + * + * @param msg error message + * @return newly created SyntaxError object + */ public ScriptObject newSyntaxError(final String msg) { return new NativeSyntaxError(msg, this); } - @Override + /** + * Create a new ECMAScript TypeError object. + * + * @param msg error message + * @return newly created TypeError object + */ public ScriptObject newTypeError(final String msg) { return new NativeTypeError(msg, this); } - @Override + /** + * Create a new ECMAScript URIError object. + * + * @param msg error message + * @return newly created URIError object + */ public ScriptObject newURIError(final String msg) { return new NativeURIError(msg, this); } - @Override + /** + * Create a new ECMAScript GenericDescriptor object. + * + * @param configurable is the property configurable? + * @param enumerable is the property enumerable? + * @return newly created GenericDescriptor object + */ public PropertyDescriptor newGenericDescriptor(final boolean configurable, final boolean enumerable) { return new GenericPropertyDescriptor(configurable, enumerable, this); } - @Override + /** + * Create a new ECMAScript DatePropertyDescriptor object. + * + * @param value of the data property + * @param configurable is the property configurable? + * @param enumerable is the property enumerable? + * @return newly created DataPropertyDescriptor object + */ public PropertyDescriptor newDataDescriptor(final Object value, final boolean configurable, final boolean enumerable, final boolean writable) { return new DataPropertyDescriptor(configurable, enumerable, writable, value, this); } - @Override + /** + * Create a new ECMAScript AccessorPropertyDescriptor object. + * + * @param get getter function of the user accessor property + * @param set setter function of the user accessor property + * @param configurable is the property configurable? + * @param enumerable is the property enumerable? + * @return newly created AccessorPropertyDescriptor object + */ public PropertyDescriptor newAccessorDescriptor(final Object get, final Object set, final boolean configurable, final boolean enumerable) { final AccessorPropertyDescriptor desc = new AccessorPropertyDescriptor(configurable, enumerable, get == null ? UNDEFINED : get, set == null ? UNDEFINED : set, this); @@ -694,62 +759,6 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { } - /** - * Cache for compiled script classes. - */ - @SuppressWarnings("serial") - private static class ClassCache extends LinkedHashMap<Source, ClassReference> { - private final int size; - private final ReferenceQueue<Class<?>> queue; - - ClassCache(int size) { - super(size, 0.75f, true); - this.size = size; - this.queue = new ReferenceQueue<>(); - } - - void cache(final Source source, final Class<?> clazz) { - put(source, new ClassReference(clazz, queue, source)); - } - - @Override - protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { - return size() > size; - } - - @Override - public ClassReference get(Object key) { - for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { - remove(ref.source); - } - return super.get(key); - } - - } - - private static class ClassReference extends SoftReference<Class<?>> { - private final Source source; - - ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { - super(clazz, queue); - this.source = source; - } - } - - // Class cache management - @Override - public Class<?> findCachedClass(final Source source) { - assert classCache != null : "Class cache used without being initialized"; - ClassReference ref = classCache.get(source); - return ref != null ? ref.get() : null; - } - - @Override - public void cacheClass(final Source source, final Class<?> clazz) { - assert classCache != null : "Class cache used without being initialized"; - classCache.cache(source, clazz); - } - private static <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) { final T obj = map.get(key); if (obj != null) { @@ -767,14 +776,25 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { private final Map<Object, InvokeByName> namedInvokers = new ConcurrentHashMap<>(); - @Override + + /** + * Get cached InvokeByName object for the given key + * @param key key to be associated with InvokeByName object + * @param creator if InvokeByName is absent 'creator' is called to make one (lazy init) + * @return InvokeByName object associated with the key. + */ public InvokeByName getInvokeByName(final Object key, final Callable<InvokeByName> creator) { return getLazilyCreatedValue(key, creator, namedInvokers); } private final Map<Object, MethodHandle> dynamicInvokers = new ConcurrentHashMap<>(); - @Override + /** + * Get cached dynamic method handle for the given key + * @param key key to be associated with dynamic method handle + * @param creator if method handle is absent 'creator' is called to make one (lazy init) + * @return dynamic method handle associated with the key. + */ public MethodHandle getDynamicInvoker(final Object key, final Callable<MethodHandle> creator) { return getLazilyCreatedValue(key, creator, dynamicInvokers); } @@ -963,6 +983,10 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { return ScriptFunction.getPrototype(builtinArrayBuffer); } + ScriptObject getDataViewPrototype() { + return ScriptFunction.getPrototype(builtinDataView); + } + ScriptObject getInt8ArrayPrototype() { return ScriptFunction.getPrototype(builtinInt8Array); } @@ -999,123 +1023,6 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { return ScriptFunction.getPrototype(builtinFloat64Array); } - // Builtin PropertyMap accessors - PropertyMap getAccessorPropertyDescriptorMap() { - return accessorPropertyDescriptorMap; - } - - PropertyMap getArrayBufferViewMap() { - return arrayBufferViewMap; - } - - PropertyMap getDataPropertyDescriptorMap() { - return dataPropertyDescriptorMap; - } - - PropertyMap getGenericPropertyDescriptorMap() { - return genericPropertyDescriptorMap; - } - - PropertyMap getArgumentsMap() { - return nativeArgumentsMap; - } - - PropertyMap getArrayMap() { - return nativeArrayMap; - } - - PropertyMap getArrayBufferMap() { - return nativeArrayBufferMap; - } - - PropertyMap getBooleanMap() { - return nativeBooleanMap; - } - - PropertyMap getDateMap() { - return nativeDateMap; - } - - PropertyMap getErrorMap() { - return nativeErrorMap; - } - - PropertyMap getEvalErrorMap() { - return nativeEvalErrorMap; - } - - PropertyMap getJSAdapterMap() { - return nativeJSAdapterMap; - } - - PropertyMap getJavaImporterMap() { - return nativeJavaImporterMap; - } - - PropertyMap getNumberMap() { - return nativeNumberMap; - } - - PropertyMap getRangeErrorMap() { - return nativeRangeErrorMap; - } - - PropertyMap getReferenceErrorMap() { - return nativeReferenceErrorMap; - } - - PropertyMap getRegExpMap() { - return nativeRegExpMap; - } - - PropertyMap getRegExpExecResultMap() { - return nativeRegExpExecResultMap; - } - - PropertyMap getStrictArgumentsMap() { - return nativeStrictArgumentsMap; - } - - PropertyMap getStringMap() { - return nativeStringMap; - } - - PropertyMap getSyntaxErrorMap() { - return nativeSyntaxErrorMap; - } - - PropertyMap getTypeErrorMap() { - return nativeTypeErrorMap; - } - - PropertyMap getURIErrorMap() { - return nativeURIErrorMap; - } - - PropertyMap getPrototypeObjectMap() { - return prototypeObjectMap; - } - - PropertyMap getObjectMap() { - return objectMap; - } - - PropertyMap getFunctionMap() { - return functionMap; - } - - PropertyMap getAnonymousFunctionMap() { - return anonymousFunctionMap; - } - - PropertyMap getStrictFunctionMap() { - return strictFunctionMap; - } - - PropertyMap getBoundFunctionMap() { - return boundFunctionMap; - } - private ScriptFunction getBuiltinArray() { return builtinArray; } @@ -1631,14 +1538,11 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { final ScriptEnvironment env = getContext().getEnv(); - // duplicate PropertyMaps of Native* classes - copyInitialMaps(env); - // initialize Function and Object constructor initFunctionAndObject(); // Now fix Global's own proto. - this.setProto(getObjectPrototype()); + this.setInitialProto(getObjectPrototype()); // initialize global function properties this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL); @@ -1705,8 +1609,25 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { initScripting(env); } - if (Context.DEBUG && System.getSecurityManager() == null) { - initDebug(); + if (Context.DEBUG) { + boolean debugOkay; + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkPermission(new RuntimePermission(Context.NASHORN_DEBUG_MODE)); + debugOkay = true; + } catch (final SecurityException ignored) { + // if no permission, don't initialize Debug object + debugOkay = false; + } + + } else { + debugOkay = true; + } + + if (debugOkay) { + initDebug(); + } } copyBuiltins(); @@ -1766,7 +1687,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { final ScriptObject prototype = ScriptFunction.getPrototype(cons); prototype.set(NativeError.NAME, name, false); prototype.set(NativeError.MESSAGE, "", false); - prototype.setProto(errorProto); + prototype.setInitialProto(errorProto); return (ScriptFunction)cons; } @@ -1834,6 +1755,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { private void initTypedArray() { this.builtinArrayBuffer = initConstructor("ArrayBuffer"); + this.builtinDataView = initConstructor("DataView"); this.builtinInt8Array = initConstructor("Int8Array"); this.builtinUint8Array = initConstructor("Uint8Array"); this.builtinUint8ClampedArray = initConstructor("Uint8ClampedArray"); @@ -1874,6 +1796,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { this.typeError = this.builtinTypeError; this.uriError = this.builtinURIError; this.arrayBuffer = this.builtinArrayBuffer; + this.dataView = this.builtinDataView; this.int8Array = this.builtinInt8Array; this.uint8Array = this.builtinUint8Array; this.uint8ClampedArray = this.builtinUint8ClampedArray; @@ -1938,7 +1861,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { } if (res.getProto() == null) { - res.setProto(getObjectPrototype()); + res.setInitialProto(getObjectPrototype()); } return res; @@ -1948,46 +1871,6 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { } } - private void copyInitialMaps(final ScriptEnvironment env) { - this.accessorPropertyDescriptorMap = AccessorPropertyDescriptor.getInitialMap().duplicate(); - this.dataPropertyDescriptorMap = DataPropertyDescriptor.getInitialMap().duplicate(); - this.genericPropertyDescriptorMap = GenericPropertyDescriptor.getInitialMap().duplicate(); - this.nativeArgumentsMap = NativeArguments.getInitialMap().duplicate(); - this.nativeArrayMap = NativeArray.getInitialMap().duplicate(); - this.nativeBooleanMap = NativeBoolean.getInitialMap().duplicate(); - this.nativeDateMap = NativeDate.getInitialMap().duplicate(); - this.nativeErrorMap = NativeError.getInitialMap().duplicate(); - this.nativeEvalErrorMap = NativeEvalError.getInitialMap().duplicate(); - this.nativeJSAdapterMap = NativeJSAdapter.getInitialMap().duplicate(); - this.nativeNumberMap = NativeNumber.getInitialMap().duplicate(); - this.nativeRangeErrorMap = NativeRangeError.getInitialMap().duplicate(); - this.nativeReferenceErrorMap = NativeReferenceError.getInitialMap().duplicate(); - this.nativeRegExpMap = NativeRegExp.getInitialMap().duplicate(); - this.nativeRegExpExecResultMap = NativeRegExpExecResult.getInitialMap().duplicate(); - this.nativeStrictArgumentsMap = NativeStrictArguments.getInitialMap().duplicate(); - this.nativeStringMap = NativeString.getInitialMap().duplicate(); - this.nativeSyntaxErrorMap = NativeSyntaxError.getInitialMap().duplicate(); - this.nativeTypeErrorMap = NativeTypeError.getInitialMap().duplicate(); - this.nativeURIErrorMap = NativeURIError.getInitialMap().duplicate(); - this.prototypeObjectMap = PrototypeObject.getInitialMap().duplicate(); - this.objectMap = JO.getInitialMap().duplicate(); - this.functionMap = ScriptFunctionImpl.getInitialMap().duplicate(); - this.anonymousFunctionMap = ScriptFunctionImpl.getInitialAnonymousMap().duplicate(); - this.strictFunctionMap = ScriptFunctionImpl.getInitialStrictMap().duplicate(); - this.boundFunctionMap = ScriptFunctionImpl.getInitialBoundMap().duplicate(); - - // java - if (! env._no_java) { - this.nativeJavaImporterMap = NativeJavaImporter.getInitialMap().duplicate(); - } - - // typed arrays - if (! env._no_typed_arrays) { - this.arrayBufferViewMap = ArrayBufferView.getInitialMap().duplicate(); - this.nativeArrayBufferMap = NativeArrayBuffer.getInitialMap().duplicate(); - } - } - // Function and Object constructors are inter-dependent. Also, // Function.prototype // functions are not properly initialized. We fix the references here. @@ -2005,13 +1888,13 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { // Function.prototype === Object.getPrototypeOf(Function) === // <anon-function> - builtinFunction.setProto(anon); + builtinFunction.setInitialProto(anon); builtinFunction.setPrototype(anon); anon.set("constructor", builtinFunction, false); 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, false, false, false); + 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")); @@ -2021,7 +1904,14 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { this.builtinObject = (ScriptFunction)initConstructor("Object"); final ScriptObject ObjectPrototype = getObjectPrototype(); // Object.getPrototypeOf(Function.prototype) === Object.prototype - anon.setProto(ObjectPrototype); + anon.setInitialProto(ObjectPrototype); + + // ES6 draft compliant __proto__ property of Object.prototype + // accessors on Object.prototype for "__proto__" + final ScriptFunction getProto = ScriptFunctionImpl.makeFunction("getProto", ScriptObject.GETPROTO); + final ScriptFunction setProto = ScriptFunctionImpl.makeFunction("setProto", ScriptObject.SETPROTOCHECK); + ObjectPrototype.addOwnProperty("__proto__", Attribute.NOT_ENUMERABLE, getProto, setProto); + // Function valued properties of Function.prototype were not properly // initialized. Because, these were created before global.function and @@ -2033,10 +1923,10 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { if (value instanceof ScriptFunction && value != anon) { final ScriptFunction func = (ScriptFunction)value; - func.setProto(getFunctionPrototype()); + func.setInitialProto(getFunctionPrototype()); final ScriptObject prototype = ScriptFunction.getPrototype(func); if (prototype != null) { - prototype.setProto(ObjectPrototype); + prototype.setInitialProto(ObjectPrototype); } } } @@ -2051,7 +1941,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { final ScriptFunction func = (ScriptFunction)value; final ScriptObject prototype = ScriptFunction.getPrototype(func); if (prototype != null) { - prototype.setProto(ObjectPrototype); + prototype.setInitialProto(ObjectPrototype); } } } @@ -2069,7 +1959,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope { final ScriptFunction func = (ScriptFunction)value; final ScriptObject prototype = ScriptFunction.getPrototype(func); if (prototype != null) { - prototype.setProto(ObjectPrototype); + prototype.setInitialProto(ObjectPrototype); } } } diff --git a/src/jdk/nashorn/internal/objects/NativeArguments.java b/src/jdk/nashorn/internal/objects/NativeArguments.java index f8535732..03f68bf6 100644 --- a/src/jdk/nashorn/internal/objects/NativeArguments.java +++ b/src/jdk/nashorn/internal/objects/NativeArguments.java @@ -68,7 +68,7 @@ public final class NativeArguments extends ScriptObject { final ArrayList<Property> properties = new ArrayList<>(2); properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH)); properties.add(AccessorProperty.create("callee", Property.NOT_ENUMERABLE, G$CALLEE, S$CALLEE)); - map$ = PropertyMap.newMap(properties).setIsShared(); + map$ = PropertyMap.newMap(properties); } static PropertyMap getInitialMap() { @@ -267,9 +267,9 @@ public final class NativeArguments extends ScriptObject { final Global global = Global.instance(); final ScriptObject proto = global.getObjectPrototype(); if (isStrict) { - return new NativeStrictArguments(arguments, numParams, proto, global.getStrictArgumentsMap()); + return new NativeStrictArguments(arguments, numParams, proto, NativeStrictArguments.getInitialMap()); } - return new NativeArguments(arguments, callee, numParams, proto, global.getArgumentsMap()); + return new NativeArguments(arguments, callee, numParams, proto, NativeArguments.getInitialMap()); } /** diff --git a/src/jdk/nashorn/internal/objects/NativeArray.java b/src/jdk/nashorn/internal/objects/NativeArray.java index 67b9f1f1..6f07cb9d 100644 --- a/src/jdk/nashorn/internal/objects/NativeArray.java +++ b/src/jdk/nashorn/internal/objects/NativeArray.java @@ -31,6 +31,7 @@ import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator; import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; import java.lang.invoke.MethodHandle; import java.util.ArrayList; @@ -156,10 +157,6 @@ public final class NativeArray extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - /* * Constructors. */ @@ -208,7 +205,7 @@ public final class NativeArray extends ScriptObject { } NativeArray(final ArrayData arrayData, final Global global) { - super(global.getArrayPrototype(), global.getArrayMap()); + super(global.getArrayPrototype(), $nasgenmap$); this.setArray(arrayData); this.setIsArray(); } @@ -354,6 +351,27 @@ public final class NativeArray extends ScriptObject { } /** + * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in + * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set + * method in such cases. This is because set method uses inherited setters (if any) + * from any object in proto chain such as Array.prototype, Object.prototype. + * This method directly sets a particular element value in the current object. + * + * @param index key for property + * @param value value to define + */ + @Override + public final void defineOwnProperty(final int index, final Object value) { + assert isValidArrayIndex(index) : "invalid array index"; + final long longIndex = ArrayIndex.toLongIndex(index); + if (longIndex >= getArray().length()) { + // make array big enough to hold.. + setArray(getArray().ensure(longIndex)); + } + setArray(getArray().set(index, value, false)); + } + + /** * Return the array contents upcasted as an ObjectArray, regardless of * representation * @@ -371,7 +389,7 @@ public final class NativeArray extends ScriptObject { * @return true if argument is an array */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object isArray(final Object self, final Object arg) { + public static boolean isArray(final Object self, final Object arg) { return isArray(arg) || (arg instanceof JSObject && ((JSObject)arg).isArray()); } @@ -470,7 +488,7 @@ public final class NativeArray extends ScriptObject { * @return locale specific string representation for array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLocaleString(final Object self) { + public static String toLocaleString(final Object self) { final StringBuilder sb = new StringBuilder(); final Iterator<Object> iter = arrayLikeIterator(self, true); @@ -516,7 +534,7 @@ public final class NativeArray extends ScriptObject { * @return the new NativeArray */ @Constructor(arity = 1) - public static Object construct(final boolean newObj, final Object self, final Object... args) { + public static NativeArray construct(final boolean newObj, final Object self, final Object... args) { switch (args.length) { case 0: return new NativeArray(0); @@ -569,7 +587,7 @@ public final class NativeArray extends ScriptObject { * @return the new NativeArray */ @SpecializedConstructor - public static Object construct(final boolean newObj, final Object self) { + public static NativeArray construct(final boolean newObj, final Object self) { return new NativeArray(0); } @@ -584,7 +602,7 @@ public final class NativeArray extends ScriptObject { * @return the new NativeArray */ @SpecializedConstructor - public static Object construct(final boolean newObj, final Object self, final int length) { + public static NativeArray construct(final boolean newObj, final Object self, final int length) { if (length >= 0) { return new NativeArray(length); } @@ -603,7 +621,7 @@ public final class NativeArray extends ScriptObject { * @return the new NativeArray */ @SpecializedConstructor - public static Object construct(final boolean newObj, final Object self, final long length) { + public static NativeArray construct(final boolean newObj, final Object self, final long length) { if (length >= 0L && length <= JSType.MAX_UINT) { return new NativeArray(length); } @@ -622,7 +640,7 @@ public final class NativeArray extends ScriptObject { * @return the new NativeArray */ @SpecializedConstructor - public static Object construct(final boolean newObj, final Object self, final double length) { + public static NativeArray construct(final boolean newObj, final Object self, final double length) { final long uint32length = JSType.toUint32(length); if (uint32length == length) { @@ -640,7 +658,7 @@ public final class NativeArray extends ScriptObject { * @return resulting NativeArray */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object concat(final Object self, final Object... args) { + public static NativeArray concat(final Object self, final Object... args) { final ArrayList<Object> list = new ArrayList<>(); concatToList(list, Global.toObject(self)); @@ -687,7 +705,7 @@ public final class NativeArray extends ScriptObject { * @return string representation after join */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object join(final Object self, final Object separator) { + public static String join(final Object self, final Object separator) { final StringBuilder sb = new StringBuilder(); final Iterator<Object> iter = arrayLikeIterator(self, true); final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator); @@ -955,7 +973,7 @@ public final class NativeArray extends ScriptObject { * @return sorted array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object sort(final Object self, final Object comparefn) { + public static ScriptObject sort(final Object self, final Object comparefn) { try { final ScriptObject sobj = (ScriptObject) self; final long len = JSType.toUint32(sobj.getLength()); @@ -1159,7 +1177,7 @@ public final class NativeArray extends ScriptObject { * @return index of element, or -1 if not found */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object indexOf(final Object self, final Object searchElement, final Object fromIndex) { + public static long indexOf(final Object self, final Object searchElement, final Object fromIndex) { try { final ScriptObject sobj = (ScriptObject)Global.toObject(self); final long len = JSType.toUint32(sobj.getLength()); @@ -1195,7 +1213,7 @@ public final class NativeArray extends ScriptObject { * @return index of element, or -1 if not found */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object lastIndexOf(final Object self, final Object... args) { + public static long lastIndexOf(final Object self, final Object... args) { try { final ScriptObject sobj = (ScriptObject)Global.toObject(self); final long len = JSType.toUint32(sobj.getLength()); @@ -1230,7 +1248,7 @@ public final class NativeArray extends ScriptObject { * @return true if callback function return true for every element in the array, false otherwise */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object every(final Object self, final Object callbackfn, final Object thisArg) { + public static boolean every(final Object self, final Object callbackfn, final Object thisArg) { return applyEvery(Global.toObject(self), callbackfn, thisArg); } @@ -1254,7 +1272,7 @@ public final class NativeArray extends ScriptObject { * @return true if callback function returned true for any element in the array, false otherwise */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object some(final Object self, final Object callbackfn, final Object thisArg) { + public static boolean some(final Object self, final Object callbackfn, final Object thisArg) { return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) { private final MethodHandle someInvoker = getSOME_CALLBACK_INVOKER(); @@ -1295,7 +1313,7 @@ public final class NativeArray extends ScriptObject { * @return array with elements transformed by map function */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object map(final Object self, final Object callbackfn, final Object thisArg) { + public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) { return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) { private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER(); @@ -1324,7 +1342,7 @@ public final class NativeArray extends ScriptObject { * @return filtered array */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object filter(final Object self, final Object callbackfn, final Object thisArg) { + public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) { return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) { private long to = 0; private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER(); diff --git a/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java b/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java index d75a2129..70c97daf 100644 --- a/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java +++ b/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java @@ -25,6 +25,7 @@ package jdk.nashorn.internal.objects; +import java.nio.ByteBuffer; import java.util.Arrays; import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; @@ -43,12 +44,8 @@ final class NativeArrayBuffer extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { + public static NativeArrayBuffer constructor(final boolean newObj, final Object self, final Object... args) { if (args.length == 0) { throw new RuntimeException("missing length argument"); } @@ -57,7 +54,7 @@ final class NativeArrayBuffer extends ScriptObject { } protected NativeArrayBuffer(final byte[] byteArray, final Global global) { - super(global.getArrayBufferPrototype(), global.getArrayBufferMap()); + super(global.getArrayBufferPrototype(), $nasgenmap$); this.buffer = byteArray; } @@ -84,7 +81,7 @@ final class NativeArrayBuffer extends ScriptObject { } @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object slice(final Object self, final Object begin0, final Object end0) { + public static NativeArrayBuffer slice(final Object self, final Object begin0, final Object end0) { final NativeArrayBuffer arrayBuffer = (NativeArrayBuffer)self; int begin = JSType.toInt32(begin0); int end = end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : arrayBuffer.getByteLength(); @@ -128,4 +125,16 @@ final class NativeArrayBuffer extends ScriptObject { public int getByteLength() { return buffer.length; } + + ByteBuffer getBuffer() { + return ByteBuffer.wrap(buffer); + } + + ByteBuffer getBuffer(final int offset) { + return ByteBuffer.wrap(buffer, offset, buffer.length - offset); + } + + ByteBuffer getBuffer(final int offset, final int length) { + return ByteBuffer.wrap(buffer, offset, length); + } } diff --git a/src/jdk/nashorn/internal/objects/NativeBoolean.java b/src/jdk/nashorn/internal/objects/NativeBoolean.java index bb57cc71..d645d6dd 100644 --- a/src/jdk/nashorn/internal/objects/NativeBoolean.java +++ b/src/jdk/nashorn/internal/objects/NativeBoolean.java @@ -30,6 +30,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.objects.annotations.Attribute; @@ -50,22 +51,21 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup; public final class NativeBoolean extends ScriptObject { private final boolean value; - final static MethodHandle WRAPFILTER = findWrapFilter(); + // Method handle to create an object wrapper for a primitive boolean + private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class)); + // Method handle to retrieve the Boolean prototype object + private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - private NativeBoolean(final boolean value, final ScriptObject proto, final PropertyMap map) { super(proto, map); this.value = value; } NativeBoolean(final boolean flag, final Global global) { - this(flag, global.getBooleanPrototype(), global.getBooleanMap()); + this(flag, global.getBooleanPrototype(), $nasgenmap$); } NativeBoolean(final boolean flag) { @@ -110,7 +110,7 @@ public final class NativeBoolean extends ScriptObject { * @return string representation of this boolean */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toString(final Object self) { + public static String toString(final Object self) { return getBoolean(self).toString(); } @@ -121,7 +121,7 @@ public final class NativeBoolean extends ScriptObject { * @return value of this boolean */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object valueOf(final Object self) { + public static boolean valueOf(final Object self) { return getBoolean(self); } @@ -164,7 +164,7 @@ public final class NativeBoolean extends ScriptObject { * @return Link to be invoked at call site. */ public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) { - return PrimitiveLookup.lookupPrimitive(request, Boolean.class, new NativeBoolean((Boolean)receiver), WRAPFILTER); + return PrimitiveLookup.lookupPrimitive(request, Boolean.class, new NativeBoolean((Boolean)receiver), WRAPFILTER, PROTOFILTER); } /** @@ -178,7 +178,12 @@ public final class NativeBoolean extends ScriptObject { return new NativeBoolean((Boolean)receiver); } - private static MethodHandle findWrapFilter() { - return MH.findStatic(MethodHandles.lookup(), NativeBoolean.class, "wrapFilter", MH.type(NativeBoolean.class, Object.class)); + @SuppressWarnings("unused") + private static Object protoFilter(final Object object) { + return Global.instance().getBooleanPrototype(); + } + + private static MethodHandle findOwnMH(final String name, final MethodType type) { + return MH.findStatic(MethodHandles.lookup(), NativeBoolean.class, name, type); } } diff --git a/src/jdk/nashorn/internal/objects/NativeDataView.java b/src/jdk/nashorn/internal/objects/NativeDataView.java new file mode 100644 index 00000000..414ff8ae --- /dev/null +++ b/src/jdk/nashorn/internal/objects/NativeDataView.java @@ -0,0 +1,1015 @@ +/* + * Copyright (c) 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.objects; + +import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import jdk.nashorn.internal.objects.annotations.Attribute; +import jdk.nashorn.internal.objects.annotations.Constructor; +import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; +import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.SpecializedConstructor; +import jdk.nashorn.internal.objects.annotations.SpecializedFunction; +import jdk.nashorn.internal.runtime.JSType; +import jdk.nashorn.internal.runtime.PropertyMap; +import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptRuntime; + +/** + * <p> + * DataView builtin constructor. Based on the specification here: + * http://www.khronos.org/registry/typedarray/specs/latest/#8 + * </p> + * <p> + * An ArrayBuffer is a useful object for representing an arbitrary chunk of data. + * In many cases, such data will be read from disk or from the network, and will + * not follow the alignment restrictions that are imposed on the typed array views + * described earlier. In addition, the data will often be heterogeneous in nature + * and have a defined byte order. The DataView view provides a low-level interface + * for reading such data from and writing it to an ArrayBuffer. + * </p> + * <p> + * Regardless of the host computer's endianness, DataView reads or writes values + * to or from main memory with a specified endianness: big or little. + * </p> + */ +@ScriptClass("DataView") +public class NativeDataView extends ScriptObject { + // initialized by nasgen + private static PropertyMap $nasgenmap$; + + // inherited ArrayBufferView properties + + /** + * Underlying ArrayBuffer storage object + */ + @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) + public final Object buffer; + + /** + * The offset in bytes from the start of the ArrayBuffer + */ + @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) + public final int byteOffset; + + /** + * The number of bytes from the offset that this DataView will reference + */ + @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) + public final int byteLength; + + // underlying ByteBuffer + private final ByteBuffer buf; + + private NativeDataView(NativeArrayBuffer arrBuf) { + this(arrBuf, arrBuf.getBuffer(), 0); + } + + private NativeDataView(NativeArrayBuffer arrBuf, int offset) { + this(arrBuf, bufferFrom(arrBuf, offset), offset); + } + + private NativeDataView(NativeArrayBuffer arrBuf, int offset, int length) { + this(arrBuf, bufferFrom(arrBuf, offset, length), offset, length); + } + + private NativeDataView(final NativeArrayBuffer arrBuf, final ByteBuffer buf, final int offset) { + this(arrBuf, buf, offset, buf.capacity() - offset); + } + + private NativeDataView(final NativeArrayBuffer arrBuf, final ByteBuffer buf, final int offset, final int length) { + super(Global.instance().getDataViewPrototype(), $nasgenmap$); + this.buffer = arrBuf; + this.byteOffset = offset; + this.byteLength = length; + this.buf = buf; + } + + /** + * Create a new DataView object using the passed ArrayBuffer for its + * storage. Optional byteOffset and byteLength can be used to limit the + * section of the buffer referenced. The byteOffset indicates the offset in + * bytes from the start of the ArrayBuffer, and the byteLength is the number + * of bytes from the offset that this DataView will reference. If both + * byteOffset and byteLength are omitted, the DataView spans the entire + * ArrayBuffer range. If the byteLength is omitted, the DataView extends from + * the given byteOffset until the end of the ArrayBuffer. + * + * If the given byteOffset and byteLength references an area beyond the end + * of the ArrayBuffer an exception is raised. + + * @param newObj if this constructor was invoked with 'new' or not + * @param self constructor function object + * @param args arguments to the constructor + * @return newly constructed DataView object + */ + @Constructor(arity = 1) + public static NativeDataView constructor(final boolean newObj, final Object self, final Object... args) { + if (args.length == 0 || !(args[0] instanceof NativeArrayBuffer)) { + throw typeError("not.an.arraybuffer.in.dataview"); + } + + final NativeArrayBuffer arrBuf = (NativeArrayBuffer) args[0]; + switch (args.length) { + case 1: + return new NativeDataView(arrBuf); + case 2: + return new NativeDataView(arrBuf, JSType.toInt32(args[1])); + default: + return new NativeDataView(arrBuf, JSType.toInt32(args[1]), JSType.toInt32(args[2])); + } + } + + /** + * Specialized version of DataView constructor + * + * @param newObj if this constructor was invoked with 'new' or not + * @param self constructor function object + * @param arrBuf underlying ArrayBuffer storage object + * @param offset offset in bytes from the start of the ArrayBuffer + * @return newly constructed DataView object + */ + @SpecializedConstructor + public static NativeDataView constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset) { + if (!(arrBuf instanceof NativeArrayBuffer)) { + throw typeError("not.an.arraybuffer.in.dataview"); + } + return new NativeDataView((NativeArrayBuffer) arrBuf, offset); + } + + /** + * Specialized version of DataView constructor + * + * @param newObj if this constructor was invoked with 'new' or not + * @param self constructor function object + * @param arrBuf underlying ArrayBuffer storage object + * @param offset in bytes from the start of the ArrayBuffer + * @param length is the number of bytes from the offset that this DataView will reference + * @return newly constructed DataView object + */ + @SpecializedConstructor + public static NativeDataView constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset, final int length) { + if (!(arrBuf instanceof NativeArrayBuffer)) { + throw typeError("not.an.arraybuffer.in.dataview"); + } + return new NativeDataView((NativeArrayBuffer) arrBuf, offset, length); + } + + // Gets the value of the given type at the specified byte offset + // from the start of the view. There is no alignment constraint; + // multi-byte values may be fetched from any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether a big-endian or little-endian value should be + // read. If false or undefined, a big-endian value is read. + // + // These methods raise an exception if they would read + // beyond the end of the view. + + /** + * Get 8-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 8-bit signed int value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE) + public static int getInt8(final Object self, final Object byteOffset) { + try { + return getBuffer(self).get(JSType.toInt32(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 8-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 8-bit signed int value at the byteOffset + */ + @SpecializedFunction + public static int getInt8(final Object self, final int byteOffset) { + try { + return getBuffer(self).get(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 8-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 8-bit unsigned int value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE) + public static int getUint8(final Object self, final Object byteOffset) { + try { + return (0xFF & getBuffer(self).get(JSType.toInt32(byteOffset))); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 8-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 8-bit unsigned int value at the byteOffset + */ + @SpecializedFunction + public static int getUint8(final Object self, final int byteOffset) { + try { + return (0xFF & getBuffer(self).get(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 16-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 16-bit signed int value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) + public static int getInt16(final Object self, final Object byteOffset, final Object littleEndian) { + try { + return getBuffer(self, littleEndian).getShort(JSType.toInt32(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 16-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 16-bit signed int value at the byteOffset + */ + @SpecializedFunction + public static int getInt16(final Object self, final int byteOffset) { + try { + return getBuffer(self, false).getShort(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 16-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 16-bit signed int value at the byteOffset + */ + @SpecializedFunction + public static int getInt16(final Object self, final int byteOffset, final boolean littleEndian) { + try { + return getBuffer(self, littleEndian).getShort(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 16-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 16-bit unsigned int value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) + public static int getUint16(final Object self, final Object byteOffset, final Object littleEndian) { + try { + return (int) (0xFFFF & getBuffer(self, littleEndian).getShort(JSType.toInt32(byteOffset))); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 16-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 16-bit unsigned int value at the byteOffset + */ + @SpecializedFunction + public static int getUint16(final Object self, final int byteOffset) { + try { + return (int) (0xFFFF & getBuffer(self, false).getShort(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 16-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 16-bit unsigned int value at the byteOffset + */ + @SpecializedFunction + public static int getUint16(final Object self, final int byteOffset, final boolean littleEndian) { + try { + return (int) (0xFFFF & getBuffer(self, littleEndian).getShort(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 32-bit signed int value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) + public static int getInt32(final Object self, final Object byteOffset, final Object littleEndian) { + try { + return getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 32-bit signed int value at the byteOffset + */ + @SpecializedFunction + public static int getInt32(final Object self, final int byteOffset) { + try { + return getBuffer(self, false).getInt(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit signed int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 32-bit signed int value at the byteOffset + */ + @SpecializedFunction + public static int getInt32(final Object self, final int byteOffset, final boolean littleEndian) { + try { + return getBuffer(self, littleEndian).getInt(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 32-bit unsigned int value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) + public static long getUint32(final Object self, final Object byteOffset, final Object littleEndian) { + try { + return (long) (0xFFFFFFFFL & getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset))); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 32-bit unsigned int value at the byteOffset + */ + @SpecializedFunction + public static long getUint32(final Object self, final int byteOffset) { + try { + return (long) (0xFFFFFFFFL & getBuffer(self, false).getInt(JSType.toInt32(byteOffset))); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit unsigned int from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 32-bit unsigned int value at the byteOffset + */ + @SpecializedFunction + public static long getUint32(final Object self, final int byteOffset, final boolean littleEndian) { + try { + return (long) (0xFFFFFFFFL & getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset))); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit float value from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 32-bit float value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) + public static double getFloat32(final Object self, final Object byteOffset, final Object littleEndian) { + try { + return getBuffer(self, littleEndian).getFloat(JSType.toInt32(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit float value from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 32-bit float value at the byteOffset + */ + @SpecializedFunction + public static double getFloat32(final Object self, final int byteOffset) { + try { + return getBuffer(self, false).getFloat(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 32-bit float value from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 32-bit float value at the byteOffset + */ + @SpecializedFunction + public static double getFloat32(final Object self, final int byteOffset, final boolean littleEndian) { + try { + return getBuffer(self, littleEndian).getFloat(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 64-bit float value from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 64-bit float value at the byteOffset + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) + public static double getFloat64(final Object self, final Object byteOffset, final Object littleEndian) { + try { + return getBuffer(self, littleEndian).getDouble(JSType.toInt32(byteOffset)); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 64-bit float value from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @return 64-bit float value at the byteOffset + */ + @SpecializedFunction + public static double getFloat64(final Object self, final int byteOffset) { + try { + return getBuffer(self, false).getDouble(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Get 64-bit float value from given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param littleEndian (optional) flag indicating whether to read in little endian order + * @return 64-bit float value at the byteOffset + */ + @SpecializedFunction + public static double getFloat64(final Object self, final int byteOffset, final boolean littleEndian) { + try { + return getBuffer(self, littleEndian).getDouble(byteOffset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + // Stores a value of the given type at the specified byte offset + // from the start of the view. There is no alignment constraint; + // multi-byte values may be stored at any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether the value should be stored in big-endian or + // little-endian byte order. If false or undefined, the value is + // stored in big-endian byte order. + // + // These methods raise an exception if they would write + // beyond the end of the view. + + /** + * Set 8-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param value byte value to set + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setInt8(final Object self, final Object byteOffset, final Object value) { + try { + getBuffer(self).put(JSType.toInt32(byteOffset), (byte)JSType.toInt32(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 8-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to read from + * @param value byte value to set + * @return undefined + */ + @SpecializedFunction + public static Object setInt8(final Object self, final int byteOffset, final int value) { + try { + getBuffer(self).put(byteOffset, (byte)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 8-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value byte value to set + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setUint8(final Object self, final Object byteOffset, final Object value) { + try { + getBuffer(self).put(JSType.toInt32(byteOffset), (byte)JSType.toInt32(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 8-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value byte value to set + * @return undefined + */ + @SpecializedFunction + public static Object setUint8(final Object self, final int byteOffset, final int value) { + try { + getBuffer(self).put(byteOffset, (byte)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 16-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value short value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setInt16(final Object self, final Object byteOffset, final Object value, final Object littleEndian) { + try { + getBuffer(self, littleEndian).putShort(JSType.toInt32(byteOffset), (short)JSType.toInt32(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 16-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value short value to set + * @return undefined + */ + @SpecializedFunction + public static Object setInt16(final Object self, final int byteOffset, final int value) { + try { + getBuffer(self, false).putShort(byteOffset, (short)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 16-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value short value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @SpecializedFunction + public static Object setInt16(final Object self, final int byteOffset, final int value, final boolean littleEndian) { + try { + getBuffer(self, littleEndian).putShort(byteOffset, (short)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 16-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value short value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setUint16(final Object self, final Object byteOffset, final Object value, final Object littleEndian) { + try { + getBuffer(self, littleEndian).putShort(JSType.toInt32(byteOffset), (short)JSType.toInt32(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 16-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value short value to set + * @return undefined + */ + @SpecializedFunction + public static Object setUint16(final Object self, final int byteOffset, final int value) { + try { + getBuffer(self, false).putShort(byteOffset, (short)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 16-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value short value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @SpecializedFunction + public static Object setUint16(final Object self, final int byteOffset, final int value, final boolean littleEndian) { + try { + getBuffer(self, littleEndian).putShort(byteOffset, (short)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value int value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setInt32(final Object self, final Object byteOffset, final Object value, final Object littleEndian) { + try { + getBuffer(self, littleEndian).putInt(JSType.toInt32(byteOffset), (int)JSType.toInt32(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value int value to set + * @return undefined + */ + @SpecializedFunction + public static Object setInt32(final Object self, final int byteOffset, final int value) { + try { + getBuffer(self, false).putInt(byteOffset, value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit signed int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value int value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @SpecializedFunction + public static Object setInt32(final Object self, final int byteOffset, final int value, final boolean littleEndian) { + try { + getBuffer(self, littleEndian).putInt(byteOffset, value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value int value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setUint32(final Object self, final Object byteOffset, final Object value, final Object littleEndian) { + try { + getBuffer(self, littleEndian).putInt(JSType.toInt32(byteOffset), (int)JSType.toUint32(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value int value to set + * @return undefined + */ + @SpecializedFunction + public static Object setUint32(final Object self, final int byteOffset, final long value) { + try { + getBuffer(self, false).putInt(byteOffset, (int)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit unsigned int at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value int value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @SpecializedFunction + public static Object setUint32(final Object self, final int byteOffset, final long value, final boolean littleEndian) { + try { + getBuffer(self, littleEndian).putInt(byteOffset, (int)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit float at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value float value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setFloat32(final Object self, final Object byteOffset, final Object value, final Object littleEndian) { + try { + getBuffer(self, littleEndian).putFloat((int)JSType.toUint32(byteOffset), (float)JSType.toNumber(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit float at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value float value to set + * @return undefined + */ + @SpecializedFunction + public static Object setFloat32(final Object self, final int byteOffset, final double value) { + try { + getBuffer(self, false).putFloat(byteOffset, (float)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 32-bit float at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value float value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @SpecializedFunction + public static Object setFloat32(final Object self, final int byteOffset, final double value, final boolean littleEndian) { + try { + getBuffer(self, littleEndian).putFloat(byteOffset, (float)value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 64-bit float at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value double value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) + public static Object setFloat64(final Object self, final Object byteOffset, final Object value, final Object littleEndian) { + try { + getBuffer(self, littleEndian).putDouble((int)JSType.toUint32(byteOffset), JSType.toNumber(value)); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 64-bit float at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value double value to set + * @return undefined + */ + @SpecializedFunction + public static Object setFloat64(final Object self, final int byteOffset, final double value) { + try { + getBuffer(self, false).putDouble(byteOffset, value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + /** + * Set 64-bit float at the given byteOffset + * + * @param self DataView object + * @param byteOffset byte offset to write at + * @param value double value to set + * @param littleEndian (optional) flag indicating whether to write in little endian order + * @return undefined + */ + @SpecializedFunction + public static Object setFloat64(final Object self, final int byteOffset, final double value, final boolean littleEndian) { + try { + getBuffer(self, littleEndian).putDouble(byteOffset, value); + return UNDEFINED; + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.offset"); + } + } + + // internals only below this point + private static ByteBuffer bufferFrom(final NativeArrayBuffer nab, final int offset) { + try { + return nab.getBuffer(offset); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.constructor.offset"); + } + } + + private static ByteBuffer bufferFrom(final NativeArrayBuffer nab, final int offset, final int length) { + try { + return nab.getBuffer(offset, length); + } catch (final IndexOutOfBoundsException ioe) { + throw rangeError(ioe, "dataview.constructor.offset"); + } + } + + private static NativeDataView checkSelf(final Object self) { + if (!(self instanceof NativeDataView)) { + throw typeError("not.an.arraybuffer", ScriptRuntime.safeToString(self)); + } + return (NativeDataView)self; + } + + private static ByteBuffer getBuffer(final Object self) { + return checkSelf(self).buf; + } + + private static ByteBuffer getBuffer(final Object self, final Object littleEndian) { + return getBuffer(self, JSType.toBoolean(littleEndian)); + } + + private static ByteBuffer getBuffer(final Object self, final boolean littleEndian) { + return getBuffer(self).order(littleEndian? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); + } +} diff --git a/src/jdk/nashorn/internal/objects/NativeDate.java b/src/jdk/nashorn/internal/objects/NativeDate.java index 935285f0..b3661df7 100644 --- a/src/jdk/nashorn/internal/objects/NativeDate.java +++ b/src/jdk/nashorn/internal/objects/NativeDate.java @@ -114,10 +114,6 @@ public final class NativeDate extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - private NativeDate(final double time, final ScriptObject proto, final PropertyMap map) { super(proto, map); final ScriptEnvironment env = Global.getEnv(); @@ -127,7 +123,7 @@ public final class NativeDate extends ScriptObject { } NativeDate(final double time, final Global global) { - this(time, global.getDatePrototype(), global.getDateMap()); + this(time, global.getDatePrototype(), $nasgenmap$); } private NativeDate (final double time) { @@ -230,7 +226,7 @@ public final class NativeDate extends ScriptObject { * @return Date interpreted from the string, or NaN for illegal values */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object parse(final Object self, final Object string) { + public static double parse(final Object self, final Object string) { return parseDateString(JSType.toString(string)); } @@ -242,7 +238,7 @@ public final class NativeDate extends ScriptObject { * @return a time clip according to the ECMA specification */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 7, where = Where.CONSTRUCTOR) - public static Object UTC(final Object self, final Object... args) { + public static double UTC(final Object self, final Object... args) { final NativeDate nd = new NativeDate(0); final double[] d = convertCtorArgs(args); final double time = d == null ? Double.NaN : timeClip(makeDate(d)); @@ -257,8 +253,8 @@ public final class NativeDate extends ScriptObject { * @return a Date that points to the current moment in time */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object now(final Object self) { - return (double)System.currentTimeMillis(); + public static long now(final Object self) { + return System.currentTimeMillis(); } /** @@ -268,7 +264,7 @@ public final class NativeDate extends ScriptObject { * @return string value that represents the Date in the current time zone */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toString(final Object self) { + public static String toString(final Object self) { return toStringImpl(self, FORMAT_DATE_TIME); } @@ -279,7 +275,7 @@ public final class NativeDate extends ScriptObject { * @return string value with the "date" part of the Date in the current time zone */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toDateString(final Object self) { + public static String toDateString(final Object self) { return toStringImpl(self, FORMAT_DATE); } @@ -290,7 +286,7 @@ public final class NativeDate extends ScriptObject { * @return string value with "time" part of Date in the current time zone */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toTimeString(final Object self) { + public static String toTimeString(final Object self) { return toStringImpl(self, FORMAT_TIME); } @@ -301,7 +297,7 @@ public final class NativeDate extends ScriptObject { * @return string value that represents the Data in the current time zone and locale */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLocaleString(final Object self) { + public static String toLocaleString(final Object self) { return toStringImpl(self, FORMAT_LOCAL_DATE_TIME); } @@ -312,7 +308,7 @@ public final class NativeDate extends ScriptObject { * @return string value with the "date" part of the Date in the current time zone and locale */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLocaleDateString(final Object self) { + public static String toLocaleDateString(final Object self) { return toStringImpl(self, FORMAT_LOCAL_DATE); } @@ -323,7 +319,7 @@ public final class NativeDate extends ScriptObject { * @return string value with the "time" part of Date in the current time zone and locale */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLocaleTimeString(final Object self) { + public static String toLocaleTimeString(final Object self) { return toStringImpl(self, FORMAT_LOCAL_TIME); } @@ -334,7 +330,7 @@ public final class NativeDate extends ScriptObject { * @return valueOf - a number which is this time value */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object valueOf(final Object self) { + public static double valueOf(final Object self) { final NativeDate nd = getNativeDate(self); return (nd != null) ? nd.getTime() : Double.NaN; } @@ -346,7 +342,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getTime(final Object self) { + public static double getTime(final Object self) { final NativeDate nd = getNativeDate(self); return (nd != null) ? nd.getTime() : Double.NaN; } @@ -369,7 +365,7 @@ public final class NativeDate extends ScriptObject { * @return UTC full year */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCFullYear(final Object self) { + public static double getUTCFullYear(final Object self) { return getUTCField(self, YEAR); } @@ -380,7 +376,7 @@ public final class NativeDate extends ScriptObject { * @return year */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getYear(final Object self) { + public static double getYear(final Object self) { final NativeDate nd = getNativeDate(self); return (nd != null && nd.isValidDate()) ? (yearFromTime(nd.getLocalTime()) - 1900) : Double.NaN; } @@ -392,7 +388,7 @@ public final class NativeDate extends ScriptObject { * @return month */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getMonth(final Object self) { + public static double getMonth(final Object self) { return getField(self, MONTH); } @@ -403,7 +399,7 @@ public final class NativeDate extends ScriptObject { * @return UTC month */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCMonth(final Object self) { + public static double getUTCMonth(final Object self) { return getUTCField(self, MONTH); } @@ -414,7 +410,7 @@ public final class NativeDate extends ScriptObject { * @return date */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getDate(final Object self) { + public static double getDate(final Object self) { return getField(self, DAY); } @@ -425,7 +421,7 @@ public final class NativeDate extends ScriptObject { * @return UTC Date */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCDate(final Object self) { + public static double getUTCDate(final Object self) { return getUTCField(self, DAY); } @@ -436,7 +432,7 @@ public final class NativeDate extends ScriptObject { * @return day */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getDay(final Object self) { + public static double getDay(final Object self) { final NativeDate nd = getNativeDate(self); return (nd != null && nd.isValidDate()) ? weekDay(nd.getLocalTime()) : Double.NaN; } @@ -448,7 +444,7 @@ public final class NativeDate extends ScriptObject { * @return UTC day */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCDay(final Object self) { + public static double getUTCDay(final Object self) { final NativeDate nd = getNativeDate(self); return (nd != null && nd.isValidDate()) ? weekDay(nd.getTime()) : Double.NaN; } @@ -460,7 +456,7 @@ public final class NativeDate extends ScriptObject { * @return hours */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getHours(final Object self) { + public static double getHours(final Object self) { return getField(self, HOUR); } @@ -471,7 +467,7 @@ public final class NativeDate extends ScriptObject { * @return UTC hours */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCHours(final Object self) { + public static double getUTCHours(final Object self) { return getUTCField(self, HOUR); } @@ -482,7 +478,7 @@ public final class NativeDate extends ScriptObject { * @return minutes */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getMinutes(final Object self) { + public static double getMinutes(final Object self) { return getField(self, MINUTE); } @@ -493,7 +489,7 @@ public final class NativeDate extends ScriptObject { * @return UTC minutes */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCMinutes(final Object self) { + public static double getUTCMinutes(final Object self) { return getUTCField(self, MINUTE); } @@ -504,7 +500,7 @@ public final class NativeDate extends ScriptObject { * @return seconds */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getSeconds(final Object self) { + public static double getSeconds(final Object self) { return getField(self, SECOND); } @@ -515,7 +511,7 @@ public final class NativeDate extends ScriptObject { * @return UTC seconds */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCSeconds(final Object self) { + public static double getUTCSeconds(final Object self) { return getUTCField(self, SECOND); } @@ -526,7 +522,7 @@ public final class NativeDate extends ScriptObject { * @return milliseconds */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getMilliseconds(final Object self) { + public static double getMilliseconds(final Object self) { return getField(self, MILLISECOND); } @@ -537,7 +533,7 @@ public final class NativeDate extends ScriptObject { * @return UTC milliseconds */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getUTCMilliseconds(final Object self) { + public static double getUTCMilliseconds(final Object self) { return getUTCField(self, MILLISECOND); } @@ -548,7 +544,7 @@ public final class NativeDate extends ScriptObject { * @return time zone offset or NaN if N/A */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object getTimezoneOffset(final Object self) { + public static double getTimezoneOffset(final Object self) { final NativeDate nd = getNativeDate(self); if (nd != null && nd.isValidDate()) { final long msec = (long) nd.getTime(); @@ -565,7 +561,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object setTime(final Object self, final Object time) { + public static double setTime(final Object self, final Object time) { final NativeDate nd = getNativeDate(self); final double num = timeClip(JSType.toNumber(time)); nd.setTime(num); @@ -580,7 +576,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object setMilliseconds(final Object self, final Object... args) { + public static double setMilliseconds(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, MILLISECOND, args, true); return nd.getTime(); @@ -594,7 +590,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object setUTCMilliseconds(final Object self, final Object... args) { + public static double setUTCMilliseconds(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, MILLISECOND, args, false); return nd.getTime(); @@ -608,7 +604,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) - public static Object setSeconds(final Object self, final Object... args) { + public static double setSeconds(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, SECOND, args, true); return nd.getTime(); @@ -622,7 +618,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) - public static Object setUTCSeconds(final Object self, final Object... args) { + public static double setUTCSeconds(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, SECOND, args, false); return nd.getTime(); @@ -636,7 +632,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) - public static Object setMinutes(final Object self, final Object... args) { + public static double setMinutes(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, MINUTE, args, true); return nd.getTime(); @@ -650,7 +646,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) - public static Object setUTCMinutes(final Object self, final Object... args) { + public static double setUTCMinutes(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, MINUTE, args, false); return nd.getTime(); @@ -664,7 +660,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4) - public static Object setHours(final Object self, final Object... args) { + public static double setHours(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, HOUR, args, true); return nd.getTime(); @@ -678,7 +674,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4) - public static Object setUTCHours(final Object self, final Object... args) { + public static double setUTCHours(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, HOUR, args, false); return nd.getTime(); @@ -692,7 +688,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object setDate(final Object self, final Object... args) { + public static double setDate(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, DAY, args, true); return nd.getTime(); @@ -706,7 +702,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object setUTCDate(final Object self, final Object... args) { + public static double setUTCDate(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, DAY, args, false); return nd.getTime(); @@ -720,7 +716,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) - public static Object setMonth(final Object self, final Object... args) { + public static double setMonth(final Object self, final Object... args) { final NativeDate nd = getNativeDate(self); setFields(nd, MONTH, args, true); return nd.getTime(); @@ -734,7 +730,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) - public static Object setUTCMonth(final Object self, final Object... args) { + public static double setUTCMonth(final Object self, final Object... args) { final NativeDate nd = ensureNativeDate(self); setFields(nd, MONTH, args, false); return nd.getTime(); @@ -748,7 +744,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) - public static Object setFullYear(final Object self, final Object... args) { + public static double setFullYear(final Object self, final Object... args) { final NativeDate nd = ensureNativeDate(self); if (nd.isValidDate()) { setFields(nd, YEAR, args, true); @@ -771,7 +767,7 @@ public final class NativeDate extends ScriptObject { * @return time */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) - public static Object setUTCFullYear(final Object self, final Object... args) { + public static double setUTCFullYear(final Object self, final Object... args) { final NativeDate nd = ensureNativeDate(self); if (nd.isValidDate()) { setFields(nd, YEAR, args, false); @@ -790,7 +786,7 @@ public final class NativeDate extends ScriptObject { * @return NativeDate */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object setYear(final Object self, final Object year) { + public static double setYear(final Object self, final Object year) { final NativeDate nd = getNativeDate(self); if (isNaN(nd.getTime())) { nd.setTime(utc(0, nd.getTimeZone())); @@ -817,7 +813,7 @@ public final class NativeDate extends ScriptObject { * @return string representation of date */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toUTCString(final Object self) { + public static String toUTCString(final Object self) { return toGMTStringImpl(self); } @@ -830,7 +826,7 @@ public final class NativeDate extends ScriptObject { * @return string representation of date */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toGMTString(final Object self) { + public static String toGMTString(final Object self) { return toGMTStringImpl(self); } @@ -841,7 +837,7 @@ public final class NativeDate extends ScriptObject { * @return string representation of date */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toISOString(final Object self) { + public static String toISOString(final Object self) { return toISOStringImpl(self); } @@ -1286,14 +1282,14 @@ public final class NativeDate extends ScriptObject { } } - private static Object getField(final Object self, final int field) { + private static double getField(final Object self, final int field) { final NativeDate nd = getNativeDate(self); - return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getLocalTime()) : Double.NaN; + return (nd != null && nd.isValidDate()) ? (double)valueFromTime(field, nd.getLocalTime()) : Double.NaN; } - private static Object getUTCField(final Object self, final int field) { + private static double getUTCField(final Object self, final int field) { final NativeDate nd = getNativeDate(self); - return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getTime()) : Double.NaN; + return (nd != null && nd.isValidDate()) ? (double)valueFromTime(field, nd.getTime()) : Double.NaN; } private static void setFields(final NativeDate nd, final int fieldId, final Object[] args, final boolean local) { @@ -1348,5 +1344,4 @@ public final class NativeDate extends ScriptObject { private TimeZone getTimeZone() { return timezone; } - } diff --git a/src/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk/nashorn/internal/objects/NativeDebug.java index 9ee7c99a..5d80c37b 100644 --- a/src/jdk/nashorn/internal/objects/NativeDebug.java +++ b/src/jdk/nashorn/internal/objects/NativeDebug.java @@ -34,7 +34,7 @@ import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.ScriptClass; import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.PropertyListenerManager; +import jdk.nashorn.internal.runtime.PropertyListeners; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -116,7 +116,7 @@ public final class NativeDebug extends ScriptObject { * @return true if reference identity */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object identical(final Object self, final Object obj1, final Object obj2) { + public static boolean identical(final Object self, final Object obj1, final Object obj2) { return obj1 == obj2; } @@ -144,7 +144,7 @@ public final class NativeDebug extends ScriptObject { * @return return {@link Object#equals(Object)} for objects. */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object equals(final Object self, final Object obj1, final Object obj2) { + public static boolean equals(final Object self, final Object obj1, final Object obj2) { return Objects.equals(obj1, obj2); } @@ -156,7 +156,7 @@ public final class NativeDebug extends ScriptObject { * @return Java string representation of {@code obj} */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object toJavaString(final Object self, final Object obj) { + public static String toJavaString(final Object self, final Object obj) { return Objects.toString(obj); } @@ -168,7 +168,7 @@ public final class NativeDebug extends ScriptObject { * @return string representation */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object toIdentString(final Object self, final Object obj) { + public static String toIdentString(final Object self, final Object obj) { if (obj == null) { return "null"; } @@ -185,8 +185,8 @@ public final class NativeDebug extends ScriptObject { * @return listener count */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object getListenerCount(final Object self, final Object obj) { - return (obj instanceof ScriptObject)? ((ScriptObject)obj).getListenerCount() : 0; + public static int getListenerCount(final Object self, final Object obj) { + return (obj instanceof ScriptObject) ? PropertyListeners.getListenerCount((ScriptObject) obj) : 0; } /** @@ -203,14 +203,13 @@ public final class NativeDebug extends ScriptObject { out.println("ScriptObject count " + ScriptObject.getCount()); out.println("Scope count " + ScriptObject.getScopeCount()); - out.println("ScriptObject listeners added " + PropertyListenerManager.getListenersAdded()); - out.println("ScriptObject listeners removed " + PropertyListenerManager.getListenersRemoved()); + out.println("ScriptObject listeners added " + PropertyListeners.getListenersAdded()); + out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved()); out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount()); out.println("ScriptFunction invokes " + ScriptFunction.getInvokes()); out.println("ScriptFunction allocations " + ScriptFunction.getAllocations()); out.println("PropertyMap count " + PropertyMap.getCount()); out.println("PropertyMap cloned " + PropertyMap.getClonedCount()); - out.println("PropertyMap shared " + PropertyMap.getSharedCount()); out.println("PropertyMap duplicated " + PropertyMap.getDuplicatedCount()); out.println("PropertyMap history hit " + PropertyMap.getHistoryHit()); out.println("PropertyMap proto invalidations " + PropertyMap.getProtoInvalidations()); diff --git a/src/jdk/nashorn/internal/objects/NativeError.java b/src/jdk/nashorn/internal/objects/NativeError.java index d3e2effe..1b6b8094 100644 --- a/src/jdk/nashorn/internal/objects/NativeError.java +++ b/src/jdk/nashorn/internal/objects/NativeError.java @@ -38,7 +38,6 @@ import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; import jdk.nashorn.internal.objects.annotations.Where; -import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.PropertyMap; @@ -75,7 +74,7 @@ public final class NativeError extends ScriptObject { static final String FILENAME = "__fileName__"; /** Message property name */ - @Property(name = NativeError.MESSAGE) + @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) public Object instMessage; /** ECMA 15.11.4.2 Error.prototype.name */ @@ -86,13 +85,14 @@ public final class NativeError extends ScriptObject { @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) public Object message; + /** Nashorn extension: underlying exception */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public Object nashornException; + // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - + @SuppressWarnings("LeakingThisInConstructor") private NativeError(final Object msg, final ScriptObject proto, final PropertyMap map) { super(proto, map); if (msg != UNDEFINED) { @@ -100,10 +100,11 @@ public final class NativeError extends ScriptObject { } else { this.delete(NativeError.MESSAGE, false); } + initException(this); } NativeError(final Object msg, final Global global) { - this(msg, global.getErrorPrototype(), global.getErrorMap()); + this(msg, global.getErrorPrototype(), $nasgenmap$); } private NativeError(final Object msg) { @@ -125,10 +126,18 @@ public final class NativeError extends ScriptObject { * @return NativeError instance */ @Constructor - public static Object constructor(final boolean newObj, final Object self, final Object msg) { + public static NativeError constructor(final boolean newObj, final Object self, final Object msg) { return new NativeError(msg); } + // This is called NativeError, NativeTypeError etc. to + // associate a ECMAException with the ECMA Error object. + @SuppressWarnings("unused") + static void initException(final ScriptObject self) { + // ECMAException constructor has side effects + new ECMAException(self, null); + } + /** * Nashorn extension: Error.captureStackTrace. Capture stack trace at the point of call into the Error object provided. * @@ -136,16 +145,17 @@ public final class NativeError extends ScriptObject { * @param errorObj the error object * @return undefined */ - @SuppressWarnings("unused") @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object captureStackTrace(final Object self, final Object errorObj) { Global.checkObject(errorObj); final ScriptObject sobj = (ScriptObject)errorObj; - new ECMAException(sobj, null); //constructor has side effects - sobj.delete("stack", false); - final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", GET_STACK); - final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", SET_STACK); - sobj.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); + 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); + sobj.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); + } return UNDEFINED; } @@ -226,7 +236,11 @@ public final class NativeError extends ScriptObject { public static Object setLineNumber(final Object self, final Object value) { Global.checkObject(self); final ScriptObject sobj = (ScriptObject)self; - sobj.set(LINENUMBER, value, false); + if (sobj.hasOwnProperty(LINENUMBER)) { + sobj.put(LINENUMBER, value, false); + } else { + sobj.addOwnProperty(LINENUMBER, Attribute.NOT_ENUMERABLE, value); + } return value; } @@ -254,7 +268,11 @@ public final class NativeError extends ScriptObject { public static Object setColumnNumber(final Object self, final Object value) { Global.checkObject(self); final ScriptObject sobj = (ScriptObject)self; - sobj.set(COLUMNNUMBER, value, false); + if (sobj.hasOwnProperty(COLUMNNUMBER)) { + sobj.put(COLUMNNUMBER, value, false); + } else { + sobj.addOwnProperty(COLUMNNUMBER, Attribute.NOT_ENUMERABLE, value); + } return value; } @@ -282,7 +300,11 @@ public final class NativeError extends ScriptObject { public static Object setFileName(final Object self, final Object value) { Global.checkObject(self); final ScriptObject sobj = (ScriptObject)self; - sobj.set(FILENAME, value, false); + if (sobj.hasOwnProperty(FILENAME)) { + sobj.put(FILENAME, value, false); + } else { + sobj.addOwnProperty(FILENAME, Attribute.NOT_ENUMERABLE, value); + } return value; } @@ -304,10 +326,12 @@ public final class NativeError extends ScriptObject { final Object exception = ECMAException.getException(sobj); if (exception instanceof Throwable) { - return getScriptStackString(sobj, (Throwable)exception); + Object value = getScriptStackString(sobj, (Throwable)exception); + sobj.put(STACK, value, false); + return value; } - return ""; + return UNDEFINED; } /** diff --git a/src/jdk/nashorn/internal/objects/NativeEvalError.java b/src/jdk/nashorn/internal/objects/NativeEvalError.java index 89e9485f..586c7cff 100644 --- a/src/jdk/nashorn/internal/objects/NativeEvalError.java +++ b/src/jdk/nashorn/internal/objects/NativeEvalError.java @@ -44,7 +44,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; public final class NativeEvalError extends ScriptObject { /** message property in instance */ - @Property(name = NativeError.MESSAGE) + @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) public Object instMessage; /** error name property */ @@ -55,13 +55,14 @@ public final class NativeEvalError extends ScriptObject { @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) public Object message; + /** Nashorn extension: underlying exception */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public Object nashornException; + // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - + @SuppressWarnings("LeakingThisInConstructor") private NativeEvalError(final Object msg, final ScriptObject proto, final PropertyMap map) { super(proto, map); if (msg != UNDEFINED) { @@ -69,10 +70,11 @@ public final class NativeEvalError extends ScriptObject { } else { this.delete(NativeError.MESSAGE, false); } + NativeError.initException(this); } NativeEvalError(final Object msg, final Global global) { - this(msg, global.getEvalErrorPrototype(), global.getEvalErrorMap()); + this(msg, global.getEvalErrorPrototype(), $nasgenmap$); } private NativeEvalError(final Object msg) { @@ -96,7 +98,7 @@ public final class NativeEvalError extends ScriptObject { * @return new EvalError */ @Constructor(name = "EvalError") - public static Object constructor(final boolean newObj, final Object self, final Object msg) { + public static NativeEvalError constructor(final boolean newObj, final Object self, final Object msg) { return new NativeEvalError(msg); } } diff --git a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java index b96c81c9..a9dfb7a4 100644 --- a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java +++ b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java @@ -136,8 +136,8 @@ public final class NativeFloat32Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeFloat32Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeFloat32Array)constructorImpl(args, FACTORY); } NativeFloat32Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -192,8 +192,8 @@ public final class NativeFloat32Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeFloat32Array subarray(final Object self, final Object begin, final Object end) { + return (NativeFloat32Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java index af9251f4..61b58807 100644 --- a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java +++ b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java @@ -146,8 +146,8 @@ public final class NativeFloat64Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeFloat64Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeFloat64Array)constructorImpl(args, FACTORY); } NativeFloat64Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -202,8 +202,8 @@ public final class NativeFloat64Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeFloat64Array subarray(final Object self, final Object begin, final Object end) { + return (NativeFloat64Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeFunction.java b/src/jdk/nashorn/internal/objects/NativeFunction.java index d092cfb6..013683e0 100644 --- a/src/jdk/nashorn/internal/objects/NativeFunction.java +++ b/src/jdk/nashorn/internal/objects/NativeFunction.java @@ -27,6 +27,7 @@ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.Source.sourceFor; import java.util.List; @@ -71,7 +72,7 @@ public final class NativeFunction { * @return string representation of Function */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toString(final Object self) { + public static String toString(final Object self) { if (!(self instanceof ScriptFunction)) { throw typeError("not.a.function", ScriptRuntime.safeToString(self)); } @@ -174,7 +175,7 @@ public final class NativeFunction { * @return function with bound arguments */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object bind(final Object self, final Object... args) { + public static ScriptFunction bind(final Object self, final Object... args) { if (!(self instanceof ScriptFunction)) { throw typeError("not.a.function", ScriptRuntime.safeToString(self)); } @@ -199,7 +200,7 @@ public final class NativeFunction { * @return source for function */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toSource(final Object self) { + public static String toSource(final Object self) { if (!(self instanceof ScriptFunction)) { throw typeError("not.a.function", ScriptRuntime.safeToString(self)); } @@ -217,7 +218,7 @@ public final class NativeFunction { * @return new NativeFunction */ @Constructor(arity = 1) - public static Object function(final boolean newObj, final Object self, final Object... args) { + public static ScriptFunction function(final boolean newObj, final Object self, final Object... args) { final StringBuilder sb = new StringBuilder(); sb.append("(function ("); @@ -253,11 +254,11 @@ public final class NativeFunction { final Global global = Global.instance(); - return Global.directEval(global, sb.toString(), global, "<function>", global.isStrictContext()); + return (ScriptFunction)Global.directEval(global, sb.toString(), global, "<function>", global.isStrictContext()); } private static void checkFunctionParameters(final String params) { - final Source src = new Source("<function>", params); + final Source src = sourceFor("<function>", params); final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager()); try { parser.parseFormalParameterList(); @@ -267,7 +268,7 @@ public final class NativeFunction { } private static void checkFunctionBody(final String funcBody) { - final Source src = new Source("<function>", funcBody); + final Source src = sourceFor("<function>", funcBody); final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager()); try { parser.parseFunctionBody(); diff --git a/src/jdk/nashorn/internal/objects/NativeInt16Array.java b/src/jdk/nashorn/internal/objects/NativeInt16Array.java index e24691af..f6aa2054 100644 --- a/src/jdk/nashorn/internal/objects/NativeInt16Array.java +++ b/src/jdk/nashorn/internal/objects/NativeInt16Array.java @@ -100,8 +100,8 @@ public final class NativeInt16Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeInt16Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeInt16Array)constructorImpl(args, FACTORY); } NativeInt16Array(final NativeArrayBuffer buffer, final int byteOffset, final int byteLength) { @@ -151,8 +151,8 @@ public final class NativeInt16Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeInt16Array subarray(final Object self, final Object begin, final Object end) { + return (NativeInt16Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeInt32Array.java b/src/jdk/nashorn/internal/objects/NativeInt32Array.java index b25f8495..643bd816 100644 --- a/src/jdk/nashorn/internal/objects/NativeInt32Array.java +++ b/src/jdk/nashorn/internal/objects/NativeInt32Array.java @@ -103,8 +103,8 @@ public final class NativeInt32Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeInt32Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeInt32Array)constructorImpl(args, FACTORY); } NativeInt32Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -154,8 +154,8 @@ public final class NativeInt32Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeInt32Array subarray(final Object self, final Object begin, final Object end) { + return (NativeInt32Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeInt8Array.java b/src/jdk/nashorn/internal/objects/NativeInt8Array.java index e5069107..5822c6d5 100644 --- a/src/jdk/nashorn/internal/objects/NativeInt8Array.java +++ b/src/jdk/nashorn/internal/objects/NativeInt8Array.java @@ -93,8 +93,8 @@ public final class NativeInt8Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeInt8Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeInt8Array)constructorImpl(args, FACTORY); } NativeInt8Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -144,8 +144,8 @@ public final class NativeInt8Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeInt8Array subarray(final Object self, final Object begin, final Object end) { + return (NativeInt8Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java index 668ca8cd..84036a32 100644 --- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java +++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java @@ -146,10 +146,6 @@ public final class NativeJSAdapter extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - NativeJSAdapter(final Object overrides, final ScriptObject adaptee, final ScriptObject proto, final PropertyMap map) { super(proto, map); this.adaptee = wrapAdaptee(adaptee); @@ -163,7 +159,7 @@ public final class NativeJSAdapter extends ScriptObject { } private static ScriptObject wrapAdaptee(final ScriptObject adaptee) { - return new JO(adaptee, Global.instance().getObjectMap()); + return new JO(adaptee, JO.getInitialMap()); } @Override @@ -540,7 +536,7 @@ public final class NativeJSAdapter extends ScriptObject { * @return new NativeJSAdapter */ @Constructor - public static Object construct(final boolean isNew, final Object self, final Object... args) { + public static NativeJSAdapter construct(final boolean isNew, final Object self, final Object... args) { Object proto = UNDEFINED; Object overrides = UNDEFINED; Object adaptee; @@ -577,7 +573,7 @@ public final class NativeJSAdapter extends ScriptObject { proto = global.getJSAdapterPrototype(); } - return new NativeJSAdapter(overrides, (ScriptObject)adaptee, (ScriptObject)proto, global.getJSAdapterMap()); + return new NativeJSAdapter(overrides, (ScriptObject)adaptee, (ScriptObject)proto, $nasgenmap$); } @Override @@ -622,14 +618,14 @@ public final class NativeJSAdapter extends ScriptObject { case "getMethod": final FindProperty find = adaptee.findProperty(__call__, true); if (find != null) { - final Object value = getObjectValue(find); + final Object value = find.getObjectValue(); if (value instanceof ScriptFunction) { final ScriptFunctionImpl func = (ScriptFunctionImpl)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), - adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), __call__), + adaptee.getProtoSwitchPoint(__call__, find.getOwner()), testJSAdaptor(adaptee, null, null, null)); } } @@ -691,7 +687,7 @@ public final class NativeJSAdapter extends ScriptObject { final MethodType type = desc.getMethodType(); if (findData != null) { final String name = desc.getNameTokenCount() > 2 ? desc.getNameToken(2) : null; - final Object value = getObjectValue(findData); + final Object value = findData.getObjectValue(); if (value instanceof ScriptFunction) { final ScriptFunction func = (ScriptFunction)value; @@ -700,7 +696,7 @@ public final class NativeJSAdapter extends ScriptObject { if (methodHandle != null) { return new GuardedInvocation( methodHandle, - adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook), + adaptee.getProtoSwitchPoint(hook, findData.getOwner()), testJSAdaptor(adaptee, findData.getGetter(Object.class), findData.getOwner(), func)); } } @@ -713,7 +709,7 @@ public final class NativeJSAdapter extends ScriptObject { final MethodHandle methodHandle = hook.equals(__put__) ? MH.asType(Lookup.EMPTY_SETTER, type) : Lookup.emptyGetter(type.returnType()); - return new GuardedInvocation(methodHandle, adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook), testJSAdaptor(adaptee, null, null, null)); + return new GuardedInvocation(methodHandle, adaptee.getProtoSwitchPoint(hook, null), testJSAdaptor(adaptee, null, null, null)); } } diff --git a/src/jdk/nashorn/internal/objects/NativeJava.java b/src/jdk/nashorn/internal/objects/NativeJava.java index b863e24f..7879bab4 100644 --- a/src/jdk/nashorn/internal/objects/NativeJava.java +++ b/src/jdk/nashorn/internal/objects/NativeJava.java @@ -75,7 +75,7 @@ public final class NativeJava { * @see #type(Object, Object) */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object isType(final Object self, final Object type) { + public static boolean isType(final Object self, final Object type) { return type instanceof StaticClass; } @@ -338,7 +338,7 @@ public final class NativeJava { * null. */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object from(final Object self, final Object objArray) { + public static NativeArray from(final Object self, final Object objArray) { if (objArray == null) { return null; } else if (objArray instanceof Collection) { diff --git a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java index 3e46a2c7..40ecbffc 100644 --- a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java +++ b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java @@ -60,17 +60,13 @@ public final class NativeJavaImporter extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - private NativeJavaImporter(final Object[] args, final ScriptObject proto, final PropertyMap map) { super(proto, map); this.args = args; } private NativeJavaImporter(final Object[] args, final Global global) { - this(args, global.getJavaImporterPrototype(), global.getJavaImporterMap()); + this(args, global.getJavaImporterPrototype(), $nasgenmap$); } private NativeJavaImporter(final Object[] args) { @@ -90,7 +86,7 @@ public final class NativeJavaImporter extends ScriptObject { * @return NativeJavaImporter instance */ @Constructor(arity = 1) - public static Object constructor(final boolean isNew, final Object self, final Object... args) { + public static NativeJavaImporter constructor(final boolean isNew, final Object self, final Object... args) { return new NativeJavaImporter(args); } @@ -134,6 +130,11 @@ public final class NativeJavaImporter extends ScriptObject { return createAndSetProperty(desc) ? super.lookup(desc, request) : super.noSuchMethod(desc, request); } + @Override + protected Object invokeNoSuchProperty(final String name) { + return createProperty(name); + } + private boolean createAndSetProperty(final CallSiteDescriptor desc) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); final Object value = createProperty(name); diff --git a/src/jdk/nashorn/internal/objects/NativeMath.java b/src/jdk/nashorn/internal/objects/NativeMath.java index cc50fbb4..ec53965a 100644 --- a/src/jdk/nashorn/internal/objects/NativeMath.java +++ b/src/jdk/nashorn/internal/objects/NativeMath.java @@ -92,7 +92,7 @@ public final class NativeMath extends ScriptObject { * @return abs of value */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object abs(final Object self, final Object x) { + public static double abs(final Object self, final Object x) { return Math.abs(JSType.toNumber(x)); } @@ -144,7 +144,7 @@ public final class NativeMath extends ScriptObject { * @return acos of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object acos(final Object self, final Object x) { + public static double acos(final Object self, final Object x) { return Math.acos(JSType.toNumber(x)); } @@ -170,7 +170,7 @@ public final class NativeMath extends ScriptObject { * @return asin of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object asin(final Object self, final Object x) { + public static double asin(final Object self, final Object x) { return Math.asin(JSType.toNumber(x)); } @@ -196,7 +196,7 @@ public final class NativeMath extends ScriptObject { * @return atan of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object atan(final Object self, final Object x) { + public static double atan(final Object self, final Object x) { return Math.atan(JSType.toNumber(x)); } @@ -223,7 +223,7 @@ public final class NativeMath extends ScriptObject { * @return atan2 of x and y */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object atan2(final Object self, final Object y, final Object x) { + public static double atan2(final Object self, final Object y, final Object x) { return Math.atan2(JSType.toNumber(y), JSType.toNumber(x)); } @@ -250,7 +250,7 @@ public final class NativeMath extends ScriptObject { * @return ceil of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object ceil(final Object self, final Object x) { + public static double ceil(final Object self, final Object x) { return Math.ceil(JSType.toNumber(x)); } @@ -302,7 +302,7 @@ public final class NativeMath extends ScriptObject { * @return cos of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object cos(final Object self, final Object x) { + public static double cos(final Object self, final Object x) { return Math.cos(JSType.toNumber(x)); } @@ -328,7 +328,7 @@ public final class NativeMath extends ScriptObject { * @return exp of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object exp(final Object self, final Object x) { + public static double exp(final Object self, final Object x) { return Math.exp(JSType.toNumber(x)); } @@ -341,7 +341,7 @@ public final class NativeMath extends ScriptObject { * @return floor of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object floor(final Object self, final Object x) { + public static double floor(final Object self, final Object x) { return Math.floor(JSType.toNumber(x)); } @@ -393,7 +393,7 @@ public final class NativeMath extends ScriptObject { * @return log of argument */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object log(final Object self, final Object x) { + public static double log(final Object self, final Object x) { return Math.log(JSType.toNumber(x)); } @@ -419,7 +419,7 @@ public final class NativeMath extends ScriptObject { * @return the largest of the arguments, {@link Double#NEGATIVE_INFINITY} if no args given, or identity if one arg is given */ @Function(arity = 2, attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object max(final Object self, final Object... args) { + public static double max(final Object self, final Object... args) { switch (args.length) { case 0: return Double.NEGATIVE_INFINITY; @@ -497,7 +497,7 @@ public final class NativeMath extends ScriptObject { * @return the smallest of the arguments, {@link Double#NEGATIVE_INFINITY} if no args given, or identity if one arg is given */ @Function(arity = 2, attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object min(final Object self, final Object... args) { + public static double min(final Object self, final Object... args) { switch (args.length) { case 0: return Double.POSITIVE_INFINITY; @@ -576,7 +576,7 @@ public final class NativeMath extends ScriptObject { * @return x raised to the power of y */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object pow(final Object self, final Object x, final Object y) { + public static double pow(final Object self, final Object x, final Object y) { return Math.pow(JSType.toNumber(x), JSType.toNumber(y)); } @@ -602,7 +602,7 @@ public final class NativeMath extends ScriptObject { * @return random number in the range [0..1) */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object random(final Object self) { + public static double random(final Object self) { return Math.random(); } @@ -615,7 +615,7 @@ public final class NativeMath extends ScriptObject { * @return x rounded */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object round(final Object self, final Object x) { + public static double round(final Object self, final Object x) { final double d = JSType.toNumber(x); if (Math.getExponent(d) >= 52) { return d; @@ -632,7 +632,7 @@ public final class NativeMath extends ScriptObject { * @return sin of x */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object sin(final Object self, final Object x) { + public static double sin(final Object self, final Object x) { return Math.sin(JSType.toNumber(x)); } @@ -658,7 +658,7 @@ public final class NativeMath extends ScriptObject { * @return sqrt of x */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object sqrt(final Object self, final Object x) { + public static double sqrt(final Object self, final Object x) { return Math.sqrt(JSType.toNumber(x)); } @@ -684,7 +684,7 @@ public final class NativeMath extends ScriptObject { * @return tan of x */ @Function(attributes = Attribute.NOT_ENUMERABLE, where=Where.CONSTRUCTOR) - public static Object tan(final Object self, final Object x) { + public static double tan(final Object self, final Object x) { return Math.tan(JSType.toNumber(x)); } diff --git a/src/jdk/nashorn/internal/objects/NativeNumber.java b/src/jdk/nashorn/internal/objects/NativeNumber.java index d9d568b7..a95587ef 100644 --- a/src/jdk/nashorn/internal/objects/NativeNumber.java +++ b/src/jdk/nashorn/internal/objects/NativeNumber.java @@ -34,6 +34,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.text.NumberFormat; import java.util.Locale; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -57,7 +58,10 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup; @ScriptClass("Number") public final class NativeNumber extends ScriptObject { - static final MethodHandle WRAPFILTER = findWrapFilter(); + // Method handle to create an object wrapper for a primitive number + private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class)); + // Method handle to retrieve the Number prototype object + private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); /** ECMA 15.7.3.2 largest positive finite value */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR) @@ -86,10 +90,6 @@ public final class NativeNumber extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - private NativeNumber(final double value, final ScriptObject proto, final PropertyMap map) { super(proto, map); this.value = value; @@ -98,7 +98,7 @@ public final class NativeNumber extends ScriptObject { } NativeNumber(final double value, final Global global) { - this(value, global.getNumberPrototype(), global.getNumberMap()); + this(value, global.getNumberPrototype(), $nasgenmap$); } private NativeNumber(final double value) { @@ -185,7 +185,7 @@ public final class NativeNumber extends ScriptObject { * @return number in decimal fixed point notation */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toFixed(final Object self, final Object fractionDigits) { + public static String toFixed(final Object self, final Object fractionDigits) { final int f = JSType.toInteger(fractionDigits); if (f < 0 || f > 20) { throw rangeError("invalid.fraction.digits", "toFixed"); @@ -217,7 +217,7 @@ public final class NativeNumber extends ScriptObject { * @return number in decimal exponential notation */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toExponential(final Object self, final Object fractionDigits) { + public static String toExponential(final Object self, final Object fractionDigits) { final double x = getNumberValue(self); final boolean trimZeros = fractionDigits == UNDEFINED; final int f = trimZeros ? 16 : JSType.toInteger(fractionDigits); @@ -245,7 +245,7 @@ public final class NativeNumber extends ScriptObject { * @return number in decimal exponentiation notation or decimal fixed notation depending on {@code precision} */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toPrecision(final Object self, final Object precision) { + public static String toPrecision(final Object self, final Object precision) { final double x = getNumberValue(self); if (precision == UNDEFINED) { return JSType.toString(x); @@ -278,7 +278,7 @@ public final class NativeNumber extends ScriptObject { * @return string representation of this Number in the given radix */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toString(final Object self, final Object radix) { + public static String toString(final Object self, final Object radix) { if (radix != UNDEFINED) { final int intRadix = JSType.toInteger(radix); if (intRadix != 10) { @@ -299,7 +299,7 @@ public final class NativeNumber extends ScriptObject { * @return localized string for this Number */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLocaleString(final Object self) { + public static String toLocaleString(final Object self) { return JSType.toString(getNumberValue(self)); } @@ -308,10 +308,10 @@ public final class NativeNumber extends ScriptObject { * ECMA 15.7.4.4 Number.prototype.valueOf ( ) * * @param self self reference - * @return boxed number value for this Number + * @return number value for this Number */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object valueOf(final Object self) { + public static double valueOf(final Object self) { return getNumberValue(self); } @@ -322,7 +322,7 @@ public final class NativeNumber extends ScriptObject { * @return Link to be invoked at call site. */ public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) { - return PrimitiveLookup.lookupPrimitive(request, Number.class, new NativeNumber(((Number)receiver).doubleValue()), WRAPFILTER); + return PrimitiveLookup.lookupPrimitive(request, Number.class, new NativeNumber(((Number)receiver).doubleValue()), WRAPFILTER, PROTOFILTER); } @SuppressWarnings("unused") @@ -330,6 +330,11 @@ public final class NativeNumber extends ScriptObject { return new NativeNumber(((Number)receiver).doubleValue()); } + @SuppressWarnings("unused") + private static Object protoFilter(final Object object) { + return Global.instance().getNumberPrototype(); + } + private static double getNumberValue(final Object self) { if (self instanceof Number) { return ((Number)self).doubleValue(); @@ -378,7 +383,7 @@ public final class NativeNumber extends ScriptObject { return str; } - private static MethodHandle findWrapFilter() { - return MH.findStatic(MethodHandles.lookup(), NativeNumber.class, "wrapFilter", MH.type(NativeNumber.class, Object.class)); + private static MethodHandle findOwnMH(final String name, final MethodType type) { + return MH.findStatic(MethodHandles.lookup(), NativeNumber.class, name, type); } } diff --git a/src/jdk/nashorn/internal/objects/NativeObject.java b/src/jdk/nashorn/internal/objects/NativeObject.java index d102c0fb..9cde4faf 100644 --- a/src/jdk/nashorn/internal/objects/NativeObject.java +++ b/src/jdk/nashorn/internal/objects/NativeObject.java @@ -31,6 +31,7 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -58,6 +59,7 @@ import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; +import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; import jdk.nashorn.internal.runtime.linker.NashornBeansLinker; @@ -101,6 +103,27 @@ public final class NativeObject { } /** + * Nashorn extension: setIndexedPropertiesToExternalArrayData + * + * @param self self reference + * @param obj object whose index properties are backed by buffer + * @param buf external buffer - should be a nio ByteBuffer + * @return the 'obj' object + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) + public static ScriptObject setIndexedPropertiesToExternalArrayData(final Object self, final Object obj, final Object buf) { + Global.checkObject(obj); + final ScriptObject sobj = (ScriptObject)obj; + if (buf instanceof ByteBuffer) { + sobj.setArray(ArrayData.allocate((ByteBuffer)buf)); + } else { + throw typeError("not.a.bytebuffer", "setIndexedPropertiesToExternalArrayData's buf argument"); + } + return sobj; + } + + + /** * ECMA 15.2.3.2 Object.getPrototypeOf ( O ) * * @param self self reference @@ -180,7 +203,7 @@ public final class NativeObject { * @return array of property names */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object getOwnPropertyNames(final Object self, final Object obj) { + public static ScriptObject getOwnPropertyNames(final Object self, final Object obj) { if (obj instanceof ScriptObject) { return new NativeArray(((ScriptObject)obj).getOwnKeys(true)); } else if (obj instanceof ScriptObjectMirror) { @@ -199,7 +222,7 @@ public final class NativeObject { * @return object created */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object create(final Object self, final Object proto, final Object props) { + public static ScriptObject create(final Object self, final Object proto, final Object props) { if (proto != null) { Global.checkObject(proto); } @@ -225,10 +248,11 @@ public final class NativeObject { * @return object */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object defineProperty(final Object self, final Object obj, final Object prop, final Object attr) { + public static ScriptObject defineProperty(final Object self, final Object obj, final Object prop, final Object attr) { Global.checkObject(obj); - ((ScriptObject)obj).defineOwnProperty(JSType.toString(prop), attr, true); - return obj; + final ScriptObject sobj = (ScriptObject)obj; + sobj.defineOwnProperty(JSType.toString(prop), attr, true); + return sobj; } /** @@ -240,7 +264,7 @@ public final class NativeObject { * @return object */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object defineProperties(final Object self, final Object obj, final Object props) { + public static ScriptObject defineProperties(final Object self, final Object obj, final Object props) { Global.checkObject(obj); final ScriptObject sobj = (ScriptObject)obj; @@ -319,7 +343,7 @@ public final class NativeObject { * @return true if sealed, false otherwise */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object isSealed(final Object self, final Object obj) { + public static boolean isSealed(final Object self, final Object obj) { if (obj instanceof ScriptObject) { return ((ScriptObject)obj).isSealed(); } else if (obj instanceof ScriptObjectMirror) { @@ -337,7 +361,7 @@ public final class NativeObject { * @return true if object is frozen, false otherwise */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object isFrozen(final Object self, final Object obj) { + public static boolean isFrozen(final Object self, final Object obj) { if (obj instanceof ScriptObject) { return ((ScriptObject)obj).isFrozen(); } else if (obj instanceof ScriptObjectMirror) { @@ -355,7 +379,7 @@ public final class NativeObject { * @return true if object is extensible, false otherwise */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object isExtensible(final Object self, final Object obj) { + public static boolean isExtensible(final Object self, final Object obj) { if (obj instanceof ScriptObject) { return ((ScriptObject)obj).isExtensible(); } else if (obj instanceof ScriptObjectMirror) { @@ -373,7 +397,7 @@ public final class NativeObject { * @return array of keys in object */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object keys(final Object self, final Object obj) { + public static ScriptObject keys(final Object self, final Object obj) { if (obj instanceof ScriptObject) { final ScriptObject sobj = (ScriptObject)obj; return new NativeArray(sobj.getOwnKeys(false)); @@ -430,7 +454,7 @@ public final class NativeObject { * @return ToString of object */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toString(final Object self) { + public static String toString(final Object self) { return ScriptRuntime.builtinObjectToString(self); } @@ -483,7 +507,7 @@ public final class NativeObject { * @return true if property exists in object */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object hasOwnProperty(final Object self, final Object v) { + public static boolean hasOwnProperty(final Object self, final Object v) { // Convert ScriptObjects to primitive with String.class hint // but no need to convert other primitives to string. final Object key = JSType.toPrimitive(v, String.class); @@ -500,7 +524,7 @@ public final class NativeObject { * @return true if object is prototype of v */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object isPrototypeOf(final Object self, final Object v) { + public static boolean isPrototypeOf(final Object self, final Object v) { if (!(v instanceof ScriptObject)) { return false; } @@ -526,7 +550,7 @@ public final class NativeObject { * @return true if property is enumerable */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object propertyIsEnumerable(final Object self, final Object v) { + public static boolean propertyIsEnumerable(final Object self, final Object v) { final String str = JSType.toString(v); final Object obj = Global.toObject(self); diff --git a/src/jdk/nashorn/internal/objects/NativeRangeError.java b/src/jdk/nashorn/internal/objects/NativeRangeError.java index d51a0c09..ff3ac7b7 100644 --- a/src/jdk/nashorn/internal/objects/NativeRangeError.java +++ b/src/jdk/nashorn/internal/objects/NativeRangeError.java @@ -44,7 +44,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; public final class NativeRangeError extends ScriptObject { /** message property in instance */ - @Property(name = NativeError.MESSAGE) + @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) public Object instMessage; /** error name property */ @@ -55,13 +55,14 @@ public final class NativeRangeError extends ScriptObject { @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) public Object message; + /** Nashorn extension: underlying exception */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public Object nashornException; + // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - + @SuppressWarnings("LeakingThisInConstructor") private NativeRangeError(final Object msg, final ScriptObject proto, final PropertyMap map) { super(proto, map); if (msg != UNDEFINED) { @@ -69,10 +70,11 @@ public final class NativeRangeError extends ScriptObject { } else { this.delete(NativeError.MESSAGE, false); } + NativeError.initException(this); } NativeRangeError(final Object msg, final Global global) { - this(msg, global.getRangeErrorPrototype(), global.getRangeErrorMap()); + this(msg, global.getRangeErrorPrototype(), $nasgenmap$); } private NativeRangeError(final Object msg) { @@ -96,7 +98,7 @@ public final class NativeRangeError extends ScriptObject { * @return new RangeError */ @Constructor(name = "RangeError") - public static Object constructor(final boolean newObj, final Object self, final Object msg) { + public static NativeRangeError constructor(final boolean newObj, final Object self, final Object msg) { return new NativeRangeError(msg); } } diff --git a/src/jdk/nashorn/internal/objects/NativeReferenceError.java b/src/jdk/nashorn/internal/objects/NativeReferenceError.java index a269b515..74190179 100644 --- a/src/jdk/nashorn/internal/objects/NativeReferenceError.java +++ b/src/jdk/nashorn/internal/objects/NativeReferenceError.java @@ -44,7 +44,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; public final class NativeReferenceError extends ScriptObject { /** message property in instance */ - @Property(name = NativeError.MESSAGE) + @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) public Object instMessage; /** error name property */ @@ -55,13 +55,14 @@ public final class NativeReferenceError extends ScriptObject { @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) public Object message; + /** Nashorn extension: underlying exception */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public Object nashornException; + // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - + @SuppressWarnings("LeakingThisInConstructor") private NativeReferenceError(final Object msg, final ScriptObject proto, final PropertyMap map) { super(proto, map); if (msg != UNDEFINED) { @@ -69,10 +70,11 @@ public final class NativeReferenceError extends ScriptObject { } else { this.delete(NativeError.MESSAGE, false); } + NativeError.initException(this); } NativeReferenceError(final Object msg, final Global global) { - this(msg, global.getReferenceErrorPrototype(), global.getReferenceErrorMap()); + this(msg, global.getReferenceErrorPrototype(), $nasgenmap$); } private NativeReferenceError(final Object msg) { @@ -96,7 +98,7 @@ public final class NativeReferenceError extends ScriptObject { * @return new ReferenceError */ @Constructor(name = "ReferenceError") - public static Object constructor(final boolean newObj, final Object self, final Object msg) { + public static NativeReferenceError constructor(final boolean newObj, final Object self, final Object msg) { return new NativeReferenceError(msg); } } diff --git a/src/jdk/nashorn/internal/objects/NativeRegExp.java b/src/jdk/nashorn/internal/objects/NativeRegExp.java index 318ca2a0..89a9a828 100644 --- a/src/jdk/nashorn/internal/objects/NativeRegExp.java +++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java @@ -70,12 +70,8 @@ public final class NativeRegExp extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - private NativeRegExp(final Global global) { - super(global.getRegExpPrototype(), global.getRegExpMap()); + super(global.getRegExpPrototype(), $nasgenmap$); this.globalObject = global; } @@ -126,7 +122,7 @@ public final class NativeRegExp extends ScriptObject { * @return new NativeRegExp */ @Constructor(arity = 2) - public static Object constructor(final boolean isNew, final Object self, final Object... args) { + public static NativeRegExp constructor(final boolean isNew, final Object self, final Object... args) { if (args.length > 1) { return newRegExp(args[0], args[1]); } else if (args.length > 0) { @@ -146,7 +142,7 @@ public final class NativeRegExp extends ScriptObject { * @return new NativeRegExp */ @SpecializedConstructor - public static Object constructor(final boolean isNew, final Object self) { + public static NativeRegExp constructor(final boolean isNew, final Object self) { return new NativeRegExp("", ""); } @@ -161,7 +157,7 @@ public final class NativeRegExp extends ScriptObject { * @return new NativeRegExp */ @SpecializedConstructor - public static Object constructor(final boolean isNew, final Object self, final Object pattern) { + public static NativeRegExp constructor(final boolean isNew, final Object self, final Object pattern) { return newRegExp(pattern, UNDEFINED); } @@ -177,7 +173,7 @@ public final class NativeRegExp extends ScriptObject { * @return new NativeRegExp */ @SpecializedConstructor - public static Object constructor(final boolean isNew, final Object self, final Object pattern, final Object flags) { + public static NativeRegExp constructor(final boolean isNew, final Object self, final Object pattern, final Object flags) { return newRegExp(pattern, flags); } @@ -287,7 +283,7 @@ public final class NativeRegExp extends ScriptObject { * @return new NativeRegExp */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object compile(final Object self, final Object pattern, final Object flags) { + public static ScriptObject compile(final Object self, final Object pattern, final Object flags) { final NativeRegExp regExp = checkRegExp(self); final NativeRegExp compiled = newRegExp(pattern, flags); // copy over regexp to 'self' @@ -306,7 +302,7 @@ public final class NativeRegExp extends ScriptObject { * @return array containing the matches or {@code null} if no match */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object exec(final Object self, final Object string) { + public static ScriptObject exec(final Object self, final Object string) { return checkRegExp(self).exec(JSType.toString(string)); } @@ -318,7 +314,7 @@ public final class NativeRegExp extends ScriptObject { * @return true if matches found, false otherwise */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object test(final Object self, final Object string) { + public static boolean test(final Object self, final Object string) { return checkRegExp(self).test(JSType.toString(string)); } @@ -329,7 +325,7 @@ public final class NativeRegExp extends ScriptObject { * @return string version of regexp */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toString(final Object self) { + public static String toString(final Object self) { return checkRegExp(self).toString(); } @@ -622,7 +618,7 @@ public final class NativeRegExp extends ScriptObject { * @param string String to match. * @return NativeArray of matches, string or null. */ - public Object exec(final String string) { + public NativeRegExpExecResult exec(final String string) { final RegExpResult match = execInner(string); if (match == null) { @@ -639,7 +635,7 @@ public final class NativeRegExp extends ScriptObject { * @param string String to match. * @return True if a match is found. */ - public Object test(final String string) { + public boolean test(final String string) { return execInner(string) != null; } @@ -653,7 +649,7 @@ public final class NativeRegExp extends ScriptObject { * @param replacement Replacement string. * @return String with substitutions. */ - Object replace(final String string, final String replacement, final ScriptFunction function) { + String replace(final String string, final String replacement, final ScriptFunction function) { final RegExpMatcher matcher = regexp.match(string); if (matcher == null) { @@ -808,7 +804,7 @@ public final class NativeRegExp extends ScriptObject { * @param limit Split limit. * @return Array of substrings. */ - Object split(final String string, final long limit) { + NativeArray split(final String string, final long limit) { if (limit == 0L) { return new NativeArray(); } @@ -871,7 +867,7 @@ public final class NativeRegExp extends ScriptObject { * @param string String to match. * @return Index of match. */ - Object search(final String string) { + int search(final String string) { final RegExpResult match = execInner(string); if (match == null) { diff --git a/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java b/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java index 3508e5f6..f12cea12 100644 --- a/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java +++ b/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java @@ -53,12 +53,8 @@ public final class NativeRegExpExecResult extends ScriptObject { // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - NativeRegExpExecResult(final RegExpResult result, final Global global) { - super(global.getArrayPrototype(), global.getRegExpExecResultMap()); + super(global.getArrayPrototype(), $nasgenmap$); setIsArray(); this.setArray(ArrayData.allocate(result.getGroups().clone())); this.index = result.getIndex(); diff --git a/src/jdk/nashorn/internal/objects/NativeStrictArguments.java b/src/jdk/nashorn/internal/objects/NativeStrictArguments.java index b81cc2af..dd2c2816 100644 --- a/src/jdk/nashorn/internal/objects/NativeStrictArguments.java +++ b/src/jdk/nashorn/internal/objects/NativeStrictArguments.java @@ -60,9 +60,9 @@ public final class NativeStrictArguments extends ScriptObject { // In strict mode, the caller and callee properties should throw TypeError // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; - map = map.addProperty(map.newUserAccessors("caller", flags)); - map = map.addProperty(map.newUserAccessors("callee", flags)); - map$ = map.setIsShared(); + map = map.addPropertyNoHistory(map.newUserAccessors("caller", flags)); + map = map.addPropertyNoHistory(map.newUserAccessors("callee", flags)); + map$ = map; } static PropertyMap getInitialMap() { diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java index 95af71a6..51edf75a 100644 --- a/src/jdk/nashorn/internal/objects/NativeString.java +++ b/src/jdk/nashorn/internal/objects/NativeString.java @@ -32,6 +32,7 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 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.Arrays; @@ -69,21 +70,20 @@ public final class NativeString extends ScriptObject { private final CharSequence value; - static final MethodHandle WRAPFILTER = findWrapFilter(); + // Method handle to create an object wrapper for a primitive string + private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); + // Method handle to retrieve the String prototype object + private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - private NativeString(final CharSequence value) { this(value, Global.instance()); } NativeString(final CharSequence value, final Global global) { - this(value, global.getStringPrototype(), global.getStringMap()); + this(value, global.getStringPrototype(), $nasgenmap$); } private NativeString(final CharSequence value, final ScriptObject proto, final PropertyMap map) { @@ -425,7 +425,7 @@ public final class NativeString extends ScriptObject { * @return string with arguments translated to charcodes */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1, where = Where.CONSTRUCTOR) - public static Object fromCharCode(final Object self, final Object... args) { + public static String fromCharCode(final Object self, final Object... args) { final char[] buf = new char[args.length]; int index = 0; for (final Object arg : args) { @@ -441,7 +441,7 @@ public final class NativeString extends ScriptObject { * @return string with one charcode */ @SpecializedFunction - public static Object fromCharCode(final Object self, final Object value) { + public static String fromCharCode(final Object self, final Object value) { try { return "" + (char)JSType.toUint16(((Number)value).doubleValue()); } catch (final ClassCastException e) { @@ -456,7 +456,7 @@ public final class NativeString extends ScriptObject { * @return string with one charcode */ @SpecializedFunction - public static Object fromCharCode(final Object self, final int value) { + public static String fromCharCode(final Object self, final int value) { return "" + (char)(value & 0xffff); } @@ -467,7 +467,7 @@ public final class NativeString extends ScriptObject { * @return string with one charcode */ @SpecializedFunction - public static Object fromCharCode(final Object self, final long value) { + public static String fromCharCode(final Object self, final long value) { return "" + (char)((int)value & 0xffff); } @@ -478,7 +478,7 @@ public final class NativeString extends ScriptObject { * @return string with one charcode */ @SpecializedFunction - public static Object fromCharCode(final Object self, final double value) { + public static String fromCharCode(final Object self, final double value) { return "" + (char)JSType.toUint16(value); } @@ -488,7 +488,7 @@ public final class NativeString extends ScriptObject { * @return self as string */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toString(final Object self) { + public static String toString(final Object self) { return getString(self); } @@ -498,7 +498,7 @@ public final class NativeString extends ScriptObject { * @return self as string */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object valueOf(final Object self) { + public static String valueOf(final Object self) { return getString(self); } @@ -509,7 +509,7 @@ public final class NativeString extends ScriptObject { * @return string representing the char at the given position */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object charAt(final Object self, final Object pos) { + public static String charAt(final Object self, final Object pos) { return charAtImpl(checkObjectToString(self), JSType.toInteger(pos)); } @@ -546,7 +546,7 @@ public final class NativeString extends ScriptObject { * @return number representing charcode at position */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object charCodeAt(final Object self, final Object pos) { + public static double charCodeAt(final Object self, final Object pos) { return charCodeAtImpl(checkObjectToString(self), JSType.toInteger(pos)); } @@ -601,7 +601,7 @@ public final class NativeString extends ScriptObject { * @return position of first match or -1 */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object indexOf(final Object self, final Object search, final Object pos) { + public static int indexOf(final Object self, final Object search, final Object pos) { final String str = checkObjectToString(self); return str.indexOf(JSType.toString(search), JSType.toInteger(pos)); } @@ -649,7 +649,7 @@ public final class NativeString extends ScriptObject { * @return last position of match or -1 */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) - public static Object lastIndexOf(final Object self, final Object search, final Object pos) { + public static int lastIndexOf(final Object self, final Object search, final Object pos) { final String str = checkObjectToString(self); final String searchStr = JSType.toString(search); @@ -680,7 +680,7 @@ public final class NativeString extends ScriptObject { * @return result of locale sensitive comparison operation between {@code self} and {@code that} */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object localeCompare(final Object self, final Object that) { + public static double localeCompare(final Object self, final Object that) { final String str = checkObjectToString(self); final Collator collator = Collator.getInstance(Global.getEnv()._locale); @@ -698,7 +698,7 @@ public final class NativeString extends ScriptObject { * @return array of regexp matches */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object match(final Object self, final Object regexp) { + public static ScriptObject match(final Object self, final Object regexp) { final String str = checkObjectToString(self); @@ -745,7 +745,7 @@ public final class NativeString extends ScriptObject { * @return string after replacement */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object replace(final Object self, final Object string, final Object replacement) { + public static String replace(final Object self, final Object string, final Object replacement) { final String str = checkObjectToString(self); @@ -771,7 +771,7 @@ public final class NativeString extends ScriptObject { * @return offset where match occurred */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object search(final Object self, final Object string) { + public static int search(final Object self, final Object string) { final String str = checkObjectToString(self); final NativeRegExp nativeRegExp = Global.toRegExp(string == UNDEFINED ? "" : string); @@ -788,7 +788,7 @@ public final class NativeString extends ScriptObject { * @return sliced out substring */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object slice(final Object self, final Object start, final Object end) { + public static String slice(final Object self, final Object start, final Object end) { final String str = checkObjectToString(self); if (end == UNDEFINED) { @@ -805,7 +805,7 @@ public final class NativeString extends ScriptObject { * @return sliced out substring */ @SpecializedFunction - public static Object slice(final Object self, final int start) { + public static String slice(final Object self, final int start) { final String str = checkObjectToString(self); final int from = (start < 0) ? Math.max(str.length() + start, 0) : Math.min(start, str.length()); @@ -820,7 +820,7 @@ public final class NativeString extends ScriptObject { * @return sliced out substring */ @SpecializedFunction - public static Object slice(final Object self, final double start) { + public static String slice(final Object self, final double start) { return slice(self, (int)start); } @@ -833,7 +833,7 @@ public final class NativeString extends ScriptObject { * @return sliced out substring */ @SpecializedFunction - public static Object slice(final Object self, final int start, final int end) { + public static String slice(final Object self, final int start, final int end) { final String str = checkObjectToString(self); final int len = str.length(); @@ -853,7 +853,7 @@ public final class NativeString extends ScriptObject { * @return sliced out substring */ @SpecializedFunction - public static Object slice(final Object self, final double start, final double end) { + public static String slice(final Object self, final double start, final double end) { return slice(self, (int)start, (int)end); } @@ -866,7 +866,7 @@ public final class NativeString extends ScriptObject { * @return array object in which splits have been placed */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object split(final Object self, final Object separator, final Object limit) { + public static ScriptObject split(final Object self, final Object separator, final Object limit) { final String str = checkObjectToString(self); final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit); @@ -882,7 +882,7 @@ public final class NativeString extends ScriptObject { return splitString(str, JSType.toString(separator), lim); } - private static Object splitString(String str, String separator, long limit) { + private static ScriptObject splitString(String str, String separator, long limit) { if (separator.isEmpty()) { final int length = (int) Math.min(str.length(), limit); final Object[] array = new Object[length]; @@ -923,7 +923,7 @@ public final class NativeString extends ScriptObject { * @return substring given start and length of section */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object substr(final Object self, final Object start, final Object length) { + public static String substr(final Object self, final Object start, final Object length) { final String str = JSType.toString(self); final int strLength = str.length(); @@ -946,7 +946,7 @@ public final class NativeString extends ScriptObject { * @return substring given start and end indexes */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object substring(final Object self, final Object start, final Object end) { + public static String substring(final Object self, final Object start, final Object end) { final String str = checkObjectToString(self); if (end == UNDEFINED) { @@ -1026,7 +1026,7 @@ public final class NativeString extends ScriptObject { * @return string to lower case */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLowerCase(final Object self) { + public static String toLowerCase(final Object self) { return checkObjectToString(self).toLowerCase(Locale.ROOT); } @@ -1036,7 +1036,7 @@ public final class NativeString extends ScriptObject { * @return string to locale sensitive lower case */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLocaleLowerCase(final Object self) { + public static String toLocaleLowerCase(final Object self) { return checkObjectToString(self).toLowerCase(Global.getEnv()._locale); } @@ -1046,7 +1046,7 @@ public final class NativeString extends ScriptObject { * @return string to upper case */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toUpperCase(final Object self) { + public static String toUpperCase(final Object self) { return checkObjectToString(self).toUpperCase(Locale.ROOT); } @@ -1056,7 +1056,7 @@ public final class NativeString extends ScriptObject { * @return string to locale sensitive upper case */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object toLocaleUpperCase(final Object self) { + public static String toLocaleUpperCase(final Object self) { return checkObjectToString(self).toUpperCase(Global.getEnv()._locale); } @@ -1066,7 +1066,7 @@ public final class NativeString extends ScriptObject { * @return string trimmed from whitespace */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object trim(final Object self) { + public static String trim(final Object self) { final String str = checkObjectToString(self); int start = 0; @@ -1088,7 +1088,7 @@ public final class NativeString extends ScriptObject { * @return string trimmed left from whitespace */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object trimLeft(final Object self) { + public static String trimLeft(final Object self) { final String str = checkObjectToString(self); int start = 0; @@ -1107,7 +1107,7 @@ public final class NativeString extends ScriptObject { * @return string trimmed right from whitespace */ @Function(attributes = Attribute.NOT_ENUMERABLE) - public static Object trimRight(final Object self) { + public static String trimRight(final Object self) { final String str = checkObjectToString(self); int start = 0; @@ -1120,7 +1120,7 @@ public final class NativeString extends ScriptObject { return str.substring(start, end + 1); } - private static Object newObj(final Object self, final CharSequence str) { + private static ScriptObject newObj(final Object self, final CharSequence str) { return new NativeString(str); } @@ -1199,7 +1199,7 @@ public final class NativeString extends ScriptObject { */ public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) { final MethodHandle guard = NashornGuards.getInstanceOf2Guard(String.class, ConsString.class); - return PrimitiveLookup.lookupPrimitive(request, guard, new NativeString((CharSequence)receiver), WRAPFILTER); + return PrimitiveLookup.lookupPrimitive(request, guard, new NativeString((CharSequence)receiver), WRAPFILTER, PROTOFILTER); } @SuppressWarnings("unused") @@ -1207,6 +1207,11 @@ public final class NativeString extends ScriptObject { return new NativeString((CharSequence)receiver); } + @SuppressWarnings("unused") + private static Object protoFilter(final Object object) { + return Global.instance().getStringPrototype(); + } + private static CharSequence getCharSequence(final Object self) { if (self instanceof String || self instanceof ConsString) { return (CharSequence)self; @@ -1254,7 +1259,7 @@ public final class NativeString extends ScriptObject { return key >= 0 && key < value.length(); } - private static MethodHandle findWrapFilter() { - return MH.findStatic(MethodHandles.lookup(), NativeString.class, "wrapFilter", MH.type(NativeString.class, Object.class)); + private static MethodHandle findOwnMH(final String name, final MethodType type) { + return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type); } } diff --git a/src/jdk/nashorn/internal/objects/NativeSyntaxError.java b/src/jdk/nashorn/internal/objects/NativeSyntaxError.java index 45920ba7..db7debb0 100644 --- a/src/jdk/nashorn/internal/objects/NativeSyntaxError.java +++ b/src/jdk/nashorn/internal/objects/NativeSyntaxError.java @@ -44,7 +44,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; public final class NativeSyntaxError extends ScriptObject { /** message property in instance */ - @Property(name = NativeError.MESSAGE) + @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) public Object instMessage; /** error name property */ @@ -55,20 +55,22 @@ public final class NativeSyntaxError extends ScriptObject { @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) public Object message; + /** Nashorn extension: underlying exception */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public Object nashornException; + // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - + @SuppressWarnings("LeakingThisInConstructor") NativeSyntaxError(final Object msg, final Global global) { - super(global.getSyntaxErrorPrototype(), global.getSyntaxErrorMap()); + super(global.getSyntaxErrorPrototype(), $nasgenmap$); if (msg != UNDEFINED) { this.instMessage = JSType.toString(msg); } else { this.delete(NativeError.MESSAGE, false); } + NativeError.initException(this); } private NativeSyntaxError(final Object msg) { @@ -92,7 +94,7 @@ public final class NativeSyntaxError extends ScriptObject { * @return new SyntaxError */ @Constructor(name = "SyntaxError") - public static Object constructor(final boolean newObj, final Object self, final Object msg) { + public static NativeSyntaxError constructor(final boolean newObj, final Object self, final Object msg) { return new NativeSyntaxError(msg); } } diff --git a/src/jdk/nashorn/internal/objects/NativeTypeError.java b/src/jdk/nashorn/internal/objects/NativeTypeError.java index 2b2308b1..6e5a4934 100644 --- a/src/jdk/nashorn/internal/objects/NativeTypeError.java +++ b/src/jdk/nashorn/internal/objects/NativeTypeError.java @@ -44,7 +44,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; public final class NativeTypeError extends ScriptObject { /** message property in instance */ - @Property(name = NativeError.MESSAGE) + @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) public Object instMessage; /** error name property */ @@ -55,20 +55,22 @@ public final class NativeTypeError extends ScriptObject { @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) public Object message; + /** Nashorn extension: underlying exception */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public Object nashornException; + // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - + @SuppressWarnings("LeakingThisInConstructor") NativeTypeError(final Object msg, final Global global) { - super(global.getTypeErrorPrototype(), global.getTypeErrorMap()); + super(global.getTypeErrorPrototype(), $nasgenmap$); if (msg != UNDEFINED) { this.instMessage = JSType.toString(msg); } else { delete(NativeError.MESSAGE, false); } + NativeError.initException(this); } private NativeTypeError(final Object msg) { @@ -92,7 +94,7 @@ public final class NativeTypeError extends ScriptObject { * @return new TypeError */ @Constructor(name = "TypeError") - public static Object constructor(final boolean newObj, final Object self, final Object msg) { + public static NativeTypeError constructor(final boolean newObj, final Object self, final Object msg) { return new NativeTypeError(msg); } } diff --git a/src/jdk/nashorn/internal/objects/NativeURIError.java b/src/jdk/nashorn/internal/objects/NativeURIError.java index 2caf1369..cf5fdaa0 100644 --- a/src/jdk/nashorn/internal/objects/NativeURIError.java +++ b/src/jdk/nashorn/internal/objects/NativeURIError.java @@ -43,7 +43,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; public final class NativeURIError extends ScriptObject { /** message property in instance */ - @Property(name = NativeError.MESSAGE) + @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) public Object instMessage; /** error name property */ @@ -54,20 +54,22 @@ public final class NativeURIError extends ScriptObject { @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) public Object message; + /** Nashorn extension: underlying exception */ + @Property(attributes = Attribute.NOT_ENUMERABLE) + public Object nashornException; + // initialized by nasgen private static PropertyMap $nasgenmap$; - static PropertyMap getInitialMap() { - return $nasgenmap$; - } - + @SuppressWarnings("LeakingThisInConstructor") NativeURIError(final Object msg, final Global global) { - super(global.getURIErrorPrototype(), global.getURIErrorMap()); + super(global.getURIErrorPrototype(), $nasgenmap$); if (msg != UNDEFINED) { this.instMessage = JSType.toString(msg); } else { this.delete(NativeError.MESSAGE, false); } + NativeError.initException(this); } private NativeURIError(final Object msg) { @@ -91,7 +93,7 @@ public final class NativeURIError extends ScriptObject { * @return new URIError */ @Constructor(name = "URIError") - public static Object constructor(final boolean newObj, final Object self, final Object msg) { + public static NativeURIError constructor(final boolean newObj, final Object self, final Object msg) { return new NativeURIError(msg); } } diff --git a/src/jdk/nashorn/internal/objects/NativeUint16Array.java b/src/jdk/nashorn/internal/objects/NativeUint16Array.java index ee91aef3..8aa3d628 100644 --- a/src/jdk/nashorn/internal/objects/NativeUint16Array.java +++ b/src/jdk/nashorn/internal/objects/NativeUint16Array.java @@ -99,8 +99,8 @@ public final class NativeUint16Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeUint16Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeUint16Array)constructorImpl(args, FACTORY); } NativeUint16Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -150,8 +150,8 @@ public final class NativeUint16Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeUint16Array subarray(final Object self, final Object begin, final Object end) { + return (NativeUint16Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeUint32Array.java b/src/jdk/nashorn/internal/objects/NativeUint32Array.java index 58909dba..73812246 100644 --- a/src/jdk/nashorn/internal/objects/NativeUint32Array.java +++ b/src/jdk/nashorn/internal/objects/NativeUint32Array.java @@ -118,8 +118,8 @@ public final class NativeUint32Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeUint32Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeUint32Array)constructorImpl(args, FACTORY); } NativeUint32Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -169,8 +169,8 @@ public final class NativeUint32Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeUint32Array subarray(final Object self, final Object begin, final Object end) { + return (NativeUint32Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeUint8Array.java b/src/jdk/nashorn/internal/objects/NativeUint8Array.java index 0db1bee6..1f304563 100644 --- a/src/jdk/nashorn/internal/objects/NativeUint8Array.java +++ b/src/jdk/nashorn/internal/objects/NativeUint8Array.java @@ -92,8 +92,8 @@ public final class NativeUint8Array extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeUint8Array constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeUint8Array)constructorImpl(args, FACTORY); } NativeUint8Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -143,8 +143,8 @@ public final class NativeUint8Array extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeUint8Array subarray(final Object self, final Object begin, final Object end) { + return (NativeUint8Array)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java index f0db743c..d69adc10 100644 --- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java +++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java @@ -89,6 +89,15 @@ public final class NativeUint8ClampedArray extends ArrayBufferView { } @Override + protected void setImpl(final int index, final long value) { + if (JSType.isRepresentableAsInt(value)) { + setImpl(index, (int)value); + } else { + buffer.getByteArray()[byteIndex(index)] = value > 0 ? (byte)0xff : 0; + } + } + + @Override protected void setImpl(final int key, final double value) { setImpl(key, (int)Math.rint(value)); } @@ -109,8 +118,8 @@ public final class NativeUint8ClampedArray extends ArrayBufferView { * @return new typed array */ @Constructor(arity = 1) - public static Object constructor(final boolean newObj, final Object self, final Object... args) { - return constructorImpl(args, FACTORY); + public static NativeUint8ClampedArray constructor(final boolean newObj, final Object self, final Object... args) { + return (NativeUint8ClampedArray)constructorImpl(args, FACTORY); } NativeUint8ClampedArray(final NativeArrayBuffer buffer, final int byteOffset, final int length) { @@ -160,8 +169,8 @@ public final class NativeUint8ClampedArray extends ArrayBufferView { * @return sub array */ @Function(attributes = Attribute.NOT_ENUMERABLE) - protected static Object subarray(final Object self, final Object begin, final Object end) { - return ArrayBufferView.subarrayImpl(self, begin, end); + protected static NativeUint8ClampedArray subarray(final Object self, final Object begin, final Object end) { + return (NativeUint8ClampedArray)ArrayBufferView.subarrayImpl(self, begin, end); } @Override diff --git a/src/jdk/nashorn/internal/objects/PrototypeObject.java b/src/jdk/nashorn/internal/objects/PrototypeObject.java index 4610afc2..483f6174 100644 --- a/src/jdk/nashorn/internal/objects/PrototypeObject.java +++ b/src/jdk/nashorn/internal/objects/PrototypeObject.java @@ -54,16 +54,11 @@ public class PrototypeObject extends ScriptObject { static { final ArrayList<Property> properties = new ArrayList<>(1); properties.add(AccessorProperty.create("constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR)); - map$ = PropertyMap.newMap(properties).setIsShared(); - } - - static PropertyMap getInitialMap() { - return map$; + map$ = PropertyMap.newMap(properties); } private PrototypeObject(final Global global, final PropertyMap map) { - super(map != map$? map.addAll(global.getPrototypeObjectMap()) : global.getPrototypeObjectMap()); - setProto(global.getObjectPrototype()); + super(global.getObjectPrototype(), map != map$? map.addAll(map$) : map$); } PrototypeObject() { diff --git a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java index f0bea3a6..dcbc7da9 100644 --- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java +++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java @@ -37,7 +37,6 @@ 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.lookup.Lookup; import jdk.nashorn.internal.runtime.AccessorProperty; /** @@ -56,27 +55,11 @@ public class ScriptFunctionImpl extends ScriptFunction { // property map for non-strict, non-bound functions. private static final PropertyMap map$; - static PropertyMap getInitialMap() { - return map$; - } - - static PropertyMap getInitialAnonymousMap() { - return AnonymousFunction.getInitialMap(); - } - - static PropertyMap getInitialStrictMap() { - return strictmodemap$; - } - - static PropertyMap getInitialBoundMap() { - return boundfunctionmap$; - } - // Marker object for lazily initialized prototype object private static final Object LAZY_PROTOTYPE = new Object(); private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs, final Global global) { - super(name, invokeHandle, global.getFunctionMap(), null, specs, false, true, true); + super(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); init(global); } @@ -93,7 +76,7 @@ public class ScriptFunctionImpl extends ScriptFunction { } private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs, final Global global) { - super(name, invokeHandle, map.addAll(global.getFunctionMap()), null, specs, false, true, true); + super(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); init(global); } @@ -110,8 +93,8 @@ public class ScriptFunctionImpl extends ScriptFunction { this(name, invokeHandle, map, specs, Global.instance()); } - private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor, final Global global) { - super(name, methodHandle, getMap(global, isStrict), scope, specs, isStrict, isBuiltin, isConstructor); + private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags, final Global global) { + super(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags); init(global); } @@ -122,16 +105,14 @@ public class ScriptFunctionImpl extends ScriptFunction { * @param methodHandle handle for invocation * @param scope scope object * @param specs specialized versions of this method, if available, null otherwise - * @param isStrict are we in strict mode - * @param isBuiltin is this a built-in function - * @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted). + * @param flags {@link ScriptFunctionData} flags */ - ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { - this(name, methodHandle, scope, specs, isStrict, isBuiltin, isConstructor, Global.instance()); + ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] 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(global, data.isStrict()), scope); + super(data, getMap(data.isStrict()), scope); init(global); } @@ -151,7 +132,7 @@ public class ScriptFunctionImpl extends ScriptFunction { * @param global the global object */ ScriptFunctionImpl(final ScriptFunctionData data, final Global global) { - super(data, global.getBoundFunctionMap(), null); + super(data, boundfunctionmap$, null); init(global); } @@ -163,25 +144,24 @@ public class ScriptFunctionImpl extends ScriptFunction { map$ = PropertyMap.newMap(properties); strictmodemap$ = createStrictModeMap(map$); boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); - // There are order dependencies between normal map, struct map and bound map - // We can make these 'shared' only after initialization of all three. - map$.setIsShared(); - strictmodemap$.setIsShared(); - boundfunctionmap$.setIsShared(); } 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.addProperty(map.newUserAccessors("arguments", flags)); - newMap = newMap.addProperty(map.newUserAccessors("caller", flags)); + 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 Global global, final boolean strict) { - return strict ? global.getStrictFunctionMap() : global.getFunctionMap(); + private static PropertyMap getMap(final boolean strict) { + return strict ? strictmodemap$ : map$; } private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { @@ -193,14 +173,10 @@ public class ScriptFunctionImpl extends ScriptFunction { // 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().setIsShared(); - - static PropertyMap getInitialMap() { - return anonmap$; - } + private static final PropertyMap anonmap$ = PropertyMap.newMap(); AnonymousFunction(final Global global) { - super("", GlobalFunctions.ANONYMOUS, global.getAnonymousFunctionMap(), null); + super("", GlobalFunctions.ANONYMOUS, anonmap$, null); } } @@ -217,7 +193,7 @@ public class ScriptFunctionImpl extends ScriptFunction { * @return new ScriptFunction */ static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) { - final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, false, true, false); + final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, ScriptFunctionData.IS_BUILTIN); func.setPrototype(UNDEFINED); // Non-constructor built-in functions do not have "prototype" property func.deleteOwnProperty(func.getMap().findProperty("prototype")); @@ -281,13 +257,17 @@ public class ScriptFunctionImpl extends ScriptFunction { } @Override - public final void setPrototype(final Object prototype) { - this.prototype = prototype; + 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.setProto(global.getFunctionPrototype()); + this.setInitialProto(global.getFunctionPrototype()); this.prototype = LAZY_PROTOTYPE; // We have to fill user accessor functions late as these are stored diff --git a/src/jdk/nashorn/internal/parser/AbstractParser.java b/src/jdk/nashorn/internal/parser/AbstractParser.java index 587ae869..d4a42ba4 100644 --- a/src/jdk/nashorn/internal/parser/AbstractParser.java +++ b/src/jdk/nashorn/internal/parser/AbstractParser.java @@ -26,6 +26,7 @@ package jdk.nashorn.internal.parser; import static jdk.nashorn.internal.parser.TokenType.COMMENT; +import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT; import static jdk.nashorn.internal.parser.TokenType.EOF; import static jdk.nashorn.internal.parser.TokenType.EOL; import static jdk.nashorn.internal.parser.TokenType.IDENT; @@ -84,6 +85,9 @@ public abstract class AbstractParser { /** Is this parser running under strict mode? */ protected boolean isStrictMode; + /** //@ sourceURL or //# sourceURL */ + protected String sourceURL; + /** * Construct a parser. * @@ -156,17 +160,38 @@ public abstract class AbstractParser { protected final TokenType nextOrEOL() { do { nextToken(); - } while (type == COMMENT); + if (type == DIRECTIVE_COMMENT) { + checkDirectiveComment(); + } + } while (type == COMMENT || type == DIRECTIVE_COMMENT); return type; } + // sourceURL= after directive comment + private static final String SOURCE_URL_PREFIX = "sourceURL="; + + // currently only @sourceURL=foo supported + private void checkDirectiveComment() { + // if already set, ignore this one + if (sourceURL != null) { + return; + } + + final String comment = (String) lexer.getValueOf(token, isStrictMode); + final int len = comment.length(); + // 4 characters for directive comment marker //@\s or //#\s + if (len > 4 && comment.substring(4).startsWith(SOURCE_URL_PREFIX)) { + sourceURL = comment.substring(4 + SOURCE_URL_PREFIX.length()); + } + } + /** * Seek next token. * * @return tokenType of next token. */ - private final TokenType nextToken() { + private TokenType nextToken() { // Capture last token tokenType. last = type; if (type != EOF) { diff --git a/src/jdk/nashorn/internal/parser/Lexer.java b/src/jdk/nashorn/internal/parser/Lexer.java index a01705dd..653f04c0 100644 --- a/src/jdk/nashorn/internal/parser/Lexer.java +++ b/src/jdk/nashorn/internal/parser/Lexer.java @@ -27,6 +27,7 @@ package jdk.nashorn.internal.parser; import static jdk.nashorn.internal.parser.TokenType.ADD; import static jdk.nashorn.internal.parser.TokenType.COMMENT; +import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT; import static jdk.nashorn.internal.parser.TokenType.DECIMAL; import static jdk.nashorn.internal.parser.TokenType.EOF; import static jdk.nashorn.internal.parser.TokenType.EOL; @@ -434,12 +435,18 @@ public class Lexer extends Scanner { if (ch1 == '/') { // Skip over //. skip(2); + + boolean directiveComment = false; + if ((ch0 == '#' || ch0 == '@') && (ch1 == ' ')) { + directiveComment = true; + } + // Scan for EOL. while (!atEOF() && !isEOL(ch0)) { skip(1); } // Did detect a comment. - add(COMMENT, start); + add(directiveComment? DIRECTIVE_COMMENT : COMMENT, start); return true; } else if (ch1 == '*') { // Skip over /*. @@ -1623,6 +1630,8 @@ public class Lexer extends Scanner { return valueOfPattern(start, len); // RegexToken::LexerToken case XML: return valueOfXML(start, len); // XMLToken::LexerToken + case DIRECTIVE_COMMENT: + return source.getString(start, len); default: break; } diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java index 8e04fdce..6ad60b58 100644 --- a/src/jdk/nashorn/internal/parser/Parser.java +++ b/src/jdk/nashorn/internal/parser/Parser.java @@ -26,13 +26,14 @@ package jdk.nashorn.internal.parser; import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL; -import static jdk.nashorn.internal.codegen.CompilerConstants.FUNCTION_PREFIX; +import static jdk.nashorn.internal.codegen.CompilerConstants.ANON_FUNCTION_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT; import static jdk.nashorn.internal.parser.TokenType.ASSIGN; import static jdk.nashorn.internal.parser.TokenType.CASE; import static jdk.nashorn.internal.parser.TokenType.CATCH; import static jdk.nashorn.internal.parser.TokenType.COLON; import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT; +import static jdk.nashorn.internal.parser.TokenType.CONST; import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.DECPREFIX; import static jdk.nashorn.internal.parser.TokenType.ELSE; @@ -389,7 +390,9 @@ loop: sb.append(parentFunction.getName()).append('$'); } - sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.symbolName()); + assert ident.getName() != null; + sb.append(ident.getName()); + final String name = namespace.uniqueName(sb.toString()); assert parentFunction != null || name.equals(RUN_SCRIPT.symbolName()) : "name = " + name;// must not rename runScript(). @@ -419,7 +422,8 @@ loop: name, parameters, kind, - flags); + flags, + sourceURL); lc.push(functionNode); // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the @@ -638,6 +642,10 @@ loop: script = restoreFunctionNode(script, token); //commit code script = script.setBody(lc, script.getBody().setNeedsScope(lc)); + // user may have directive comment to set sourceURL + if (sourceURL != null) { + script = script.setSourceURL(lc, sourceURL); + } return script; } @@ -842,6 +850,11 @@ loop: expect(SEMICOLON); break; default: + if (env._const_as_var && type == CONST) { + variableStatement(true); + break; + } + if (type == IDENT || isNonStrictModeIdent()) { if (T(k + 1) == COLON) { labelStatement(); @@ -1103,6 +1116,12 @@ loop: case SEMICOLON: break; default: + if (env._const_as_var && type == CONST) { + // Var statements captured in for outer block. + vars = variableStatement(false); + break; + } + final Expression expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true); forNode = forNode.setInit(lc, expression); break; @@ -1693,9 +1712,11 @@ loop: // ECMA 12.4.1 strict mode restrictions verifyStrictIdent(exception, "catch argument"); - // Check for conditional catch. + // Nashorn extension: catch clause can have optional + // condition. So, a single try can have more than one + // catch clause each with it's own condition. final Expression ifExpression; - if (type == IF) { + if (!env._no_syntax_extensions && type == IF) { next(); // Get the exception condition. ifExpression = expression(); @@ -1792,6 +1813,7 @@ loop: case THIS: final String name = type.getName(); next(); + lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_THIS); return new IdentNode(primaryToken, finish, name); case IDENT: final IdentNode ident = getIdent(); @@ -2132,11 +2154,20 @@ loop: final String setterName = setIdent.getPropertyName(); final IdentNode setNameNode = new IdentNode(((Node)setIdent).getToken(), finish, NameCodec.encode("set " + setterName)); expect(LPAREN); - final IdentNode argIdent = getIdent(); - verifyStrictIdent(argIdent, "setter argument"); + // be sloppy and allow missing setter parameter even though + // spec does not permit it! + final IdentNode argIdent; + if (type == IDENT || isNonStrictModeIdent()) { + argIdent = getIdent(); + verifyStrictIdent(argIdent, "setter argument"); + } else { + argIdent = null; + } expect(RPAREN); List<IdentNode> parameters = new ArrayList<>(); - parameters.add(argIdent); + if (argIdent != null) { + parameters.add(argIdent); + } functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER); return new PropertyNode(propertyToken, finish, setIdent, null, null, functionNode); @@ -2448,7 +2479,7 @@ loop: // name is null, generate anonymous name boolean isAnonymous = false; if (name == null) { - final String tmpName = "_L" + functionLine; + final String tmpName = ANON_FUNCTION_PREFIX.symbolName() + functionLine; name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName); isAnonymous = true; } diff --git a/src/jdk/nashorn/internal/parser/TokenType.java b/src/jdk/nashorn/internal/parser/TokenType.java index 6287a234..59005077 100644 --- a/src/jdk/nashorn/internal/parser/TokenType.java +++ b/src/jdk/nashorn/internal/parser/TokenType.java @@ -41,10 +41,14 @@ import static jdk.nashorn.internal.parser.TokenKind.UNARY; */ @SuppressWarnings("javadoc") public enum TokenType { - ERROR (SPECIAL, null), - EOF (SPECIAL, null), - EOL (SPECIAL, null), - COMMENT (SPECIAL, null), + ERROR (SPECIAL, null), + EOF (SPECIAL, null), + EOL (SPECIAL, null), + COMMENT (SPECIAL, null), + // comments of the form //@ foo=bar or //# foo=bar + // These comments are treated as special instructions + // to the lexer, parser or codegenerator. + DIRECTIVE_COMMENT (SPECIAL, null), NOT (UNARY, "!", 14, false), NE (BINARY, "!=", 9, true), @@ -107,7 +111,7 @@ public enum TokenType { CATCH (KEYWORD, "catch"), // CHAR (FUTURE, "char"), CLASS (FUTURE, "class"), - CONST (FUTURE, "const"), + CONST (KEYWORD, "const"), CONTINUE (KEYWORD, "continue"), DEBUGGER (KEYWORD, "debugger"), DEFAULT (KEYWORD, "default"), diff --git a/src/jdk/nashorn/internal/runtime/AccessorProperty.java b/src/jdk/nashorn/internal/runtime/AccessorProperty.java index 702ca76b..e8ff153d 100644 --- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java @@ -39,6 +39,9 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getNumberOfAcces import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -51,11 +54,12 @@ import jdk.nashorn.internal.lookup.MethodHandleFactory; * An AccessorProperty is the most generic property type. An AccessorProperty is * represented as fields in a ScriptObject class. */ -public final class AccessorProperty extends Property { +public final class AccessorProperty extends Property implements Serializable { private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class); private static final int NOOF_TYPES = getNumberOfAccessorTypes(); + private static final long serialVersionUID = 3371720170182154920L; /** * Properties in different maps for the same structure class will share their field getters and setters. This could @@ -71,7 +75,7 @@ public final class AccessorProperty extends Property { }; /** Property getter cache */ - private MethodHandle[] getters = new MethodHandle[NOOF_TYPES]; + private transient MethodHandle[] getters = new MethodHandle[NOOF_TYPES]; private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES]; private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES]; @@ -122,16 +126,16 @@ public final class AccessorProperty extends Property { } /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ - private MethodHandle primitiveGetter; + private transient MethodHandle primitiveGetter; /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ - private MethodHandle primitiveSetter; + private transient MethodHandle primitiveSetter; /** Seed getter for the Object version of this field */ - private MethodHandle objectGetter; + private transient MethodHandle objectGetter; /** Seed setter for the Object version of this field */ - private MethodHandle objectSetter; + private transient MethodHandle objectSetter; /** * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode @@ -141,10 +145,12 @@ public final class AccessorProperty extends Property { private Class<?> currentType; /** - * Delegate constructor. This is used when adding properties to the Global scope, which - * is necessary for outermost levels in a script (the ScriptObject is represented by - * a JO-prefixed ScriptObject class, but the properties need to be in the Global scope - * and are thus rebound with that as receiver + * Delegate constructor for bound properties. This is used for properties created by + * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method. + * The former is used to add a script's defined globals to the current global scope while + * still storing them in a JO-prefixed ScriptObject class. + * + * <p>All properties created by this constructor have the {@link #IS_BOUND} flag set.</p> * * @param property accessor property to rebind * @param delegate delegate object to rebind receiver to @@ -157,6 +163,8 @@ public final class AccessorProperty extends Property { this.objectGetter = bindTo(property.ensureObjectGetter(), delegate); this.objectSetter = bindTo(property.ensureObjectSetter(), delegate); + // Properties created this way are bound to a delegate + this.flags |= IS_BOUND; setCurrentType(property.getCurrentType()); } @@ -239,6 +247,12 @@ public final class AccessorProperty extends Property { public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) { super(key, flags, slot); + initGetterSetter(structure); + } + + private void initGetterSetter(final Class<?> structure) { + final int slot = getSlot(); + final String key = getKey(); /* * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also * works in dual field mode, it only means that the property never has a primitive @@ -301,6 +315,12 @@ public final class AccessorProperty extends Property { setCurrentType(property.getCurrentType()); } + private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + // Restore getters array + getters = new MethodHandle[NOOF_TYPES]; + } + private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) { if (mh == null) { return null; @@ -360,6 +380,16 @@ public final class AccessorProperty extends Property { } @Override + void initMethodHandles(final Class<?> structure) { + if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) { + throw new IllegalArgumentException(); + } + if (!isSpill()) { + initGetterSetter(structure); + } + } + + @Override public MethodHandle getGetter(final Class<?> type) { final int i = getAccessorTypeIndex(type); ensureObjectGetter(); diff --git a/src/jdk/nashorn/internal/runtime/CodeInstaller.java b/src/jdk/nashorn/internal/runtime/CodeInstaller.java index 28c9630e..579b6f3e 100644 --- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java +++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java @@ -25,6 +25,7 @@ package jdk.nashorn.internal.runtime; +import java.util.Map; import jdk.nashorn.internal.codegen.ClassEmitter; /** @@ -52,7 +53,7 @@ public interface CodeInstaller<T> { * @param bytecode bytecode * @return the installed class */ - public Class<?> install(final String className, final byte[] bytecode); + public Class<?> install(final String className, final byte[] bytecode, final Source source, final Object[] constants); /** * Verify generated bytecode before emission. This is called back from the @@ -74,4 +75,13 @@ public interface CodeInstaller<T> { * @return unique eval id */ public long getUniqueEvalId(); + + /** + * Store a compiled script for later reuse + * @param source the script source + * @param mainClassName the main class name + * @param classBytes map of class names to class bytes + * @param constants constants array + */ + public void storeCompiledScript(Source source, String mainClassName, Map<String, byte[]> classBytes, Object[] constants); } diff --git a/src/jdk/nashorn/internal/runtime/CodeStore.java b/src/jdk/nashorn/internal/runtime/CodeStore.java new file mode 100644 index 00000000..8f595999 --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/CodeStore.java @@ -0,0 +1,179 @@ +/* + * 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.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Base64; +import java.util.Map; + +/** + * A code cache for persistent caching of compiled scripts. + */ +final class CodeStore { + + private final File dir; + private final int minSize; + + // Message digest to file name encoder + private final static Base64.Encoder BASE64 = Base64.getUrlEncoder().withoutPadding(); + + // Default minimum size for storing a compiled script class + private final static int DEFAULT_MIN_SIZE = 1000; + + /** + * Constructor + * @param path directory to store code in + * @throws IOException + */ + public CodeStore(final String path) throws IOException { + this(path, DEFAULT_MIN_SIZE); + } + + /** + * Constructor + * @param path directory to store code in + * @param minSize minimum file size for caching scripts + * @throws IOException + */ + public CodeStore(final String path, final int minSize) throws IOException { + this.dir = checkDirectory(path); + this.minSize = minSize; + } + + private static File checkDirectory(final String path) throws IOException { + try { + return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() { + @Override + public File run() throws IOException { + final File dir = new File(path).getAbsoluteFile(); + if (!dir.exists() && !dir.mkdirs()) { + throw new IOException("Could not create directory: " + dir); + } else if (!dir.isDirectory()) { + throw new IOException("Not a directory: " + dir); + } else if (!dir.canRead() || !dir.canWrite()) { + throw new IOException("Directory not readable or writable: " + dir); + } + return dir; + } + }); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } + + /** + * Return a compiled script from the cache, or null if it isn't found. + * + * @param source the source + * @return the compiled script or null + * @throws IOException + * @throws ClassNotFoundException + */ + public CompiledScript getScript(final Source source) throws IOException, ClassNotFoundException { + if (source.getLength() < minSize) { + return null; + } + + final String digest = BASE64.encodeToString(source.getDigest()); + final File file = new File(dir, digest); + + try { + return AccessController.doPrivileged(new PrivilegedExceptionAction<CompiledScript>() { + @Override + public CompiledScript run() throws IOException, ClassNotFoundException { + if (!file.exists()) { + return null; + } + try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { + CompiledScript compiledScript = (CompiledScript) in.readObject(); + compiledScript.setSource(source); + return compiledScript; + } + } + }); + } catch (PrivilegedActionException e) { + final Exception ex = e.getException(); + if (ex instanceof IOException) { + throw (IOException) ex; + } else if (ex instanceof ClassNotFoundException) { + throw (ClassNotFoundException) ex; + } + throw (new RuntimeException(ex)); + } + } + + /** + * Store a compiled script in the cache. + * + * @param source the source + * @param mainClassName the main class name + * @param classBytes a map of class bytes + * @param constants the constants array + * @throws IOException + */ + public void putScript(final Source source, final String mainClassName, final Map<String, byte[]> classBytes, final Object[] constants) + throws IOException { + if (source.getLength() < minSize) { + return; + } + for (final Object constant : constants) { + // Make sure all constant data is serializable + if (! (constant instanceof Serializable)) { + return; + } + } + + final String digest = BASE64.encodeToString(source.getDigest()); + final File file = new File(dir, digest); + final CompiledScript script = new CompiledScript(source, mainClassName, classBytes, constants); + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws IOException { + try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { + out.writeObject(script); + } + return null; + } + }); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } +} + diff --git a/src/jdk/nashorn/internal/runtime/CompiledScript.java b/src/jdk/nashorn/internal/runtime/CompiledScript.java new file mode 100644 index 00000000..6bb8e9a0 --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/CompiledScript.java @@ -0,0 +1,127 @@ +/* + * 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.Serializable; +import java.util.Arrays; +import java.util.Map; + +/** + * Class representing a compiled script. + */ +final class CompiledScript implements Serializable { + + /** Main class name. */ + private final String mainClassName; + + /** Map of class names to class bytes. */ + private final Map<String, byte[]> classBytes; + + /** Constants array. */ + private final Object[] constants; + + /** The source */ + private transient Source source; + + private static final long serialVersionUID = 2958227232195298340L; + + /** + * Constructor. + * + * @param mainClassName main class name + * @param classBytes map of class names to class bytes + * @param constants constants array + */ + CompiledScript(final Source source, final String mainClassName, final Map<String, byte[]> classBytes, final Object[] constants) { + this.source = source; + this.mainClassName = mainClassName; + this.classBytes = classBytes; + this.constants = constants; + } + + /** + * Returns the main class name. + * @return the main class name + */ + public String getMainClassName() { + return mainClassName; + } + + /** + * Returns a map of class names to class bytes. + * @return map of class bytes + */ + public Map<String, byte[]> getClassBytes() { + return classBytes; + } + + /** + * Returns the constants array. + * @return constants array + */ + public Object[] getConstants() { + return constants; + } + + /** + * Returns the source of this cached script. + * @return the source + */ + public Source getSource() { + return source; + } + + /** + * Sets the source of this cached script. + * @param source the source + */ + void setSource(final Source source) { + this.source = source; + } + + @Override + public int hashCode() { + int hash = mainClassName.hashCode(); + hash = 31 * hash + classBytes.hashCode(); + hash = 31 * hash + Arrays.hashCode(constants); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof CompiledScript)) { + return false; + } + + final CompiledScript cs = (CompiledScript) obj; + return mainClassName.equals(cs.mainClassName) + && classBytes.equals(cs.classBytes) + && Arrays.equals(constants, cs.constants); + } +} diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java index 86b5abd8..9f2c521d 100644 --- a/src/jdk/nashorn/internal/runtime/Context.java +++ b/src/jdk/nashorn/internal/runtime/Context.java @@ -25,17 +25,23 @@ package jdk.nashorn.internal.runtime; +import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT; +import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import static jdk.nashorn.internal.runtime.Source.sourceFor; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.concurrent.atomic.AtomicLong; import java.net.MalformedURLException; @@ -46,7 +52,11 @@ import java.security.CodeSigner; import java.security.CodeSource; import java.security.Permissions; import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import jdk.internal.org.objectweb.asm.ClassReader; @@ -91,6 +101,11 @@ public final class Context { */ public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; + /** + * Permission to enable nashorn debug mode. + */ + public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode"; + // nashorn load psuedo URL prefixes private static final String LOAD_CLASSPATH = "classpath:"; private static final String LOAD_FX = "fx:"; @@ -126,8 +141,32 @@ public final class Context { } @Override - public Class<?> install(final String className, final byte[] bytecode) { - return loader.installClass(className, bytecode, codeSource); + public Class<?> install(final String className, final byte[] bytecode, final Source source, final Object[] constants) { + Compiler.LOG.fine("Installing class ", className); + + final String binaryName = Compiler.binaryName(className); + final Class<?> clazz = loader.installClass(binaryName, bytecode, codeSource); + + try { + // Need doPrivileged because these fields are private + AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + //use reflection to write source and constants table to installed classes + final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); + final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); + sourceField.setAccessible(true); + constantsField.setAccessible(true); + sourceField.set(null, source); + constantsField.set(null, constants); + return null; + } + }); + } catch (final PrivilegedActionException e) { + throw new RuntimeException(e); + } + + return clazz; } @Override @@ -144,21 +183,39 @@ public final class Context { public long getUniqueEvalId() { return context.getUniqueEvalId(); } + + @Override + public void storeCompiledScript(final Source source, final String mainClassName, + final Map<String, byte[]> classBytes, final Object[] constants) { + if (context.codeStore != null) { + try { + context.codeStore.putScript(source, mainClassName, classBytes, constants); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + } } /** Is Context global debug mode enabled ? */ public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); - private static final ThreadLocal<ScriptObject> currentGlobal = new ThreadLocal<>(); + private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); + + // in-memory cache for loaded classes + private ClassCache classCache; + + // persistent code store + private CodeStore codeStore; /** * Get the current global scope * @return the current global scope */ - public static ScriptObject getGlobal() { + public static Global getGlobal() { // This class in a package.access protected package. // Trusted code only can call this method. - return getGlobalTrusted(); + return currentGlobal.get(); } /** @@ -167,10 +224,19 @@ public final class Context { */ public static void setGlobal(final ScriptObject global) { if (global != null && !(global instanceof Global)) { - throw new IllegalArgumentException("global is not an instance of Global!"); + throw new IllegalArgumentException("not a global!"); } + setGlobal((Global)global); + } - setGlobalTrusted(global); + /** + * Set the current global scope + * @param global the global scope + */ + public static void setGlobal(final Global global) { + // This class in a package.access protected package. + // Trusted code only can call this method. + currentGlobal.set(global); } /** @@ -191,7 +257,7 @@ public final class Context { * @return error writer of the current context */ public static PrintWriter getCurrentErr() { - final ScriptObject global = getGlobalTrusted(); + final ScriptObject global = getGlobal(); return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); } @@ -344,6 +410,24 @@ public final class Context { this.classPathLoader = null; } + final int cacheSize = env._class_cache_size; + if (cacheSize > 0) { + classCache = new ClassCache(cacheSize); + } + + if (env._persistent_cache) { + if (env._lazy_compilation || env._specialize_calls != null) { + getErr().println("Can not use persistent class caching with lazy compilation or call specialization."); + } else { + try { + final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"); + codeStore = new CodeStore(cacheDir); + } catch (IOException e) { + throw new RuntimeException("Error initializing code cache", e); + } + } + } + // print version info if asked. if (env._version) { getErr().println("nashorn " + Version.version()); @@ -391,7 +475,7 @@ public final class Context { * @return the property map of the current global scope */ public static PropertyMap getGlobalMap() { - return Context.getGlobalTrusted().getMap(); + return Context.getGlobal().getMap(); } /** @@ -407,6 +491,39 @@ public final class Context { } /** + * Interface to represent compiled code that can be re-used across many + * global scope instances + */ + public static interface MultiGlobalCompiledScript { + /** + * Obtain script function object for a specific global scope object. + * + * @param newGlobal global scope for which function object is obtained + * @return script function for script level expressions + */ + public ScriptFunction getFunction(final Global newGlobal); + } + + /** + * Compile a top level script. + * + * @param source the script source + * @return reusable compiled script across many global scopes. + */ + public MultiGlobalCompiledScript compileScript(final Source source) { + final Class<?> clazz = compile(source, this.errors, this._strict); + final MethodHandle runMethodHandle = getRunScriptHandle(clazz); + final boolean strict = isStrict(clazz); + + return new MultiGlobalCompiledScript() { + @Override + public ScriptFunction getFunction(final Global newGlobal) { + return Context.getGlobal().newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, newGlobal, strict); + } + }; + } + + /** * Entry point for {@code eval} * * @param initialScope The scope of this eval call @@ -419,9 +536,9 @@ public final class Context { */ public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) { final String file = (location == UNDEFINED || location == null) ? "<eval>" : location.toString(); - final Source source = new Source(file, string); + final Source source = sourceFor(file, string); final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval? - final ScriptObject global = Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); ScriptObject scope = initialScope; @@ -453,7 +570,7 @@ public final class Context { // in the caller's environment. A new environment is created! if (strictFlag) { // Create a new scope object - final ScriptObject strictEvalScope = ((GlobalObject)global).newObject(); + final ScriptObject strictEvalScope = global.newObject(); // bless it as a "scope" strictEvalScope.setIsScope(); @@ -486,7 +603,7 @@ public final class Context { public Source run() { try { final URL resURL = Context.class.getResource(resource); - return (resURL != null)? new Source(srcStr, resURL) : null; + return (resURL != null)? sourceFor(srcStr, resURL) : null; } catch (final IOException exp) { return null; } @@ -518,7 +635,7 @@ public final class Context { final String srcStr = (String)src; if (srcStr.startsWith(LOAD_CLASSPATH)) { URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); - source = (url != null)? new Source(url.toString(), url) : null; + source = (url != null)? sourceFor(url.toString(), url) : null; } else { final File file = new File(srcStr); if (srcStr.indexOf(':') != -1) { @@ -531,31 +648,31 @@ public final class Context { } catch (final MalformedURLException e) { url = file.toURI().toURL(); } - source = new Source(url.toString(), url); + source = sourceFor(url.toString(), url); } } else if (file.isFile()) { - source = new Source(srcStr, file); + source = sourceFor(srcStr, file); } } } else if (src instanceof File && ((File)src).isFile()) { final File file = (File)src; - source = new Source(file.getName(), file); + source = sourceFor(file.getName(), file); } else if (src instanceof URL) { final URL url = (URL)src; - source = new Source(url.toString(), url); + source = sourceFor(url.toString(), url); } else if (src instanceof ScriptObject) { final ScriptObject sobj = (ScriptObject)src; if (sobj.has("script") && sobj.has("name")) { final String script = JSType.toString(sobj.get("script")); final String name = JSType.toString(sobj.get("name")); - source = new Source(name, script); + source = sourceFor(name, script); } } else if (src instanceof Map) { final Map<?,?> map = (Map<?,?>)src; if (map.containsKey("script") && map.containsKey("name")) { final String script = JSType.toString(map.get("script")); final String name = JSType.toString(map.get("name")); - source = new Source(name, script); + source = sourceFor(name, script); } } @@ -578,10 +695,10 @@ public final class Context { * @throws IOException if source cannot be found or loaded */ public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { - final ScriptObject oldGlobal = getGlobalTrusted(); - final ScriptObject newGlobal = AccessController.doPrivileged(new PrivilegedAction<ScriptObject>() { + final Global oldGlobal = getGlobal(); + final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { @Override - public ScriptObject run() { + public Global run() { try { return newGlobal(); } catch (final RuntimeException e) { @@ -594,17 +711,17 @@ public final class Context { }, CREATE_GLOBAL_ACC_CTXT); // initialize newly created Global instance initGlobal(newGlobal); - setGlobalTrusted(newGlobal); + setGlobal(newGlobal); final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); - newGlobal.put("arguments", ((GlobalObject)newGlobal).wrapAsObject(wrapped), env._strict); + newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); try { // wrap objects from newGlobal's world as mirrors - but if result // is from oldGlobal's world, unwrap it! return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); } finally { - setGlobalTrusted(oldGlobal); + setGlobal(oldGlobal); } } @@ -633,7 +750,7 @@ public final class Context { * Checks that the given Class can be accessed from no permissions context. * * @param clazz Class object - * @throw SecurityException if not accessible + * @throws SecurityException if not accessible */ public static void checkPackageAccess(final Class<?> clazz) { final SecurityManager sm = System.getSecurityManager(); @@ -650,12 +767,12 @@ public final class Context { * Checks that the given package name can be accessed from no permissions context. * * @param pkgName package name - * @throw SecurityException if not accessible + * @throws SecurityException if not accessible */ public static void checkPackageAccess(final String pkgName) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - checkPackageAccess(sm, pkgName.endsWith(".")? pkgName : pkgName + "."); + checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); } } @@ -779,7 +896,7 @@ public final class Context { * * @return the initialized global scope object. */ - public ScriptObject createGlobal() { + public Global createGlobal() { return initGlobal(newGlobal()); } @@ -787,7 +904,7 @@ public final class Context { * Create a new uninitialized global scope object * @return the global script object */ - public ScriptObject newGlobal() { + public Global newGlobal() { return new Global(this); } @@ -797,20 +914,16 @@ public final class Context { * @param global the global * @return the initialized global scope object. */ - public ScriptObject initGlobal(final ScriptObject global) { - if (! (global instanceof GlobalObject)) { - throw new IllegalArgumentException("not a global object!"); - } - + public Global initGlobal(final Global global) { // Need only minimal global object, if we are just compiling. if (!env._compile_only) { - final ScriptObject oldGlobal = Context.getGlobalTrusted(); + final Global oldGlobal = Context.getGlobal(); try { - Context.setGlobalTrusted(global); + Context.setGlobal(global); // initialize global scope with builtin global objects - ((GlobalObject)global).initBuiltinObjects(); + global.initBuiltinObjects(); } finally { - Context.setGlobalTrusted(oldGlobal); + Context.setGlobal(oldGlobal); } } @@ -818,30 +931,15 @@ public final class Context { } /** - * Trusted variants - package-private + * Trusted variant - package-private */ /** - * Return the current global scope - * @return current global scope - */ - static ScriptObject getGlobalTrusted() { - return currentGlobal.get(); - } - - /** - * Set the current global scope - */ - static void setGlobalTrusted(ScriptObject global) { - currentGlobal.set(global); - } - - /** * Return the current global's context * @return current global's context */ static Context getContextTrusted() { - return Context.getGlobalTrusted().getContext(); + return ((ScriptObject)Context.getGlobal()).getContext(); } /** @@ -885,14 +983,8 @@ public final class Context { return ScriptRuntime.apply(script, thiz); } - private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) { - if (script == null) { - return null; - } - - // Get run method - the entry point to the script - final MethodHandle runMethodHandle = - MH.findStatic( + private static MethodHandle getRunScriptHandle(final Class<?> script) { + return MH.findStatic( MethodHandles.lookup(), script, RUN_SCRIPT.symbolName(), @@ -900,17 +992,27 @@ public final class Context { Object.class, ScriptFunction.class, Object.class)); + } - boolean strict; - + private static boolean isStrict(final Class<?> script) { try { - strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null); + return script.getField(STRICT_MODE.symbolName()).getBoolean(null); } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - strict = false; + return false; + } + } + + private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) { + if (script == null) { + return null; } + // Get run method - the entry point to the script + final MethodHandle runMethodHandle = getRunScriptHandle(script); + boolean strict = isStrict(script); + // Package as a JavaScript function and pass function back to shell. - return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict); + return Context.getGlobal().newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict); } private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { @@ -921,29 +1023,38 @@ public final class Context { // start with no errors, no warnings. errMan.reset(); - GlobalObject global = null; - Class<?> script; + Class<?> script = findCachedClass(source); + if (script != null) { + Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile."); + return script; + } + + CompiledScript compiledScript = null; + FunctionNode functionNode = null; - if (env._class_cache_size > 0) { - global = (GlobalObject)Context.getGlobalTrusted(); - script = global.findCachedClass(source); - if (script != null) { - Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile."); - return script; + if (!env._parse_only && codeStore != null) { + try { + compiledScript = codeStore.getScript(source); + } catch (IOException | ClassNotFoundException e) { + Compiler.LOG.warning("Error loading ", source, " from cache: ", e); + // Fall back to normal compilation } } - final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse(); - if (errors.hasErrors()) { - return null; - } + if (compiledScript == null) { + functionNode = new Parser(env, source, errMan, strict).parse(); - if (env._print_ast) { - getErr().println(new ASTWriter(functionNode)); - } + if (errors.hasErrors()) { + return null; + } + + if (env._print_ast) { + getErr().println(new ASTWriter(functionNode)); + } - if (env._print_parse) { - getErr().println(new PrintVisitor(functionNode)); + if (env._print_parse) { + getErr().println(new PrintVisitor(functionNode)); + } } if (env._parse_only) { @@ -952,18 +1063,18 @@ public final class Context { final URL url = source.getURL(); final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; - final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null); + final CodeSource cs = new CodeSource(url, (CodeSigner[])null); final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); - final Compiler compiler = new Compiler(installer, strict); - - final FunctionNode newFunctionNode = compiler.compile(functionNode); - script = compiler.install(newFunctionNode); - - if (global != null) { - global.cacheClass(source, script); + if (functionNode != null) { + final Compiler compiler = new Compiler(installer, strict); + final FunctionNode newFunctionNode = compiler.compile(functionNode); + script = compiler.install(newFunctionNode); + } else { + script = install(compiledScript, installer); } + cacheClass(source, script); return script; } @@ -984,4 +1095,96 @@ public final class Context { private long getUniqueScriptId() { return uniqueScriptId.getAndIncrement(); } + + + /** + * Install a previously compiled class from the code cache. + * + * @param compiledScript cached script containing class bytes and constants + * @return main script class + */ + private Class<?> install(final CompiledScript compiledScript, final CodeInstaller<ScriptEnvironment> installer) { + + final Map<String, Class<?>> installedClasses = new HashMap<>(); + final Source source = compiledScript.getSource(); + final Object[] constants = compiledScript.getConstants(); + final String rootClassName = compiledScript.getMainClassName(); + final byte[] rootByteCode = compiledScript.getClassBytes().get(rootClassName); + final Class<?> rootClass = installer.install(rootClassName, rootByteCode, source, constants); + + installedClasses.put(rootClassName, rootClass); + + for (final Map.Entry<String, byte[]> entry : compiledScript.getClassBytes().entrySet()) { + final String className = entry.getKey(); + if (className.equals(rootClassName)) { + continue; + } + final byte[] code = entry.getValue(); + + installedClasses.put(className, installer.install(className, code, source, constants)); + } + for (Object constant : constants) { + if (constant instanceof RecompilableScriptFunctionData) { + ((RecompilableScriptFunctionData) constant).setCodeAndSource(installedClasses, source); + } + } + + return rootClass; + } + + /** + * Cache for compiled script classes. + */ + @SuppressWarnings("serial") + private static class ClassCache extends LinkedHashMap<Source, ClassReference> { + private final int size; + private final ReferenceQueue<Class<?>> queue; + + ClassCache(int size) { + super(size, 0.75f, true); + this.size = size; + this.queue = new ReferenceQueue<>(); + } + + void cache(final Source source, final Class<?> clazz) { + put(source, new ClassReference(clazz, queue, source)); + } + + @Override + protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { + return size() > size; + } + + @Override + public ClassReference get(Object key) { + for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { + remove(ref.source); + } + return super.get(key); + } + + } + + private static class ClassReference extends SoftReference<Class<?>> { + private final Source source; + + ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { + super(clazz, queue); + this.source = source; + } + } + + // Class cache management + private Class<?> findCachedClass(final Source source) { + ClassReference ref = classCache == null ? null : classCache.get(source); + return ref != null ? ref.get() : null; + } + + private void cacheClass(final Source source, final Class<?> clazz) { + if (classCache != null) { + classCache.cache(source, clazz); + } + } + + } diff --git a/src/jdk/nashorn/internal/runtime/DebuggerSupport.java b/src/jdk/nashorn/internal/runtime/DebuggerSupport.java index c288a963..261bc020 100644 --- a/src/jdk/nashorn/internal/runtime/DebuggerSupport.java +++ b/src/jdk/nashorn/internal/runtime/DebuggerSupport.java @@ -75,7 +75,7 @@ final class DebuggerSupport { * @return context global. */ static Object getGlobal() { - return Context.getGlobalTrusted(); + return Context.getGlobal(); } /** @@ -87,7 +87,7 @@ final class DebuggerSupport { * @return Result of eval as string, or, an exception or null depending on returnException. */ static Object eval(final ScriptObject scope, final Object self, final String string, final boolean returnException) { - final ScriptObject global = Context.getGlobalTrusted(); + final ScriptObject global = Context.getGlobal(); final ScriptObject initialScope = scope != null ? scope : global; final Object callThis = self != null ? self : global; final Context context = global.getContext(); diff --git a/src/jdk/nashorn/internal/runtime/ECMAErrors.java b/src/jdk/nashorn/internal/runtime/ECMAErrors.java index 0ff843a4..affba497 100644 --- a/src/jdk/nashorn/internal/runtime/ECMAErrors.java +++ b/src/jdk/nashorn/internal/runtime/ECMAErrors.java @@ -30,6 +30,8 @@ import java.util.Locale; import java.util.ResourceBundle; import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.internal.scripts.JS; +import jdk.nashorn.internal.codegen.CompilerConstants; +import jdk.nashorn.internal.objects.Global; /** * Helper class to throw various standard "ECMA error" exceptions such as Error, ReferenceError, TypeError etc. @@ -65,7 +67,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException asEcmaException(final ParserException e) { - return asEcmaException(Context.getGlobalTrusted(), e); + return asEcmaException(Context.getGlobal(), e); } /** @@ -77,11 +79,11 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException asEcmaException(final ScriptObject global, final ParserException e) { + public static ECMAException asEcmaException(final Global global, final ParserException e) { final JSErrorType errorType = e.getErrorType(); assert errorType != null : "error type for " + e + " was null"; - final GlobalObject globalObj = (GlobalObject)global; + final Global globalObj = global; final String msg = e.getMessage(); // translate to ECMAScript Error object using error type @@ -115,7 +117,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException syntaxError(final String msgId, final String... args) { - return syntaxError(Context.getGlobalTrusted(), msgId, args); + return syntaxError(Context.getGlobal(), msgId, args); } /** @@ -127,7 +129,7 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException syntaxError(final ScriptObject global, final String msgId, final String... args) { + public static ECMAException syntaxError(final Global global, final String msgId, final String... args) { return syntaxError(global, null, msgId, args); } @@ -141,7 +143,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException syntaxError(final Throwable cause, final String msgId, final String... args) { - return syntaxError(Context.getGlobalTrusted(), cause, msgId, args); + return syntaxError(Context.getGlobal(), cause, msgId, args); } /** @@ -154,9 +156,9 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException syntaxError(final ScriptObject global, final Throwable cause, final String msgId, final String... args) { + public static ECMAException syntaxError(final Global global, final Throwable cause, final String msgId, final String... args) { final String msg = getMessage("syntax.error." + msgId, args); - return error(((GlobalObject)global).newSyntaxError(msg), cause); + return error(global.newSyntaxError(msg), cause); } /** @@ -168,7 +170,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException typeError(final String msgId, final String... args) { - return typeError(Context.getGlobalTrusted(), msgId, args); + return typeError(Context.getGlobal(), msgId, args); } /** @@ -180,7 +182,7 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException typeError(final ScriptObject global, final String msgId, final String... args) { + public static ECMAException typeError(final Global global, final String msgId, final String... args) { return typeError(global, null, msgId, args); } @@ -194,7 +196,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException typeError(final Throwable cause, final String msgId, final String... args) { - return typeError(Context.getGlobalTrusted(), cause, msgId, args); + return typeError(Context.getGlobal(), cause, msgId, args); } /** @@ -207,9 +209,9 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException typeError(final ScriptObject global, final Throwable cause, final String msgId, final String... args) { + public static ECMAException typeError(final Global global, final Throwable cause, final String msgId, final String... args) { final String msg = getMessage("type.error." + msgId, args); - return error(((GlobalObject)global).newTypeError(msg), cause); + return error(global.newTypeError(msg), cause); } /** @@ -221,7 +223,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException rangeError(final String msgId, final String... args) { - return rangeError(Context.getGlobalTrusted(), msgId, args); + return rangeError(Context.getGlobal(), msgId, args); } /** @@ -233,7 +235,7 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException rangeError(final ScriptObject global, final String msgId, final String... args) { + public static ECMAException rangeError(final Global global, final String msgId, final String... args) { return rangeError(global, null, msgId, args); } @@ -247,7 +249,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException rangeError(final Throwable cause, final String msgId, final String... args) { - return rangeError(Context.getGlobalTrusted(), cause, msgId, args); + return rangeError(Context.getGlobal(), cause, msgId, args); } /** @@ -260,9 +262,9 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException rangeError(final ScriptObject global, final Throwable cause, final String msgId, final String... args) { + public static ECMAException rangeError(final Global global, final Throwable cause, final String msgId, final String... args) { final String msg = getMessage("range.error." + msgId, args); - return error(((GlobalObject)global).newRangeError(msg), cause); + return error(global.newRangeError(msg), cause); } /** @@ -274,7 +276,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException referenceError(final String msgId, final String... args) { - return referenceError(Context.getGlobalTrusted(), msgId, args); + return referenceError(Context.getGlobal(), msgId, args); } /** @@ -286,7 +288,7 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException referenceError(final ScriptObject global, final String msgId, final String... args) { + public static ECMAException referenceError(final Global global, final String msgId, final String... args) { return referenceError(global, null, msgId, args); } @@ -300,7 +302,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException referenceError(final Throwable cause, final String msgId, final String... args) { - return referenceError(Context.getGlobalTrusted(), cause, msgId, args); + return referenceError(Context.getGlobal(), cause, msgId, args); } /** @@ -313,9 +315,9 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException referenceError(final ScriptObject global, final Throwable cause, final String msgId, final String... args) { + public static ECMAException referenceError(final Global global, final Throwable cause, final String msgId, final String... args) { final String msg = getMessage("reference.error." + msgId, args); - return error(((GlobalObject)global).newReferenceError(msg), cause); + return error(global.newReferenceError(msg), cause); } /** @@ -327,7 +329,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException uriError(final String msgId, final String... args) { - return uriError(Context.getGlobalTrusted(), msgId, args); + return uriError(Context.getGlobal(), msgId, args); } /** @@ -339,7 +341,7 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException uriError(final ScriptObject global, final String msgId, final String... args) { + public static ECMAException uriError(final Global global, final String msgId, final String... args) { return uriError(global, null, msgId, args); } @@ -353,7 +355,7 @@ public final class ECMAErrors { * @return the resulting {@link ECMAException} */ public static ECMAException uriError(final Throwable cause, final String msgId, final String... args) { - return uriError(Context.getGlobalTrusted(), cause, msgId, args); + return uriError(Context.getGlobal(), cause, msgId, args); } /** @@ -366,9 +368,9 @@ public final class ECMAErrors { * * @return the resulting {@link ECMAException} */ - public static ECMAException uriError(final ScriptObject global, final Throwable cause, final String msgId, final String... args) { + public static ECMAException uriError(final Global global, final Throwable cause, final String msgId, final String... args) { final String msg = getMessage("uri.error." + msgId, args); - return error(((GlobalObject)global).newURIError(msg), cause); + return error(global.newURIError(msg), cause); } /** @@ -401,7 +403,7 @@ public final class ECMAErrors { final String className = frame.getClassName(); // Look for script package in class name (into which compiler puts generated code) - if (className.startsWith(scriptPackage)) { + if (className.startsWith(scriptPackage) && !frame.getMethodName().startsWith(CompilerConstants.INTERNAL_METHOD_PREFIX)) { final String source = frame.getFileName(); /* * Make sure that it is not some Java code that Nashorn has in that package! diff --git a/src/jdk/nashorn/internal/runtime/ECMAException.java b/src/jdk/nashorn/internal/runtime/ECMAException.java index a32e721c..c900963d 100644 --- a/src/jdk/nashorn/internal/runtime/ECMAException.java +++ b/src/jdk/nashorn/internal/runtime/ECMAException.java @@ -25,7 +25,7 @@ package jdk.nashorn.internal.runtime; -import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; +import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualField; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; @@ -44,9 +44,9 @@ import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess; @SuppressWarnings("serial") public final class ECMAException extends NashornException { /** - * Method handle pointing to the constructor {@link ECMAException#ECMAException(Object, String, int, int)}, + * Method handle pointing to the constructor {@link ECMAException#create(Object, String, int, int)}, */ - public static final Call THROW_INIT = constructorNoLookup(ECMAException.class, Object.class, String.class, int.class, int.class); + public static final Call CREATE = staticCallNoLookup(ECMAException.class, "create", ECMAException.class, Object.class, String.class, int.class, int.class); /** Field handle to the{@link ECMAException#thrown} field, so that it can be accessed from generated code */ public static final FieldAccess THROWN = virtualField(ECMAException.class, "thrown", Object.class); @@ -57,23 +57,21 @@ public final class ECMAException extends NashornException { public final Object thrown; /** - * Constructor. This is called from generated code to implement the {@code throw} - * instruction from generated script code + * Constructor. Called from the factory method 'create'. * * @param thrown object to be thrown * @param fileName script file name * @param line line number of throw * @param column column number of throw */ - public ECMAException(final Object thrown, final String fileName, final int line, final int column) { + private ECMAException(final Object thrown, final String fileName, final int line, final int column) { super(ScriptRuntime.safeToString(thrown), asThrowable(thrown), fileName, line, column); this.thrown = thrown; setExceptionToThrown(); } /** - * Constructor. This is called from runtime code in Nashorn to throw things like - * type errors. + * Constructor. This is called from the runtime code. * * @param thrown object to be thrown * @param cause Java exception that triggered this throw @@ -85,9 +83,39 @@ public final class ECMAException extends NashornException { } /** + * Factory method to retrieve the underlying exception or create an exception. + * This method is called from the generated code. + * + * @param thrown object to be thrown + * @param fileName script file name + * @param line line number of throw + * @param column column number of throw + * @return ECMAException object + */ + public static ECMAException create(final Object thrown, final String fileName, final int line, final int column) { + // If thrown object is an Error or sub-object like TypeError, then + // an ECMAException object has been already initialized at constructor. + if (thrown instanceof ScriptObject) { + ScriptObject sobj = (ScriptObject)thrown; + Object exception = getException(sobj); + if (exception instanceof ECMAException) { + // copy over file name, line number and column number. + final ECMAException ee = (ECMAException)exception; + ee.setFileName(fileName); + ee.setLineNumber(line); + ee.setColumnNumber(column); + return ee; + } + } + + return new ECMAException(thrown, fileName, line, column); + } + + /** * Get the thrown object * @return thrown object */ + @Override public Object getThrown() { return thrown; } @@ -256,6 +284,8 @@ public final class ECMAException extends NashornException { final ScriptObject sobj = (ScriptObject)thrown; if (!sobj.has(EXCEPTION_PROPERTY)) { sobj.addOwnProperty(EXCEPTION_PROPERTY, Property.NOT_ENUMERABLE, this); + } else { + sobj.set(EXCEPTION_PROPERTY, this, false); } } } diff --git a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java index c7a410ea..f16cf41c 100644 --- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java +++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java @@ -38,31 +38,27 @@ final class FinalScriptFunctionData extends ScriptFunctionData { /** * Constructor - used for bind * - * @param name name - * @param arity arity - * @param functions precompiled code - * @param isStrict strict - * @param isBuiltin builtin - * @param isConstructor constructor + * @param name name + * @param arity arity + * @param functions precompiled code + * @param flags {@link ScriptFunctionData} flags */ - FinalScriptFunctionData(final String name, int arity, CompiledFunctions functions, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { - super(name, arity, isStrict, isBuiltin, isConstructor); + FinalScriptFunctionData(final String name, final int arity, final CompiledFunctions functions, final int flags) { + super(name, arity, flags); code.addAll(functions); } /** - * Constructor - used from ScriptFunction. This assumes that we have code alraedy for the + * Constructor - used from ScriptFunction. This assumes that we have code already for the * method (typically a native method) and possibly specializations. * - * @param name name - * @param mh method handle for generic version of method - * @param specs specializations - * @param isStrict strict - * @param isBuiltin builtin - * @param isConstructor constructor + * @param name name + * @param mh method handle for generic version of method + * @param specs specializations + * @param flags {@link ScriptFunctionData} flags */ - FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { - super(name, arity(mh), isStrict, isBuiltin, isConstructor); + FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final int flags) { + super(name, arity(mh), flags); addInvoker(mh); if (specs != null) { diff --git a/src/jdk/nashorn/internal/runtime/FindProperty.java b/src/jdk/nashorn/internal/runtime/FindProperty.java index c14accb7..87a8c748 100644 --- a/src/jdk/nashorn/internal/runtime/FindProperty.java +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java @@ -112,7 +112,7 @@ public final class FindProperty { return property != null && property.hasGetterFunction(prototype) ? self : prototype; } - /** + /** * Return the appropriate receiver for a setter. * @return appropriate receiver */ @@ -172,5 +172,20 @@ public final class FindProperty { property.setObjectValue(getSetterReceiver(), getOwner(), value, strict); } + /** + * Get the number of objects in the prototype chain between the {@code self} and the + * {@code owner} objects. + * @return the prototype chain length + */ + int getProtoChainLength() { + assert self != null; + int length = 0; + for (ScriptObject obj = self; obj != prototype; obj = obj.getProto()) { + assert !(obj instanceof WithObject); + ++length; + } + return length; + } + } diff --git a/src/jdk/nashorn/internal/runtime/GlobalObject.java b/src/jdk/nashorn/internal/runtime/GlobalObject.java deleted file mode 100644 index 3779f3bd..00000000 --- a/src/jdk/nashorn/internal/runtime/GlobalObject.java +++ /dev/null @@ -1,244 +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; - -import java.lang.invoke.MethodHandle; -import java.util.concurrent.Callable; -import jdk.internal.dynalink.linker.GuardedInvocation; -import jdk.internal.dynalink.linker.LinkRequest; -import jdk.nashorn.internal.runtime.linker.InvokeByName; - -/** - * Runtime interface to the global scope objects. - */ - -public interface GlobalObject { - /** - * Is this global of the given Context? - * @param ctxt the context - * @return true if this global belongs to the given Context - */ - public boolean isOfContext(final Context ctxt); - - /** - * Does this global belong to a strict Context? - * @return true if this global belongs to a strict Context - */ - public boolean isStrictContext(); - - /** - * Initialize standard builtin objects like "Object", "Array", "Function" etc. - * as well as our extension builtin objects like "Java", "JSAdapter" as properties - * of the global scope object. - */ - public void initBuiltinObjects(); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newScriptFunction(String, MethodHandle, ScriptObject, boolean)} - * - * @param name function name - * @param handle invocation handle for function - * @param scope the scope - * @param strict are we in strict mode - * - * @return new script function - */ - public ScriptFunction newScriptFunction(String name, MethodHandle handle, ScriptObject scope, boolean strict); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#wrapAsObject(Object)} - * - * @param obj object to wrap - * @return wrapped object - */ - public Object wrapAsObject(Object obj); - - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#primitiveLookup(LinkRequest, Object)} - * - * @param request the link request for the dynamic call site. - * @param self self reference - * - * @return guarded invocation - */ - public GuardedInvocation primitiveLookup(LinkRequest request, Object self); - - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newObject()} - * - * @return the new ScriptObject - */ - public ScriptObject newObject(); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#isError(ScriptObject)} - * - * @param sobj to check if it is an error object - * @return true if error object - */ - public boolean isError(ScriptObject sobj); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newError(String)} - * - * @param msg the error message - * - * @return the new ScriptObject representing the error - */ - public ScriptObject newError(String msg); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newEvalError(String)} - * - * @param msg the error message - * - * @return the new ScriptObject representing the eval error - */ - public ScriptObject newEvalError(String msg); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newRangeError(String)} - * - * @param msg the error message - * - * @return the new ScriptObject representing the range error - */ - public ScriptObject newRangeError(String msg); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newReferenceError(String)} - * - * @param msg the error message - * - * @return the new ScriptObject representing the reference error - */ - public ScriptObject newReferenceError(String msg); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newSyntaxError(String)} - * - * @param msg the error message - * - * @return the new ScriptObject representing the syntax error - */ - public ScriptObject newSyntaxError(String msg); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newTypeError(String)} - * - * @param msg the error message - * - * @return the new ScriptObject representing the type error - */ - public ScriptObject newTypeError(String msg); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newURIError(String)} - * - * @param msg the error message - * - * @return the new ScriptObject representing the URI error - */ - public ScriptObject newURIError(String msg); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newGenericDescriptor(boolean, boolean)} - * - * @param configurable is the described property configurable - * @param enumerable is the described property enumerable - * - * @return property descriptor - */ - public PropertyDescriptor newGenericDescriptor(boolean configurable, boolean enumerable); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newDataDescriptor(Object, boolean, boolean, boolean)} - * - * @param value data value - * @param configurable is the described property configurable - * @param enumerable is the described property enumerable - * @param writable is the described property writable - * - * @return property descriptor - */ - public PropertyDescriptor newDataDescriptor(Object value, boolean configurable, boolean enumerable, boolean writable); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#newAccessorDescriptor(Object, Object, boolean, boolean)} - * - * @param get property getter, or null if none - * @param set property setter, or null if none - * @param configurable is the described property configurable - * @param enumerable is the described property enumerable - * - * @return property descriptor - */ - public PropertyDescriptor newAccessorDescriptor(Object get, Object set, boolean configurable, boolean enumerable); - - /** - * Wrapper for {@link jdk.nashorn.internal.objects.Global#getDefaultValue(ScriptObject, Class)} - * - * @param sobj script object - * @param typeHint type hint - * - * @return default value - */ - public Object getDefaultValue(ScriptObject sobj, Class<?> typeHint); - - /** - * Find the compiled Class for the given script source, if available - * - * @param source Source object of the script - * @return compiled Class object or null - */ - public Class<?> findCachedClass(Source source); - - /** - * Put the Source associated Class object in the Source-to-Class cache - * - * @param source Source of the script - * @param clazz compiled Class object for the source - */ - public void cacheClass(Source source, Class<?> clazz); - - /** - * Get cached InvokeByName object for the given key - * @param key key to be associated with InvokeByName object - * @param creator if InvokeByName is absent 'creator' is called to make one (lazy init) - * @return InvokeByName object associated with the key. - */ - public InvokeByName getInvokeByName(final Object key, final Callable<InvokeByName> creator); - - /** - * Get cached dynamic method handle for the given key - * @param key key to be associated with dynamic method handle - * @param creator if method handle is absent 'creator' is called to make one (lazy init) - * @return dynamic method handle associated with the key. - */ - public MethodHandle getDynamicInvoker(final Object key, final Callable<MethodHandle> creator); -} diff --git a/src/jdk/nashorn/internal/runtime/JSONFunctions.java b/src/jdk/nashorn/internal/runtime/JSONFunctions.java index 73552cb1..ab1de42f 100644 --- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java +++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java @@ -33,11 +33,14 @@ import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.UnaryNode; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.JSONParser; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.arrays.ArrayIndex; import jdk.nashorn.internal.runtime.linker.Bootstrap; +import static jdk.nashorn.internal.runtime.Source.sourceFor; + /** * Utilities used by "JSON" object implementation. */ @@ -47,7 +50,7 @@ public final class JSONFunctions { private static final Object REVIVER_INVOKER = new Object(); private static MethodHandle getREVIVER_INVOKER() { - return ((GlobalObject)Context.getGlobal()).getDynamicInvoker(REVIVER_INVOKER, + return Context.getGlobal().getDynamicInvoker(REVIVER_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { @@ -76,9 +79,7 @@ public final class JSONFunctions { */ public static Object parse(final Object text, final Object reviver) { final String str = JSType.toString(text); - final JSONParser parser = new JSONParser( - new Source("<json>", str), - new Context.ThrowErrorManager()); + final JSONParser parser = new JSONParser(sourceFor("<json>", str), new Context.ThrowErrorManager()); Node node; @@ -88,7 +89,7 @@ public final class JSONFunctions { throw ECMAErrors.syntaxError(e, "invalid.json", e.getMessage()); } - final ScriptObject global = Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); Object unfiltered = convertNode(global, node); return applyReviver(global, unfiltered, reviver); } @@ -98,10 +99,9 @@ public final class JSONFunctions { // parse helpers // apply 'reviver' function if available - private static Object applyReviver(final ScriptObject global, final Object unfiltered, final Object reviver) { + private static Object applyReviver(final Global global, final Object unfiltered, final Object reviver) { if (reviver instanceof ScriptFunction) { - assert global instanceof GlobalObject; - final ScriptObject root = ((GlobalObject)global).newObject(); + final ScriptObject root = global.newObject(); root.addOwnProperty("", Property.WRITABLE_ENUMERABLE_CONFIGURABLE, unfiltered); return walk(root, "", (ScriptFunction)reviver); } @@ -138,8 +138,8 @@ public final class JSONFunctions { } // Converts IR node to runtime value - private static Object convertNode(final ScriptObject global, final Node node) { - assert global instanceof GlobalObject; + private static Object convertNode(final Global global, final Node node) { + assert global instanceof Global; if (node instanceof LiteralNode) { // check for array literal @@ -157,7 +157,7 @@ public final class JSONFunctions { for (final Node elem : elements) { values[index++] = JSType.toNumber(convertNode(global, elem)); } - return ((GlobalObject)global).wrapAsObject(values); + return global.wrapAsObject(values); } final Object[] values = new Object[elements.length]; @@ -167,14 +167,14 @@ public final class JSONFunctions { values[index++] = convertNode(global, elem); } - return ((GlobalObject)global).wrapAsObject(values); + return global.wrapAsObject(values); } return ((LiteralNode<?>)node).getValue(); } else if (node instanceof ObjectNode) { final ObjectNode objNode = (ObjectNode) node; - final ScriptObject object = ((GlobalObject)global).newObject(); + final ScriptObject object = global.newObject(); for (final PropertyNode pNode: objNode.getElements()) { final Node valueNode = pNode.getValue(); diff --git a/src/jdk/nashorn/internal/runtime/JSType.java b/src/jdk/nashorn/internal/runtime/JSType.java index 54130196..11cecbfe 100644 --- a/src/jdk/nashorn/internal/runtime/JSType.java +++ b/src/jdk/nashorn/internal/runtime/JSType.java @@ -36,6 +36,7 @@ import java.util.List; import jdk.internal.dynalink.beans.StaticClass; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.internal.codegen.CompilerConstants.Call; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -437,7 +438,9 @@ public enum JSType { // encode integer part from least significant digit, then reverse do { - sb.append(chars.charAt((int) (intPart % radix))); + final double remainder = intPart % radix; + sb.append(chars.charAt((int) remainder)); + intPart -= remainder; intPart /= radix; } while (intPart >= 1.0); @@ -852,7 +855,7 @@ public enum JSType { * @return the wrapped object */ public static Object toScriptObject(final Object obj) { - return toScriptObject(Context.getGlobalTrusted(), obj); + return toScriptObject(Context.getGlobal(), obj); } /** @@ -865,7 +868,7 @@ public enum JSType { * * @return the wrapped object */ - public static Object toScriptObject(final ScriptObject global, final Object obj) { + public static Object toScriptObject(final Global global, final Object obj) { if (nullOrUndefined(obj)) { throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj)); } @@ -874,7 +877,7 @@ public enum JSType { return obj; } - return ((GlobalObject)global).wrapAsObject(obj); + return global.wrapAsObject(obj); } /** @@ -984,7 +987,7 @@ public enum JSType { if (obj instanceof ScriptObject) { if (safe) { final ScriptObject sobj = (ScriptObject)obj; - final GlobalObject gobj = (GlobalObject)Context.getGlobalTrusted(); + final Global gobj = Context.getGlobal(); return gobj.isError(sobj) ? ECMAException.safeToString(sobj) : sobj.safeToString(); diff --git a/src/jdk/nashorn/internal/runtime/ListAdapter.java b/src/jdk/nashorn/internal/runtime/ListAdapter.java index 26c22c39..9a3408fb 100644 --- a/src/jdk/nashorn/internal/runtime/ListAdapter.java +++ b/src/jdk/nashorn/internal/runtime/ListAdapter.java @@ -34,6 +34,7 @@ import java.util.RandomAccess; import java.util.concurrent.Callable; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.api.scripting.ScriptObjectMirror; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; @@ -54,7 +55,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random // These add to the back and front of the list private static final Object PUSH = new Object(); private static InvokeByName getPUSH() { - return ((GlobalObject)Context.getGlobal()).getInvokeByName(PUSH, + return Context.getGlobal().getInvokeByName(PUSH, new Callable<InvokeByName>() { @Override public InvokeByName call() { @@ -65,7 +66,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random private static final Object UNSHIFT = new Object(); private static InvokeByName getUNSHIFT() { - return ((GlobalObject)Context.getGlobal()).getInvokeByName(UNSHIFT, + return Context.getGlobal().getInvokeByName(UNSHIFT, new Callable<InvokeByName>() { @Override public InvokeByName call() { @@ -77,7 +78,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random // These remove from the back and front of the list private static final Object POP = new Object(); private static InvokeByName getPOP() { - return ((GlobalObject)Context.getGlobal()).getInvokeByName(POP, + return Context.getGlobal().getInvokeByName(POP, new Callable<InvokeByName>() { @Override public InvokeByName call() { @@ -88,7 +89,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random private static final Object SHIFT = new Object(); private static InvokeByName getSHIFT() { - return ((GlobalObject)Context.getGlobal()).getInvokeByName(SHIFT, + return Context.getGlobal().getInvokeByName(SHIFT, new Callable<InvokeByName>() { @Override public InvokeByName call() { @@ -100,7 +101,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random // These insert and remove in the middle of the list private static final Object SPLICE_ADD = new Object(); private static InvokeByName getSPLICE_ADD() { - return ((GlobalObject)Context.getGlobal()).getInvokeByName(SPLICE_ADD, + return Context.getGlobal().getInvokeByName(SPLICE_ADD, new Callable<InvokeByName>() { @Override public InvokeByName call() { @@ -111,7 +112,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random private static final Object SPLICE_REMOVE = new Object(); private static InvokeByName getSPLICE_REMOVE() { - return ((GlobalObject)Context.getGlobal()).getInvokeByName(SPLICE_REMOVE, + return Context.getGlobal().getInvokeByName(SPLICE_REMOVE, new Callable<InvokeByName>() { @Override public InvokeByName call() { diff --git a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java index 534d495c..fbca1c34 100644 --- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java +++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java @@ -35,7 +35,6 @@ import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.support.Guards; import jdk.nashorn.internal.lookup.MethodHandleFactory; import jdk.nashorn.internal.lookup.MethodHandleFunctionality; -import jdk.nashorn.internal.objects.NativeJava; import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Function; @@ -52,7 +51,7 @@ import jdk.nashorn.internal.objects.annotations.Function; * var ArrayList = java.util.ArrayList * var list = new ArrayList * </pre> - * You can also use {@link NativeJava#type(Object, Object)} to access Java classes. These two statements are mostly + * You can also use {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)} to access Java classes. These two statements are mostly * equivalent: * <pre> * var listType1 = java.util.ArrayList @@ -198,8 +197,26 @@ public final class NativeJavaPackage extends ScriptObject { @Override public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { final String propertyName = desc.getNameToken(2); - final String fullName = name.isEmpty() ? propertyName : name + "." + propertyName; + createProperty(propertyName); + return super.lookup(desc, request); + } + + @Override + protected Object invokeNoSuchProperty(final String name) { + return createProperty(name); + } + + @Override + public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { + return noSuchProperty(desc, request); + } + + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { + return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types)); + } + private Object createProperty(final String propertyName) { + final String fullName = name.isEmpty() ? propertyName : name + "." + propertyName; final Context context = Context.getContextTrusted(); Class<?> javaClass = null; @@ -209,21 +226,14 @@ public final class NativeJavaPackage extends ScriptObject { //ignored } + final Object propertyValue; if (javaClass == null) { - set(propertyName, new NativeJavaPackage(fullName, getProto()), false); + propertyValue = new NativeJavaPackage(fullName, getProto()); } else { - set(propertyName, StaticClass.forClass(javaClass), false); + propertyValue = StaticClass.forClass(javaClass); } - return super.lookup(desc, request); - } - - @Override - public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { - return noSuchProperty(desc, request); - } - - private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { - return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types)); + set(propertyName, propertyValue, false); + return propertyValue; } } diff --git a/src/jdk/nashorn/internal/runtime/ParserException.java b/src/jdk/nashorn/internal/runtime/ParserException.java index 2424bc3f..5137d596 100644 --- a/src/jdk/nashorn/internal/runtime/ParserException.java +++ b/src/jdk/nashorn/internal/runtime/ParserException.java @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import jdk.nashorn.api.scripting.NashornException; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Token; /** @@ -110,7 +111,7 @@ public final class ParserException extends NashornException { * Throw this {@code ParserException} as one of the 7 native JavaScript errors * @param global global scope object */ - public void throwAsEcmaException(final ScriptObject global) { + public void throwAsEcmaException(final Global global) { throw ECMAErrors.asEcmaException(global, this); } } diff --git a/src/jdk/nashorn/internal/runtime/Property.java b/src/jdk/nashorn/internal/runtime/Property.java index d1dbe2ad..b3f1e0af 100644 --- a/src/jdk/nashorn/internal/runtime/Property.java +++ b/src/jdk/nashorn/internal/runtime/Property.java @@ -29,6 +29,7 @@ import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE; import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE; import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; +import java.io.Serializable; import java.lang.invoke.MethodHandle; import java.util.Objects; import jdk.nashorn.internal.codegen.ObjectClassGenerator; @@ -43,7 +44,7 @@ import jdk.nashorn.internal.codegen.types.Type; * @see AccessorProperty * @see UserAccessorProperty */ -public abstract class Property { +public abstract class Property implements Serializable { /* * ECMA 8.6.1 Property Attributes * @@ -84,9 +85,13 @@ public abstract class Property { /** Can this property be undefined? */ public static final int CAN_BE_UNDEFINED = 1 << 8; - /* Is this a function declaration property ? */ + /** Is this a function declaration property ? */ public static final int IS_FUNCTION_DECLARATION = 1 << 9; + /** Is this property bound to a receiver? This means get/set operations will be delegated to + * a statically defined object instead of the object passed as callsite parameter. */ + public static final int IS_BOUND = 1 << 10; + /** Property key. */ private final String key; @@ -96,6 +101,8 @@ public abstract class Property { /** Property field number or spill slot. */ private final int slot; + private static final long serialVersionUID = 2099814273074501176L; + /** * Constructor * @@ -252,6 +259,16 @@ public abstract class Property { } /** + * Is this property bound to a receiver? If this method returns {@code true} get and set operations + * will be delegated to a statically bound object instead of the object passed as parameter. + * + * @return true if this is a bound property + */ + public boolean isBound() { + return (flags & IS_BOUND) == IS_BOUND; + } + + /** * Does this property use any slots in the spill array described in * {@link Property#isSpill}? In that case how many. Currently a property * only uses max one spill slot, but this may change in future representations @@ -344,6 +361,13 @@ public abstract class Property { public abstract MethodHandle getGetter(final Class<?> type); /** + * Hook to initialize method handles after deserialization. + * + * @param structure the structure class + */ + abstract void initMethodHandles(final Class<?> structure); + + /** * Get the key for this property. This key is an ordinary string. The "name". * @return key for property */ diff --git a/src/jdk/nashorn/internal/runtime/PropertyDescriptor.java b/src/jdk/nashorn/internal/runtime/PropertyDescriptor.java index 49a7a9d6..f36bd45e 100644 --- a/src/jdk/nashorn/internal/runtime/PropertyDescriptor.java +++ b/src/jdk/nashorn/internal/runtime/PropertyDescriptor.java @@ -151,5 +151,12 @@ public interface PropertyDescriptor { * @return true if property exists in implementor */ public boolean has(Object key); + + /** + * Check existence and compare attributes of descriptors. + * + * @return true if every field of this desc exists in otherDesc and has the same value. + */ + public boolean hasAndEquals(PropertyDescriptor otherDesc); } diff --git a/src/jdk/nashorn/internal/runtime/PropertyListener.java b/src/jdk/nashorn/internal/runtime/PropertyListener.java deleted file mode 100644 index 307d58b4..00000000 --- a/src/jdk/nashorn/internal/runtime/PropertyListener.java +++ /dev/null @@ -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; - -/** - * Property change listener gets notified whenever properties are added/deleted/modified. - */ -public interface PropertyListener { - /** - * A new property is being added. - * - * @param object The ScriptObject to which property was added. - * @param prop The new Property added. - */ - public void propertyAdded(ScriptObject object, Property prop); - - /** - * An existing property is being deleted. - * - * @param object The ScriptObject whose property is being deleted. - * @param prop The property being deleted. - */ - public void propertyDeleted(ScriptObject object, Property prop); - - /** - * An existing Property is being replaced with a new Property. - * - * @param object The ScriptObject whose property is being modified. - * @param oldProp The old property that is being replaced. - * @param newProp The new property that replaces the old property. - * - */ - public void propertyModified(ScriptObject object, Property oldProp, Property newProp); - - /** - * Given object's __proto__ has changed. - * - * @param object object whose __proto__ has changed. - * @param oldProto old __proto__ - * @param newProto new __proto__ - */ - public void protoChanged(ScriptObject object, ScriptObject oldProto, ScriptObject newProto); -} diff --git a/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java b/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java deleted file mode 100644 index 27ec2c97..00000000 --- a/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java +++ /dev/null @@ -1,179 +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; - -import java.util.Map; -import java.util.WeakHashMap; - -/** - * Helper class to manage property listeners and notification. - */ -public class PropertyListenerManager implements PropertyListener { - PropertyListenerManager() {} - - /** property listeners for this object. */ - private Map<PropertyListener,Boolean> listeners; - - // These counters are updated in debug mode - private static int listenersAdded; - private static int listenersRemoved; - - /** - * Return aggregate listeners added to all PropertyListenerManagers - * @return the listenersAdded - */ - public static int getListenersAdded() { - return listenersAdded; - } - - /** - * Return aggregate listeners removed from all PropertyListenerManagers - * @return the listenersRemoved - */ - public static int getListenersRemoved() { - return listenersRemoved; - } - - /** - * Return listeners added to this PropertyListenerManager. - * @return the listener count - */ - public final int getListenerCount() { - return listeners != null? listeners.size() : 0; - } - - // Property listener management methods - - /** - * Add a property listener to this object. - * - * @param listener The property listener that is added. - */ - public synchronized final void addPropertyListener(final PropertyListener listener) { - if (listeners == null) { - listeners = new WeakHashMap<>(); - } - - if (Context.DEBUG) { - listenersAdded++; - } - listeners.put(listener, Boolean.TRUE); - } - - /** - * Remove a property listener from this object. - * - * @param listener The property listener that is removed. - */ - public synchronized final void removePropertyListener(final PropertyListener listener) { - if (listeners != null) { - if (Context.DEBUG) { - listenersRemoved++; - } - listeners.remove(listener); - } - } - - /** - * This method can be called to notify property addition to this object's listeners. - * - * @param object The ScriptObject to which property was added. - * @param prop The property being added. - */ - protected synchronized final void notifyPropertyAdded(final ScriptObject object, final Property prop) { - if (listeners != null) { - for (PropertyListener listener : listeners.keySet()) { - listener.propertyAdded(object, prop); - } - } - } - - /** - * This method can be called to notify property deletion to this object's listeners. - * - * @param object The ScriptObject from which property was deleted. - * @param prop The property being deleted. - */ - protected synchronized final void notifyPropertyDeleted(final ScriptObject object, final Property prop) { - if (listeners != null) { - for (PropertyListener listener : listeners.keySet()) { - listener.propertyDeleted(object, prop); - } - } - } - - /** - * This method can be called to notify property modification to this object's listeners. - * - * @param object The ScriptObject to which property was modified. - * @param oldProp The old property being replaced. - * @param newProp The new property that replaces the old property. - */ - protected synchronized final void notifyPropertyModified(final ScriptObject object, final Property oldProp, final Property newProp) { - if (listeners != null) { - for (PropertyListener listener : listeners.keySet()) { - listener.propertyModified(object, oldProp, newProp); - } - } - } - - /** - * This method can be called to notify __proto__ modification to this object's listeners. - * - * @param object The ScriptObject whose __proto__ was changed. - * @param oldProto old __proto__ - * @param newProto new __proto__ - */ - protected synchronized final void notifyProtoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) { - if (listeners != null) { - for (PropertyListener listener : listeners.keySet()) { - listener.protoChanged(object, oldProto, newProto); - } - } - } - - // PropertyListener methods - - @Override - public final void propertyAdded(final ScriptObject object, final Property prop) { - notifyPropertyAdded(object, prop); - } - - @Override - public final void propertyDeleted(final ScriptObject object, final Property prop) { - notifyPropertyDeleted(object, prop); - } - - @Override - public final void propertyModified(final ScriptObject object, final Property oldProp, final Property newProp) { - notifyPropertyModified(object, oldProp, newProp); - } - - @Override - public final void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) { - notifyProtoChanged(object, oldProto, newProto); - } -} diff --git a/src/jdk/nashorn/internal/runtime/PropertyListeners.java b/src/jdk/nashorn/internal/runtime/PropertyListeners.java new file mode 100644 index 00000000..532969cc --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/PropertyListeners.java @@ -0,0 +1,222 @@ +/* + * 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.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * Helper class to manage property listeners and notification. + */ +public class PropertyListeners { + + private Map<String, WeakPropertyMapSet> listeners; + + // These counters are updated in debug mode + private static int listenersAdded; + private static int listenersRemoved; + + /** + * Copy constructor + * @param listener listener to copy + */ + PropertyListeners(final PropertyListeners listener) { + if (listener != null && listener.listeners != null) { + this.listeners = new WeakHashMap<>(listener.listeners); + } + } + + /** + * Return aggregate listeners added to all PropertyListenerManagers + * @return the listenersAdded + */ + public static int getListenersAdded() { + return listenersAdded; + } + + /** + * Return aggregate listeners removed from all PropertyListenerManagers + * @return the listenersRemoved + */ + public static int getListenersRemoved() { + return listenersRemoved; + } + + /** + * Return listeners added to this 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; + } + + // Property listener management methods + + /** + * Add {@code propertyMap} as property listener to {@code listeners} using key {@code key} by + * creating and returning a new {@code PropertyListeners} instance. + * + * @param listeners the original property listeners instance, may be null + * @param key the property key + * @param propertyMap the property map + * @return the new property map + */ + public static PropertyListeners addListener(final PropertyListeners listeners, final String key, final PropertyMap propertyMap) { + final PropertyListeners newListeners; + if (listeners == null || !listeners.containsListener(key, propertyMap)) { + newListeners = new PropertyListeners(listeners); + newListeners.addListener(key, propertyMap); + return newListeners; + } + return listeners; + } + + /** + * Checks whether {@code propertyMap} is registered as listener with {@code key}. + * + * @param key the property key + * @param propertyMap the property map + * @return true if property map is registered with property key + */ + synchronized boolean containsListener(final String key, final PropertyMap propertyMap) { + if (listeners == null) { + return false; + } + WeakPropertyMapSet set = listeners.get(key); + return set != null && set.contains(propertyMap); + } + + /** + * Add a property listener to this object. + * + * @param propertyMap The property listener that is added. + */ + synchronized final void addListener(final String key, final PropertyMap propertyMap) { + if (Context.DEBUG) { + listenersAdded++; + } + if (listeners == null) { + listeners = new WeakHashMap<>(); + } + + WeakPropertyMapSet set = listeners.get(key); + if (set == null) { + set = new WeakPropertyMapSet(); + listeners.put(key, set); + } + if (!set.contains(propertyMap)) { + set.add(propertyMap); + } + } + + /** + * A new property is being added. + * + * @param prop The new Property added. + */ + public synchronized void propertyAdded(final Property prop) { + if (listeners != null) { + WeakPropertyMapSet set = listeners.get(prop.getKey()); + if (set != null) { + for (PropertyMap propertyMap : set.elements()) { + propertyMap.propertyAdded(prop); + } + listeners.remove(prop.getKey()); + } + } + } + + /** + * An existing property is being deleted. + * + * @param prop The property being deleted. + */ + public synchronized void propertyDeleted(final Property prop) { + if (listeners != null) { + WeakPropertyMapSet set = listeners.get(prop.getKey()); + if (set != null) { + for (PropertyMap propertyMap : set.elements()) { + propertyMap.propertyDeleted(prop); + } + listeners.remove(prop.getKey()); + } + } + } + + /** + * An existing Property is being replaced with a new Property. + * + * @param oldProp The old property that is being replaced. + * @param newProp The new property that replaces the old property. + * + */ + public synchronized void propertyModified(final Property oldProp, final Property newProp) { + if (listeners != null) { + WeakPropertyMapSet set = listeners.get(oldProp.getKey()); + if (set != null) { + for (PropertyMap propertyMap : set.elements()) { + propertyMap.propertyModified(oldProp, newProp); + } + listeners.remove(oldProp.getKey()); + } + } + } + + public synchronized void protoChanged() { + if (listeners != null) { + for (WeakPropertyMapSet set : listeners.values()) { + for (PropertyMap propertyMap : set.elements()) { + propertyMap.protoChanged(); + } + } + listeners.clear(); + } + } + + private static class WeakPropertyMapSet { + + private WeakHashMap<PropertyMap, Boolean> map = new WeakHashMap<>(); + + void add(final PropertyMap propertyMap) { + map.put(propertyMap, Boolean.TRUE); + } + + boolean contains(final PropertyMap propertyMap) { + return map.containsKey(propertyMap); + } + + Set<PropertyMap> elements() { + return map.keySet(); + } + + } +} diff --git a/src/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk/nashorn/internal/runtime/PropertyMap.java index 03c4978a..e2e07fdf 100644 --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java @@ -29,16 +29,19 @@ import static jdk.nashorn.internal.runtime.PropertyHashMap.EMPTY_HASHMAP; import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.lang.invoke.SwitchPoint; -import java.lang.ref.WeakReference; +import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.NoSuchElementException; import java.util.WeakHashMap; +import jdk.nashorn.internal.scripts.JO; /** * Map of object properties. The PropertyMap is the "template" for JavaScript object @@ -49,23 +52,17 @@ import java.util.WeakHashMap; * 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>, PropertyListener { +public final class PropertyMap implements Iterable<Object>, Serializable { /** Used for non extensible PropertyMaps, negative logic as the normal case is extensible. See {@link ScriptObject#preventExtensions()} */ public static final int NOT_EXTENSIBLE = 0b0000_0001; /** Does this map contain valid array keys? */ public static final int CONTAINS_ARRAY_KEYS = 0b0000_0010; - /** This mask is used to preserve certain flags when cloning the PropertyMap. Others should not be copied */ - private static final int CLONEABLE_FLAGS_MASK = 0b0000_1111; - /** Has a listener been added to this property map. This flag is not copied when cloning a map. See {@link PropertyListener} */ - public static final int IS_LISTENER_ADDED = 0b0001_0000; - /** Is this process wide "shared" map?. This flag is not copied when cloning a map */ - public static final int IS_SHARED = 0b0010_0000; /** Map status flags. */ private int flags; /** Map of properties. */ - private final PropertyHashMap properties; + private transient PropertyHashMap properties; /** Number of fields in use. */ private int fieldCount; @@ -76,17 +73,22 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { /** Length of spill in use. */ private int spillLength; + /** Structure class name */ + private String className; + /** {@link SwitchPoint}s for gets on inherited properties. */ - private Map<String, SwitchPoint> protoGetSwitches; + private transient HashMap<String, SwitchPoint> protoGetSwitches; /** History of maps, used to limit map duplication. */ - private HashMap<Property, PropertyMap> history; + private transient WeakHashMap<Property, SoftReference<PropertyMap>> history; /** History of prototypes, used to limit map duplication. */ - private WeakHashMap<ScriptObject, WeakReference<PropertyMap>> protoHistory; + private transient WeakHashMap<PropertyMap, SoftReference<PropertyMap>> protoHistory; + + /** property listeners */ + private transient PropertyListeners listeners; - /** Cache for hashCode */ - private int hashCode; + private static final long serialVersionUID = -7041836752008732533L; /** * Constructor. @@ -97,8 +99,10 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @param spillLength Number of spill slots used. * @param containsArrayKeys True if properties contain numeric keys */ - private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) { + private PropertyMap(final PropertyHashMap properties, final String className, final int fieldCount, + final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) { this.properties = properties; + this.className = className; this.fieldCount = fieldCount; this.fieldMaximum = fieldMaximum; this.spillLength = spillLength; @@ -119,10 +123,12 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { */ private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties) { this.properties = properties; - this.flags = propertyMap.getClonedFlags(); + this.flags = propertyMap.flags; this.spillLength = propertyMap.spillLength; this.fieldCount = propertyMap.fieldCount; this.fieldMaximum = propertyMap.fieldMaximum; + // We inherit the parent property listeners instance. It will be cloned when a new listener is added. + this.listeners = propertyMap.listeners; if (Context.DEBUG) { count++; @@ -151,7 +157,25 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { if (Context.DEBUG) { duplicatedCount++; } - return new PropertyMap(this.properties, 0, 0, 0, containsArrayKeys()); + return new PropertyMap(this.properties, this.className, 0, 0, 0, containsArrayKeys()); + } + + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(properties.getProperties()); + } + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + + final Property[] props = (Property[]) in.readObject(); + this.properties = EMPTY_HASHMAP.immutableAdd(props); + + assert className != null; + final Class<?> structure = Context.forStructureClass(className); + for (Property prop : props) { + prop.initMethodHandles(structure); + } } /** @@ -166,9 +190,9 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @param spillLength Number of used spill slots. * @return New {@link PropertyMap}. */ - public static PropertyMap newMap(final Collection<Property> properties, final int fieldCount, final int fieldMaximum, final int spillLength) { + public static PropertyMap newMap(final Collection<Property> properties, final String className, final int fieldCount, final int fieldMaximum, final int spillLength) { PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties); - return new PropertyMap(newProperties, fieldCount, fieldMaximum, spillLength, false); + return new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength, false); } /** @@ -181,7 +205,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return New {@link PropertyMap}. */ public static PropertyMap newMap(final Collection<Property> properties) { - return (properties == null || properties.isEmpty())? newMap() : newMap(properties, 0, 0, 0); + return (properties == null || properties.isEmpty())? newMap() : newMap(properties, JO.class.getName(), 0, 0, 0); } /** @@ -190,7 +214,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return New empty {@link PropertyMap}. */ public static PropertyMap newMap() { - return new PropertyMap(EMPTY_HASHMAP, 0, 0, 0, false); + return new PropertyMap(EMPTY_HASHMAP, JO.class.getName(), 0, 0, 0, false); } /** @@ -203,35 +227,92 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** - * Return a SwitchPoint used to track changes of a property in a prototype. + * Get the listeners of this map, or null if none exists * - * @param proto Object prototype. - * @param key {@link Property} key. + * @return the listeners + */ + public PropertyListeners getListeners() { + return listeners; + } + + /** + * Add {@code listenerMap} as a listener to this property map for the given {@code key}. * - * @return A shared {@link SwitchPoint} for the property. + * @param key the property name + * @param listenerMap the listener map + */ + public void addListener(final String key, final PropertyMap listenerMap) { + if (listenerMap != this) { + // We need to clone listener instance when adding a new listener since we share + // the listeners instance with our parent maps that don't need to see the new listener. + listeners = PropertyListeners.addListener(listeners, key, listenerMap); + } + } + + /** + * A new property is being added. + * + * @param property The new Property added. */ - public SwitchPoint getProtoGetSwitchPoint(final ScriptObject proto, final String key) { - assert !isShared() : "proto SwitchPoint from a shared PropertyMap"; + public void propertyAdded(final Property property) { + invalidateProtoGetSwitchPoint(property); + if (listeners != null) { + listeners.propertyAdded(property); + } + } - if (proto == null) { - return null; + /** + * An existing property is being deleted. + * + * @param property The property being deleted. + */ + public void propertyDeleted(final Property property) { + invalidateProtoGetSwitchPoint(property); + if (listeners != null) { + listeners.propertyDeleted(property); } + } + /** + * An existing property is being redefined. + * + * @param oldProperty The old property + * @param newProperty The new property + */ + public void propertyModified(final Property oldProperty, final Property newProperty) { + invalidateProtoGetSwitchPoint(oldProperty); + if (listeners != null) { + listeners.propertyModified(oldProperty, newProperty); + } + } + + /** + * The prototype of an object associated with this {@link PropertyMap} is changed. + */ + public void protoChanged() { + invalidateAllProtoGetSwitchPoints(); + if (listeners != null) { + listeners.protoChanged(); + } + } + + /** + * Return a SwitchPoint used to track changes of a property in a prototype. + * + * @param key Property key. + * @return A shared {@link SwitchPoint} for the property. + */ + public synchronized SwitchPoint getSwitchPoint(final String key) { if (protoGetSwitches == null) { protoGetSwitches = new HashMap<>(); - if (! isListenerAdded()) { - proto.addPropertyListener(this); - setIsListenerAdded(); - } } - if (protoGetSwitches.containsKey(key)) { - return protoGetSwitches.get(key); + SwitchPoint switchPoint = protoGetSwitches.get(key); + if (switchPoint == null) { + switchPoint = new SwitchPoint(); + protoGetSwitches.put(key, switchPoint); } - final SwitchPoint switchPoint = new SwitchPoint(); - protoGetSwitches.put(key, switchPoint); - return switchPoint; } @@ -240,14 +321,13 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * * @param property {@link Property} to invalidate. */ - private void invalidateProtoGetSwitchPoint(final Property property) { - assert !isShared() : "proto invalidation on a shared PropertyMap"; - + synchronized void invalidateProtoGetSwitchPoint(final Property property) { if (protoGetSwitches != null) { + final String key = property.getKey(); final SwitchPoint sp = protoGetSwitches.get(key); if (sp != null) { - protoGetSwitches.put(key, new SwitchPoint()); + protoGetSwitches.remove(key); if (Context.DEBUG) { protoInvalidations++; } @@ -257,14 +337,15 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** - * Indicate that proto itself has changed in hierachy somewhere. + * Indicate that proto itself has changed in hierarchy somewhere. */ - private void invalidateAllProtoGetSwitchPoints() { - assert !isShared() : "proto invalidation on a shared PropertyMap"; - - if (protoGetSwitches != null) { - final Collection<SwitchPoint> sws = protoGetSwitches.values(); - SwitchPoint.invalidateAll(sws.toArray(new SwitchPoint[sws.size()])); + synchronized void invalidateAllProtoGetSwitchPoints() { + if (protoGetSwitches != null && !protoGetSwitches.isEmpty()) { + if (Context.DEBUG) { + protoInvalidations += protoGetSwitches.size(); + } + SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[protoGetSwitches.values().size()])); + protoGetSwitches.clear(); } } @@ -279,7 +360,33 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return New {@link PropertyMap} with {@link Property} added. */ PropertyMap addPropertyBind(final AccessorProperty property, final Object bindTo) { - return addProperty(new AccessorProperty(property, bindTo)); + // No need to store bound property in the history as bound properties can't be reused. + return addPropertyNoHistory(new AccessorProperty(property, bindTo)); + } + + /** + * Add a property to the map without adding it to the history. This should be used for properties that + * can't be shared such as bound properties, or properties that are expected to be added only once. + * + * @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); + + if(!property.isSpill()) { + newMap.fieldCount = Math.max(newMap.fieldCount, property.getSlot() + 1); + } + if (isValidArrayIndex(getArrayIndex(property.getKey()))) { + newMap.setContainsArrayKeys(); + } + + newMap.spillLength += property.getSpillCount(); + return newMap; } /** @@ -290,6 +397,9 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return New {@link PropertyMap} with {@link Property} added. */ public PropertyMap addProperty(final Property property) { + if (listeners != null) { + listeners.propertyAdded(property); + } PropertyMap newMap = checkHistory(property); if (newMap == null) { @@ -318,6 +428,9 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found. */ public PropertyMap deleteProperty(final Property property) { + if (listeners != null) { + listeners.propertyDeleted(property); + } PropertyMap newMap = checkHistory(property); final String key = property.getKey(); @@ -339,6 +452,9 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return New {@link PropertyMap} with {@link Property} replaced. */ PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) { + if (listeners != null) { + listeners.propertyModified(oldProperty, newProperty); + } // Add replaces existing property. final PropertyHashMap newProperties = properties.immutableAdd(newProperty); final PropertyMap newMap = new PropertyMap(this, newProperties); @@ -363,7 +479,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { (oldProperty instanceof AccessorProperty && newProperty instanceof UserAccessorProperty) : "arbitrary replaceProperty attempted"; - newMap.flags = getClonedFlags(); + newMap.flags = flags; /* * spillLength remains same in case (1) and (2) because of slot reuse. Only for case (3), we need @@ -491,28 +607,6 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** - * Make this property map 'shared' one. Shared property map instances are - * process wide singleton objects. A shaped map should never be added as a listener - * to a proto object. Nor it should have history or proto history. A shared map - * is just a template that is meant to be duplicated before use. All nasgen initialized - * property maps are shared. - * - * @return this map after making it as shared - */ - public PropertyMap setIsShared() { - assert !isListenerAdded() : "making PropertyMap shared after listener added"; - assert protoHistory == null : "making PropertyMap shared after associating a proto with it"; - if (Context.DEBUG) { - sharedCount++; - } - - flags |= IS_SHARED; - // clear any history on this PropertyMap, won't be used. - history = null; - return this; - } - - /** * Check for any configurable properties. * * @return {@code true} if any configurable. @@ -551,14 +645,14 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { /** * Check prototype history for an existing property map with specified prototype. * - * @param newProto New prototype object. + * @param parentMap New prototype object. * * @return Existing {@link PropertyMap} or {@code null} if not found. */ - private PropertyMap checkProtoHistory(final ScriptObject newProto) { + private PropertyMap checkProtoHistory(final PropertyMap parentMap) { final PropertyMap cachedMap; if (protoHistory != null) { - final WeakReference<PropertyMap> weakMap = protoHistory.get(newProto); + final SoftReference<PropertyMap> weakMap = protoHistory.get(parentMap); cachedMap = (weakMap != null ? weakMap.get() : null); } else { cachedMap = null; @@ -574,17 +668,15 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { /** * Add a map to the prototype history. * - * @param newProto Prototype to add (key.) + * @param parentMap Prototype to add (key.) * @param newMap {@link PropertyMap} associated with prototype. */ - private void addToProtoHistory(final ScriptObject newProto, final PropertyMap newMap) { - assert !isShared() : "proto history modified on a shared PropertyMap"; - + private void addToProtoHistory(final PropertyMap parentMap, final PropertyMap newMap) { if (protoHistory == null) { protoHistory = new WeakHashMap<>(); } - protoHistory.put(newProto, new WeakReference<>(newMap)); + protoHistory.put(parentMap, new SoftReference<>(newMap)); } /** @@ -594,14 +686,12 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @param newMap Modified {@link PropertyMap}. */ private void addToHistory(final Property property, final PropertyMap newMap) { - assert !isShared() : "history modified on a shared PropertyMap"; - if (!properties.isEmpty()) { if (history == null) { - history = new LinkedHashMap<>(); + history = new WeakHashMap<>(); } - history.put(property, newMap); + history.put(property, new SoftReference<>(newMap)); } } @@ -613,8 +703,10 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { * @return Existing map or {@code null} if not found. */ private PropertyMap checkHistory(final Property property) { + if (history != null) { - PropertyMap historicMap = history.get(property); + SoftReference<PropertyMap> ref = history.get(property); + final PropertyMap historicMap = ref == null ? null : ref.get(); if (historicMap != null) { if (Context.DEBUG) { @@ -628,54 +720,6 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { return null; } - /** - * Calculate the hash code for the map. - * - * @return Computed hash code. - */ - private int computeHashCode() { - int hash = 0; - - for (final Property property : getProperties()) { - hash = hash << 7 ^ hash >> 7; - hash ^= property.hashCode(); - } - - return hash; - } - - @Override - public int hashCode() { - if (hashCode == 0 && !properties.isEmpty()) { - hashCode = computeHashCode(); - } - return hashCode; - } - - @Override - public boolean equals(final Object other) { - if (!(other instanceof PropertyMap)) { - return false; - } - - final PropertyMap otherMap = (PropertyMap)other; - - if (properties.size() != otherMap.properties.size()) { - return false; - } - - final Iterator<Property> iter = properties.values().iterator(); - final Iterator<Property> otherIter = otherMap.properties.values().iterator(); - - while (iter.hasNext() && otherIter.hasNext()) { - if (!iter.next().equals(otherIter.next())) { - return false; - } - } - - return true; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder(); @@ -728,24 +772,6 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** - * Check whether a {@link PropertyListener} has been added to this map. - * - * @return {@code true} if {@link PropertyListener} exists - */ - public boolean isListenerAdded() { - return (flags & IS_LISTENER_ADDED) != 0; - } - - /** - * Check if this map shared or not. - * - * @return true if this map is shared. - */ - public boolean isShared() { - return (flags & IS_SHARED) != 0; - } - - /** * Test to see if {@link PropertyMap} is extensible. * * @return {@code true} if {@link PropertyMap} can be added to. @@ -800,50 +826,29 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** - * Change the prototype of objects associated with this {@link PropertyMap}. + * Return a property map with the same layout that is associated with the new prototype object. * - * @param oldProto Current prototype object. * @param newProto New prototype object to replace oldProto. - * * @return New {@link PropertyMap} with prototype changed. */ - PropertyMap changeProto(final ScriptObject oldProto, final ScriptObject newProto) { - assert !isShared() : "proto associated with a shared PropertyMap"; - - if (oldProto == newProto) { - return this; - } + public PropertyMap changeProto(final ScriptObject newProto) { - final PropertyMap nextMap = checkProtoHistory(newProto); + final PropertyMap parentMap = newProto == null ? null : newProto.getMap(); + final PropertyMap nextMap = checkProtoHistory(parentMap); if (nextMap != null) { return nextMap; } if (Context.DEBUG) { - incrementSetProtoNewMapCount(); + setProtoNewMapCount++; } final PropertyMap newMap = new PropertyMap(this); - addToProtoHistory(newProto, newMap); + addToProtoHistory(parentMap, newMap); return newMap; } - /** - * Indicate that the map has listeners. - */ - private void setIsListenerAdded() { - flags |= IS_LISTENER_ADDED; - } - - /** - * Return only the flags that should be copied during cloning. - * - * @return Subset of flags that should be copied. - */ - private int getClonedFlags() { - return flags & CLONEABLE_FLAGS_MASK; - } /** * {@link PropertyMap} iterator. @@ -900,41 +905,12 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /* - * PropertyListener implementation. - */ - - @Override - public void propertyAdded(final ScriptObject object, final Property prop) { - invalidateProtoGetSwitchPoint(prop); - } - - @Override - public void propertyDeleted(final ScriptObject object, final Property prop) { - invalidateProtoGetSwitchPoint(prop); - } - - @Override - public void propertyModified(final ScriptObject object, final Property oldProp, final Property newProp) { - invalidateProtoGetSwitchPoint(oldProp); - } - - @Override - public void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) { - // We may walk and invalidate SwitchPoints for properties inherited - // from 'object' or it's old proto chain. But, it may not be worth it. - // For example, a new proto may have a user defined getter/setter for - // a data property down the chain. So, invalidating all is better. - invalidateAllProtoGetSwitchPoints(); - } - - /* * Debugging and statistics. */ // counters updated only in debug mode private static int count; private static int clonedCount; - private static int sharedCount; private static int duplicatedCount; private static int historyHit; private static int protoInvalidations; @@ -956,13 +932,6 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { } /** - * @return The number of maps that are shared. - */ - public static int getSharedCount() { - return sharedCount; - } - - /** * @return The number of maps that are duplicated. */ public static int getDuplicatedCount() { @@ -997,10 +966,4 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener { return setProtoNewMapCount; } - /** - * Increment the prototype set count. - */ - private static void incrementSetProtoNewMapCount() { - setProtoNewMapCount++; - } } diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java index e5ca3c9a..90885444 100644 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java @@ -27,14 +27,15 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.lookup.Lookup.MH; +import java.io.Serializable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; +import java.util.Map; import jdk.internal.dynalink.support.NameCodec; - import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.FunctionSignature; @@ -43,6 +44,7 @@ import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; +import jdk.nashorn.internal.scripts.JS; /** * This is a subclass that represents a script function that may be regenerated, @@ -50,13 +52,19 @@ import jdk.nashorn.internal.parser.TokenType; * The common denominator is that it can get new invokers during its lifespan, * unlike {@code FinalScriptFunctionData} */ -public final class RecompilableScriptFunctionData extends ScriptFunctionData { +public final class RecompilableScriptFunctionData extends ScriptFunctionData implements Serializable { /** FunctionNode with the code for this ScriptFunction */ - private FunctionNode functionNode; + private transient FunctionNode functionNode; /** Source from which FunctionNode was parsed. */ - private final Source source; + private transient Source source; + + /** The line number where this function begins. */ + private final int lineNumber; + + /** Allows us to retrieve the method handle for this function once the code is compiled */ + private MethodLocator methodLocator; /** Token of this function within the source. */ private final long token; @@ -65,13 +73,13 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { private final PropertyMap allocatorMap; /** Code installer used for all further recompilation/specialization of this ScriptFunction */ - private CodeInstaller<ScriptEnvironment> installer; + private transient CodeInstaller<ScriptEnvironment> installer; /** Name of class where allocator function resides */ private final String allocatorClassName; /** lazily generated allocator */ - private MethodHandle allocator; + private transient MethodHandle allocator; private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); @@ -79,7 +87,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { * Used for specialization based on runtime arguments. Whenever we specialize on * callsite parameter types at runtime, we need to use a parameter type guard to * ensure that the specialized version of the script function continues to be - * applicable for a particular callsite * + * applicable for a particular callsite. */ private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Object[].class); @@ -88,10 +96,12 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { * (or java.lang.Number instance) to specialize the parameter to an integer, if the * parameter in question can be represented as one. The double typically only exists * because the compiler doesn't know any better than "a number type" and conservatively - * picks doubles when it can't prove that an integer addition wouldn't overflow + * picks doubles when it can't prove that an integer addition wouldn't overflow. */ private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class); + private static final long serialVersionUID = 4914839316174633726L; + /** * Constructor - public as scripts use it * @@ -103,16 +113,17 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) { super(functionName(functionNode), functionNode.getParameters().size(), - functionNode.isStrict(), - false, - true); - + getFlags(functionNode)); this.functionNode = functionNode; this.source = functionNode.getSource(); + this.lineNumber = functionNode.getLineNumber(); this.token = tokenFor(functionNode); this.installer = installer; this.allocatorClassName = allocatorClassName; this.allocatorMap = allocatorMap; + if (!functionNode.isLazy()) { + methodLocator = new MethodLocator(functionNode); + } } @Override @@ -124,15 +135,19 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { return "function " + (name == null ? "" : name) + "() { [native code] }"; } + public void setCodeAndSource(final Map<String, Class<?>> code, final Source source) { + this.source = source; + if (methodLocator != null) { + methodLocator.setClass(code.get(methodLocator.getClassName())); + } + } + @Override public String toString() { final StringBuilder sb = new StringBuilder(); if (source != null) { - sb.append(source.getName()) - .append(':') - .append(functionNode.getLineNumber()) - .append(' '); + sb.append(source.getName()).append(':').append(lineNumber).append(' '); } return sb.toString() + super.toString(); @@ -159,11 +174,25 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { return Token.toDesc(TokenType.FUNCTION, position, length); } + private static int getFlags(final FunctionNode functionNode) { + int flags = IS_CONSTRUCTOR; + if (functionNode.isStrict()) { + flags |= IS_STRICT; + } + if (functionNode.needsCallee()) { + flags |= NEEDS_CALLEE; + } + if (functionNode.usesThis() || functionNode.hasEval()) { + flags |= USES_THIS; + } + return flags; + } + @Override - ScriptObject allocate() { + ScriptObject allocate(final PropertyMap map) { try { ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try - return allocator == null ? null : (ScriptObject)allocator.invokeExact(allocatorMap); + return allocator == null ? null : (ScriptObject)allocator.invokeExact(map); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { @@ -178,40 +207,52 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { } @Override + PropertyMap getAllocatorMap() { + return allocatorMap; + } + + + @Override + protected void ensureCompiled() { + if (functionNode != null && functionNode.isLazy()) { + Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'"); + final Compiler compiler = new Compiler(installer); + functionNode = compiler.compile(functionNode); + assert !functionNode.isLazy(); + compiler.install(functionNode); + methodLocator = new MethodLocator(functionNode); + flags = getFlags(functionNode); + } + + if (functionNode != null) { + methodLocator.setClass(functionNode.getCompileUnit().getCode()); + } + } + + @Override protected synchronized void ensureCodeGenerated() { - if (!code.isEmpty()) { - return; // nothing to do, we have code, at least some. - } - - if (functionNode.isLazy()) { - Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'"); - final Compiler compiler = new Compiler(installer); - functionNode = compiler.compile(functionNode); - assert !functionNode.isLazy(); - compiler.install(functionNode); - - /* - * We don't need to update any flags - varArgs and needsCallee are instrincic - * in the function world we need to get a destination node from the compile instead - * and replace it with our function node. TODO - */ - } - - /* - * We can't get to this program point unless we have bytecode, either from - * eager compilation or from running a lazy compile on the lines above - */ - - assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode); - - // code exists - look it up and add it into the automatically sorted invoker list - addCode(functionNode); - - if (! functionNode.canSpecialize()) { - // allow GC to claim IR stuff that is not needed anymore - functionNode = null; - installer = null; - } + if (!code.isEmpty()) { + return; // nothing to do, we have code, at least some. + } + + ensureCompiled(); + + /* + * We can't get to this program point unless we have bytecode, either from + * eager compilation or from running a lazy compile on the lines above + */ + + assert functionNode == null || functionNode.hasState(CompilationState.EMITTED) : + functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode); + + // code exists - look it up and add it into the automatically sorted invoker list + addCode(functionNode); + + if (functionNode != null && !functionNode.canSpecialize()) { + // allow GC to claim IR stuff that is not needed anymore + functionNode = null; + installer = null; + } } private MethodHandle addCode(final FunctionNode fn) { @@ -219,13 +260,9 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { } private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) { - final MethodType targetType = new FunctionSignature(fn).getMethodType(); - MethodHandle target = - MH.findStatic( - LOOKUP, - fn.getCompileUnit().getCode(), - fn.getName(), - targetType); + assert methodLocator != null; + MethodHandle target = methodLocator.getMethodHandle(); + final MethodType targetType = methodLocator.getMethodType(); /* * For any integer argument. a double that is representable as an integer is OK. @@ -405,7 +442,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args)); - assert snapshot != null; assert snapshot != functionNode; final Compiler compiler = new Compiler(installer); @@ -431,5 +467,45 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData { return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types)); } + /** + * Helper class that allows us to retrieve the method handle for this function once it has been generated. + */ + private static class MethodLocator implements Serializable { + private transient Class<?> clazz; + private final String className; + private final String methodName; + private final MethodType methodType; + + private static final long serialVersionUID = -5420835725902966692L; + + MethodLocator(final FunctionNode functionNode) { + this.className = functionNode.getCompileUnit().getUnitClassName(); + this.methodName = functionNode.getName(); + this.methodType = new FunctionSignature(functionNode).getMethodType(); + + assert className != null; + assert methodName != null; + } + + void setClass(final Class<?> clazz) { + if (!JS.class.isAssignableFrom(clazz)) { + throw new IllegalArgumentException(); + } + this.clazz = clazz; + } + + String getClassName() { + return className; + } + + MethodType getMethodType() { + return methodType; + } + + MethodHandle getMethodHandle() { + return MH.findStatic(LOOKUP, clazz, methodName, methodType); + } + } + } diff --git a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java index 53274710..445106d7 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java @@ -62,6 +62,9 @@ public final class ScriptEnvironment { /** Only compile script, do not run it or generate other ScriptObjects */ public final boolean _compile_only; + /** Accept "const" keyword and treat it as variable. Interim feature */ + public final boolean _const_as_var; + /** Accumulated callsite flags that will be used when bootstrapping script callsites */ public final int _callsite_flags; @@ -134,6 +137,9 @@ public final class ScriptEnvironment { /** Only parse the source code, do not compile */ public final boolean _parse_only; + /** Enable disk cache for compiled scripts */ + public final boolean _persistent_cache; + /** Print the AST before lowering */ public final boolean _print_ast; @@ -197,6 +203,7 @@ public final class ScriptEnvironment { _class_cache_size = options.getInteger("class.cache.size"); _compile_only = options.getBoolean("compile.only"); + _const_as_var = options.getBoolean("const.as.var"); _debug_lines = options.getBoolean("debug.lines"); _dest_dir = options.getString("d"); _dump_on_error = options.getBoolean("doe"); @@ -218,6 +225,7 @@ public final class ScriptEnvironment { _no_syntax_extensions = options.getBoolean("no.syntax.extensions"); _no_typed_arrays = options.getBoolean("no.typed.arrays"); _parse_only = options.getBoolean("parse.only"); + _persistent_cache = options.getBoolean("persistent.code.cache"); _print_ast = options.getBoolean("print.ast"); _print_lower_ast = options.getBoolean("print.lower.ast"); _print_code = options.getBoolean("print.code"); diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java index d0669d70..3d8b4ff2 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java @@ -38,6 +38,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.lookup.MethodHandleFactory; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornGuards; @@ -66,6 +67,8 @@ public abstract class ScriptFunction extends ScriptObject { private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class); + private static final MethodHandle GLOBALFILTER = findOwnMH("globalFilter", Object.class, Object.class); + /** method handle to scope getter for this ScriptFunction */ public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); @@ -80,6 +83,9 @@ public abstract class ScriptFunction extends ScriptObject { private final ScriptFunctionData data; + /** The property map used for newly allocated object when function is used as constructor. */ + protected PropertyMap allocatorMap; + /** * Constructor * @@ -88,9 +94,7 @@ public abstract class ScriptFunction extends ScriptObject { * @param map property map * @param scope scope * @param specs specialized version of this function - other method handles - * @param strict is this a strict mode function? - * @param builtin is this a built in function? - * @param isConstructor is this a constructor? + * @param flags {@link ScriptFunctionData} flags */ protected ScriptFunction( final String name, @@ -98,11 +102,9 @@ public abstract class ScriptFunction extends ScriptObject { final PropertyMap map, final ScriptObject scope, final MethodHandle[] specs, - final boolean strict, - final boolean builtin, - final boolean isConstructor) { + final int flags) { - this(new FinalScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope); + this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope); } /** @@ -125,6 +127,7 @@ public abstract class ScriptFunction extends ScriptObject { this.data = data; this.scope = scope; + this.allocatorMap = data.getAllocatorMap(); } @Override @@ -229,16 +232,16 @@ public abstract class ScriptFunction extends ScriptObject { } assert !isBoundFunction(); // allocate never invoked on bound functions - final ScriptObject object = data.allocate(); + final ScriptObject object = data.allocate(allocatorMap); if (object != null) { Object prototype = getPrototype(); if (prototype instanceof ScriptObject) { - object.setProto((ScriptObject)prototype); + object.setInitialProto((ScriptObject)prototype); } if (object.getProto() == null) { - object.setProto(getObjectPrototype()); + object.setInitialProto(getObjectPrototype()); } } @@ -473,7 +476,14 @@ public abstract class ScriptFunction extends ScriptObject { if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) { return obj; } - return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(obj); + return Context.getGlobal().wrapAsObject(obj); + } + + + @SuppressWarnings("unused") + private static Object globalFilter(final Object object) { + // replace whatever we get with the current global object + return Context.getGlobal(); } /** @@ -491,11 +501,11 @@ public abstract class ScriptFunction extends ScriptObject { @Override protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { final MethodType type = desc.getMethodType(); + final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); if (request.isCallSiteUnstable()) { - // (this, callee, args...) => (this, callee, args[]) - final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, - type.parameterCount() - 2); + // (callee, this, args...) => (callee, this, args[]) + final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2); // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a // generic "is this a ScriptFunction?" guard. @@ -506,17 +516,12 @@ public abstract class ScriptFunction extends ScriptObject { MethodHandle boundHandle; MethodHandle guard = null; - final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); - if (data.needsCallee()) { final MethodHandle callHandle = getBestInvoker(type, request.getArguments()); - if (scopeCall) { + if (scopeCall && needsWrappedThis()) { // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined - // (callee, this, args...) => (callee, args...) - boundHandle = MH.insertArguments(callHandle, 1, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); - // (callee, args...) => (callee, [this], args...) - boundHandle = MH.dropArguments(boundHandle, 1, Object.class); - + // (callee, this, args...) => (callee, [this], args...) + boundHandle = MH.filterArguments(callHandle, 1, GLOBALFILTER); } else { // It's already (callee, this, args...), just what we need boundHandle = callHandle; @@ -527,12 +532,12 @@ public abstract class ScriptFunction extends ScriptObject { // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the // current lookup as its "this" so it can do security-sensitive creation of adapter classes. boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, Object.class, Object.class); - } else if (scopeCall) { + } else if (scopeCall && needsWrappedThis()) { // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined - // (this, args...) => (args...) - boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); - // (args...) => ([callee], [this], args...) - boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class); + // (this, args...) => ([this], args...) + boundHandle = MH.filterArguments(callHandle, 0, GLOBALFILTER); + // ([this], args...) => ([callee], [this], args...) + boundHandle = MH.dropArguments(boundHandle, 0, Object.class); } else { // (this, args...) => ([callee], this, args...) boundHandle = MH.dropArguments(callHandle, 0, Object.class); diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java index 4dbbfba9..ad40d622 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java @@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import java.io.Serializable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -39,7 +40,7 @@ import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; * Instances of this class are created during codegen and stored in script classes' * constants array to reduce function instantiation overhead during runtime. */ -public abstract class ScriptFunctionData { +public abstract class ScriptFunctionData implements Serializable { /** Name of the function or "" for anonynous functions */ protected final String name; @@ -47,33 +48,46 @@ public abstract class ScriptFunctionData { /** All versions of this function that have been generated to code */ protected final CompiledFunctions code; - private int arity; - - private final boolean isStrict; - - private final boolean isBuiltin; + /** Function flags */ + protected int flags; - private final boolean isConstructor; + private int arity; private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class); + /** Is this a strict mode function? */ + public static final int IS_STRICT = 1 << 0; + /** Is this a built-in function? */ + public static final int IS_BUILTIN = 1 << 1; + /** Is this a constructor function? */ + public static final int IS_CONSTRUCTOR = 1 << 2; + /** Does this function expect a callee argument? */ + public static final int NEEDS_CALLEE = 1 << 3; + /** Does this function make use of the this-object argument? */ + public static final int USES_THIS = 1 << 4; + + /** Flag for strict or built-in functions */ + public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN; + /** Flag for built-in constructors */ + public static final int IS_BUILTIN_CONSTRUCTOR = IS_BUILTIN | IS_CONSTRUCTOR; + /** Flag for strict constructors */ + public static final int IS_STRICT_CONSTRUCTOR = IS_STRICT | IS_CONSTRUCTOR; + + private static final long serialVersionUID = 4252901245508769114L; + /** * Constructor * * @param name script function name * @param arity arity - * @param isStrict is the function strict - * @param isBuiltin is the function built in - * @param isConstructor is the function a constructor + * @param flags the function flags */ - ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { - this.name = name; - this.arity = arity; - this.code = new CompiledFunctions(); - this.isStrict = isStrict; - this.isBuiltin = isBuiltin; - this.isConstructor = isConstructor; + ScriptFunctionData(final String name, final int arity, final int flags) { + this.name = name; + this.arity = arity; + this.code = new CompiledFunctions(); + this.flags = flags; } final int getArity() { @@ -105,21 +119,21 @@ public abstract class ScriptFunctionData { * @return true if strict, false otherwise */ public boolean isStrict() { - return isStrict; + return (flags & IS_STRICT) != 0; } boolean isBuiltin() { - return isBuiltin; + return (flags & IS_BUILTIN) != 0; } boolean isConstructor() { - return isConstructor; + return (flags & IS_CONSTRUCTOR) != 0; } boolean needsCallee() { - // we don't know if we need a callee or not unless we are generated - ensureCodeGenerated(); - return code.needsCallee(); + // we don't know if we need a callee or not unless code has been compiled + ensureCompiled(); + return (flags & NEEDS_CALLEE) != 0; } /** @@ -128,7 +142,7 @@ public abstract class ScriptFunctionData { * @return true if this argument must be an object */ boolean needsWrappedThis() { - return !isStrict && !isBuiltin; + return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0; } String toSource() { @@ -202,6 +216,15 @@ public abstract class ScriptFunctionData { } /** + * If we can have lazy code generation, this is a hook to ensure that the code has been compiled. + * This does not guarantee the code been installed in this {@code ScriptFunctionData} instance; + * use {@link #ensureCodeGenerated()} to install the actual method handles. + */ + protected void ensureCompiled() { + //empty + } + + /** * Return a generic Object/Object invoker for this method. It will ensure code * is generated, get the most generic of all versions of this function and adapt it * to Objects. @@ -229,9 +252,20 @@ public abstract class ScriptFunctionData { /** * Allocates an object using this function's allocator. + * + * @param map the property map for the allocated object. * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator. */ - ScriptObject allocate() { + ScriptObject allocate(final PropertyMap map) { + return null; + } + + /** + * Get the property map to use for objects allocated by this function. + * + * @return the property map for allocated objects. + */ + PropertyMap getAllocatorMap() { return null; } @@ -248,6 +282,8 @@ public abstract class ScriptFunctionData { final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args; final int length = args == null ? 0 : args.length; + // Clear the callee and this flags + final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS; CompiledFunctions boundList = new CompiledFunctions(); if (code.size() == 1) { @@ -262,8 +298,7 @@ public abstract class ScriptFunctionData { boundList.add(bind(inv, fn, self, allArgs)); } - ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor()); - return boundData; + return new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, boundFlags); } /** @@ -340,11 +375,11 @@ public abstract class ScriptFunctionData { private Object convertThisObject(final Object thiz) { if (!(thiz instanceof ScriptObject) && needsWrappedThis()) { if (JSType.nullOrUndefined(thiz)) { - return Context.getGlobalTrusted(); + return Context.getGlobal(); } if (isPrimitiveThis(thiz)) { - return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(thiz); + return Context.getGlobal().wrapAsObject(thiz); } } diff --git a/src/jdk/nashorn/internal/runtime/ScriptLoader.java b/src/jdk/nashorn/internal/runtime/ScriptLoader.java index 78185b1c..52c98d40 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptLoader.java +++ b/src/jdk/nashorn/internal/runtime/ScriptLoader.java @@ -70,9 +70,8 @@ final class ScriptLoader extends NashornLoader { * @return Installed class. */ synchronized Class<?> installClass(final String name, final byte[] data, final CodeSource cs) { - if (cs == null) { - return defineClass(name, data, 0, data.length, new ProtectionDomain(null, getPermissions(null))); - } + // null check + cs.getClass(); return defineClass(name, data, 0, data.length, cs); } } diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java index 02160d69..d4556d89 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java @@ -43,6 +43,7 @@ import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.invoke.SwitchPoint; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; @@ -65,6 +66,7 @@ import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.lookup.MethodHandleFactory; import jdk.nashorn.internal.objects.AccessorPropertyDescriptor; import jdk.nashorn.internal.objects.DataPropertyDescriptor; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.arrays.ArrayIndex; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -88,8 +90,8 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards; * </ul> */ -public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess { - /** __proto__ special property name */ +public abstract class ScriptObject implements PropertyAccess { + /** __proto__ special property name inside object literals. ES6 draft. */ public static final String PROTO_PROPERTY_NAME = "__proto__"; /** Search fall back routine name for "no such method" */ @@ -107,9 +109,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr /** Per ScriptObject flag - is this an arguments object? */ public static final int IS_ARGUMENTS = 0b0000_0100; - /** Is this a prototype PropertyMap? */ - public static final int IS_PROTOTYPE = 0b0000_1000; - /** Is length property not-writable? */ public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000; @@ -131,9 +130,12 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr /** Indexed array data. */ private ArrayData arrayData; - static final MethodHandle GETPROTO = findOwnMH("getProto", ScriptObject.class); - static final MethodHandle SETPROTOCHECK = findOwnMH("setProtoCheck", void.class, Object.class); - static final MethodHandle MEGAMORPHIC_GET = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class); + /** Method handle to retrive prototype of this object */ + public static final MethodHandle GETPROTO = findOwnMH("getProto", ScriptObject.class); + /** Method handle to set prototype of this object */ + public static final MethodHandle SETPROTOCHECK = findOwnMH("setProtoCheck", void.class, Object.class); + static final MethodHandle MEGAMORPHIC_GET = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class, boolean.class); + static final MethodHandle GLOBALFILTER = findOwnMH("globalFilter", Object.class, Object.class); static final MethodHandle SETFIELD = findOwnMH("setField", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class); static final MethodHandle SETSPILL = findOwnMH("setSpill", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class); @@ -143,6 +145,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr private static final MethodHandle TRUNCATINGFILTER = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class); private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class); + private static final ArrayList<MethodHandle> protoFilters = new ArrayList<>(); + /** Method handle for getting a function argument at a given index. Used from MapCreator */ public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class); @@ -153,7 +157,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class); /** Method handle for setting the proto of a ScriptObject */ - public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class); + public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setInitialProto", void.class, ScriptObject.class); /** Method handle for setting the proto of a ScriptObject after checking argument */ public static final Call SET_PROTO_CHECK = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class); @@ -199,10 +203,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr this.arrayData = ArrayData.EMPTY_ARRAY; this.setMap(map == null ? PropertyMap.newMap() : map); this.proto = proto; - - if (proto != null) { - proto.setIsPrototype(); - } } /** @@ -229,8 +229,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final Property oldProp = newMap.findProperty(key); if (oldProp == null) { 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)); - newMap = newMap.addProperty(prop); + newMap = newMap.addPropertyNoHistory(prop); } else { newMap = newMap.addPropertyBind((AccessorProperty)property, source); } @@ -326,18 +327,18 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * @return property descriptor */ public final PropertyDescriptor toPropertyDescriptor() { - final GlobalObject global = (GlobalObject) Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); final PropertyDescriptor desc; if (isDataDescriptor()) { if (has(SET) || has(GET)) { - throw typeError((ScriptObject)global, "inconsistent.property.descriptor"); + throw typeError(global, "inconsistent.property.descriptor"); } desc = global.newDataDescriptor(UNDEFINED, false, false, false); } else if (isAccessorDescriptor()) { if (has(VALUE) || has(WRITABLE)) { - throw typeError((ScriptObject)global, "inconsistent.property.descriptor"); + throw typeError(global, "inconsistent.property.descriptor"); } desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false); @@ -356,7 +357,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * * @return property descriptor */ - public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) { + public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) { if (obj instanceof ScriptObject) { return ((ScriptObject)obj).toPropertyDescriptor(); } @@ -375,7 +376,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr public Object getOwnPropertyDescriptor(final String key) { final Property property = getMap().findProperty(key); - final GlobalObject global = (GlobalObject)Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); if (property != null) { final ScriptFunction get = property.getGetterFunction(this); @@ -440,7 +441,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * @return true if property was successfully defined */ public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) { - final ScriptObject global = Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc); final Object current = getOwnPropertyDescriptor(key); final String name = JSType.toString(key); @@ -467,7 +468,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return true; } - if (currentDesc.equals(newDesc)) { + if (newDesc.hasAndEquals(currentDesc)) { // every descriptor field of the new is same as the current return true; } @@ -594,23 +595,16 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } /** - * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in - * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set - * method in such cases. This is because set method uses inherited setters (if any) - * from any object in proto chain such as Array.prototype, Object.prototype. - * This method directly sets a particular element value in the current object. + * Almost like defineOwnProperty(int,Object) for arrays this one does + * not add 'gap' elements (like the array one does). * * @param index key for property * @param value value to define */ - public final void defineOwnProperty(final int index, final Object value) { + public void defineOwnProperty(final int index, final Object value) { assert isValidArrayIndex(index) : "invalid array index"; final long longIndex = ArrayIndex.toLongIndex(index); - if (longIndex >= getArray().length()) { - // make array big enough to hold.. - setArray(getArray().ensure(longIndex)); - } - setArray(getArray().set(index, value, false)); + setValueAtArrayIndex(longIndex, index, value, false); } private void checkIntegerKey(final String key) { @@ -638,7 +632,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final int propFlags = Property.toFlags(pdesc); if (pdesc.type() == PropertyDescriptor.GENERIC) { - final GlobalObject global = (GlobalObject) Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false); dDesc.fillFrom((ScriptObject)pdesc); @@ -873,8 +867,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter); } - notifyPropertyModified(this, oldProperty, newProperty); - return modifyOwnProperty(oldProperty, newProperty); } @@ -981,17 +973,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } /** - * Get the object value of a property - * - * @param find {@link FindProperty} lookup result - * - * @return the value of the property - */ - protected static Object getObjectValue(final FindProperty find) { - return find.getObjectValue(); - } - - /** * Return methodHandle of value function for call. * * @param find data from find property. @@ -1001,7 +982,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * @return value of property as a MethodHandle or null. */ protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) { - return getCallMethodHandle(getObjectValue(find), type, bindName); + return getCallMethodHandle(find.getObjectValue(), type, bindName); } /** @@ -1025,7 +1006,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * @return Value of property. */ public final Object getWithProperty(final Property property) { - return getObjectValue(new FindProperty(this, this, property)); + return new FindProperty(this, this, property).getObjectValue(); } /** @@ -1118,29 +1099,33 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr */ public synchronized final void setProto(final ScriptObject newProto) { final ScriptObject oldProto = proto; - map = map.changeProto(oldProto, newProto); - - if (newProto != null) { - newProto.setIsPrototype(); - } - - proto = newProto; - if (isPrototype()) { - // tell listeners that my __proto__ has been changed - notifyProtoChanged(this, oldProto, newProto); + if (oldProto != newProto) { + proto = newProto; - if (oldProto != null) { - oldProto.removePropertyListener(this); - } - - if (newProto != null) { - newProto.addPropertyListener(this); + // Let current listeners know that the protototype has changed and set our map + final PropertyListeners listeners = getMap().getListeners(); + if (listeners != null) { + listeners.protoChanged(); } + // Replace our current allocator map with one that is associated with the new prototype. + setMap(getMap().changeProto(newProto)); } } /** + * Set the initial __proto__ of this object. This should be used instead of + * {@link #setProto} if it is known that the current property map will not be + * used on a new object with any other parent property map, so we can pass over + * property map invalidation/evolution. + * + * @param initialProto the initial __proto__ to set. + */ + public void setInitialProto(final ScriptObject initialProto) { + this.proto = initialProto; + } + + /** * Set the __proto__ of an object with checks. * @param newProto Prototype to set. */ @@ -1160,7 +1145,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } setProto((ScriptObject)newProto); } else { - final ScriptObject global = Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); final Object newProtoObject = JSType.toScriptObject(global, newProto); if (newProtoObject instanceof ScriptObject) { @@ -1250,11 +1235,11 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * @return the default value */ public Object getDefaultValue(final Class<?> typeHint) { - // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and + // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts // are being executed in a long-running program, we move the code and their associated dynamic call sites // (Global.TO_STRING and Global.VALUE_OF) into per-context code. - return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint); + return Context.getGlobal().getDefaultValue(this, typeHint); } /** @@ -1330,25 +1315,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } /** - * Check if this object is a prototype - * - * @return {@code true} if is prototype - */ - public final boolean isPrototype() { - return (flags & IS_PROTOTYPE) != 0; - } - - /** - * Flag this object as having a prototype. - */ - public final void setIsPrototype() { - if (proto != null && !isPrototype()) { - proto.addPropertyListener(this); - } - flags |= IS_PROTOTYPE; - } - - /** * Check if this object has non-writable length property * * @return {@code true} if 'length' property is non-writable @@ -1712,6 +1678,44 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } /** + * Test whether this object contains in its prototype chain or is itself a with-object. + * @return true if a with-object was found + */ + final boolean hasWithScope() { + if (isScope()) { + for (ScriptObject obj = this; obj != null; obj = obj.getProto()) { + if (obj instanceof WithObject) { + return true; + } + } + } + return false; + } + + /** + * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method + * {@code depth} times. + * @param methodHandle a method handle + * @param depth distance to target prototype + * @return the filtered method handle + */ + static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) { + if (depth == 0) { + return methodHandle; + } + final int listIndex = depth - 1; // We don't need 0-deep walker + MethodHandle filter = listIndex < protoFilters.size() ? protoFilters.get(listIndex) : null; + + if(filter == null) { + filter = addProtoFilter(GETPROTO, depth - 1); + protoFilters.add(null); + protoFilters.set(listIndex, filter); + } + + return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0)))); + } + + /** * Find the appropriate GET method for an invoke dynamic call. * * @param desc the call site descriptor @@ -1722,18 +1726,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr */ protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - if (request.isCallSiteUnstable()) { - return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator)); + if (request.isCallSiteUnstable() || hasWithScope()) { + return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator), isScope() && NashornCallSiteDescriptor.isScope(desc)); } final FindProperty find = findProperty(name, true); MethodHandle methodHandle; if (find == null) { - if (PROTO_PROPERTY_NAME.equals(name)) { - return new GuardedInvocation(GETPROTO, NashornGuards.getScriptObjectGuard()); - } - if ("getProp".equals(operator)) { return noSuchProperty(desc, request); } else if ("getMethod".equals(operator)) { @@ -1748,40 +1748,43 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final Property property = find.getProperty(); methodHandle = find.getGetter(returnType); - // getMap() is fine as we have the prototype switchpoint depending on where the property was found - final MethodHandle guard = NashornGuards.getMapGuard(getMap()); + // Get the appropriate guard for this callsite and property. + final MethodHandle guard = NashornGuards.getGuard(this, property, desc); + final ScriptObject owner = find.getOwner(); if (methodHandle != null) { assert methodHandle.type().returnType().equals(returnType); if (find.isSelf()) { - return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY && - NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard); + return new GuardedInvocation(methodHandle, guard); } - final ScriptObject prototype = find.getOwner(); - - if (!property.hasGetterFunction(prototype)) { - methodHandle = bindTo(methodHandle, prototype); + if (!property.hasGetterFunction(owner)) { + // Add a filter that replaces the self object with the prototype owning the property. + methodHandle = addProtoFilter(methodHandle, find.getProtoChainLength()); } - return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard); + return new GuardedInvocation(methodHandle, guard == null ? null : getProtoSwitchPoint(name, owner), guard); } assert !NashornCallSiteDescriptor.isFastScope(desc); - return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard); + return new GuardedInvocation(Lookup.emptyGetter(returnType), getProtoSwitchPoint(name, owner), guard); } - private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) { - final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod); + private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, + final boolean isMethod, final boolean isScope) { + final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, isScope); final MethodHandle guard = getScriptObjectGuard(desc.getMethodType()); 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 getObjectValue(find); + return find.getObjectValue(); + } + if (isScope) { + throw referenceError("not.defined", key); } return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key); @@ -1824,6 +1827,28 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } /** + * Get a switch point for a property with the given {@code name} that will be invalidated when + * the property definition is changed in this object's prototype chain. Returns {@code null} if + * the property is defined in this object itself. + * + * @param name the property name + * @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) { + if (owner == this || getProto() == null) { + return null; + } + + for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { + ScriptObject parent = obj.getProto(); + parent.getMap().addListener(name, obj.getMap()); + } + + return getMap().getSwitchPoint(name); + } + + /** * Find the appropriate SET method for an invoke dynamic call. * * @param desc the call site descriptor @@ -1833,7 +1858,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr */ protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - if (request.isCallSiteUnstable()) { + if (request.isCallSiteUnstable() || hasWithScope()) { return findMegaMorphicSetMethod(desc, name); } @@ -1863,9 +1888,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return createEmptySetMethod(desc, "property.not.writable", true); } } else { - if (PROTO_PROPERTY_NAME.equals(name)) { - return new GuardedInvocation(SETPROTOCHECK, NashornGuards.getScriptObjectGuard()); - } else if (! isExtensible()) { + if (! isExtensible()) { return createEmptySetMethod(desc, "object.non.extensible", false); } } @@ -1879,8 +1902,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this))); } assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc); - final PropertyMap myMap = getMap(); - return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap)); + return new GuardedInvocation(Lookup.EMPTY_SETTER, getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap())); } @SuppressWarnings("unused") @@ -1888,7 +1910,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final ScriptObject obj = (ScriptObject)self; final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); if (!obj.isExtensible()) { - throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj)); + if (isStrict) { + throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj)); + } } else if (obj.compareAndSetMap(oldMap, newMap)) { setter.invokeExact(self, value); } else { @@ -1953,6 +1977,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } } + @SuppressWarnings("unused") + private static Object globalFilter(final Object object) { + ScriptObject sobj = (ScriptObject) object; + while (sobj != null && !(sobj instanceof Global)) { + sobj = sobj.getProto(); + } + return sobj; + } + private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) { final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class); final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc)); @@ -1998,7 +2031,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return noSuchProperty(desc, request); } - final Object value = getObjectValue(find); + final Object value = find.getObjectValue(); if (! (value instanceof ScriptFunction)) { return createEmptyGetter(desc, name); } @@ -2024,7 +2057,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc); if (find != null) { - final Object value = getObjectValue(find); + final Object value = find.getObjectValue(); ScriptFunction func = null; MethodHandle methodHandle = null; @@ -2038,7 +2071,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr methodHandle = bindTo(methodHandle, UNDEFINED); } return new GuardedInvocation(methodHandle, - find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null, + getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()), getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func)); } } @@ -2049,16 +2082,17 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return createEmptyGetter(desc, name); } + /** * Invoke fall back if a property is not found. * @param name Name of property. * @return Result from call. */ - private Object invokeNoSuchProperty(final String name) { + protected Object invokeNoSuchProperty(final String name) { final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); if (find != null) { - final Object func = getObjectValue(find); + final Object func = find.getObjectValue(); if (func instanceof ScriptFunction) { return ScriptRuntime.apply((ScriptFunction)func, this, name); @@ -2080,7 +2114,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr return invokeNoSuchProperty(name); } - final Object value = getObjectValue(find); + final Object value = find.getObjectValue(); if (! (value instanceof ScriptFunction)) { return UNDEFINED; } @@ -2089,7 +2123,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) { - return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap())); + return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), + getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap())); } private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> { @@ -2170,12 +2205,10 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr if (fieldCount < fieldMaximum) { property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount); - notifyPropertyAdded(this, property); property = addOwnProperty(property); } else { int i = getMap().getSpillLength(); property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i); - notifyPropertyAdded(this, property); property = addOwnProperty(property); i = property.getSlot(); @@ -2621,7 +2654,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final FindProperty find = object.findProperty(key, false, false, this); if (find != null) { - return getObjectValue(find); + return find.getObjectValue(); } } @@ -2639,7 +2672,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr final FindProperty find = findProperty(key, true); if (find != null) { - return getObjectValue(find); + return find.getObjectValue(); } } @@ -2703,9 +2736,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr * @param strict are we in strict mode */ private void doesNotHave(final int index, final Object value, final boolean strict) { - final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); - if (getMap().containsArrayKeys()) { final String key = JSType.toString(longIndex); final FindProperty find = findProperty(key, true); @@ -2716,6 +2747,18 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } } + setValueAtArrayIndex(longIndex, index, value, strict); + } + + /** + * Handle when an array doesn't have a slot - possibly grow and/or convert array. + * + * @param index key as index + * @param value element value + * @param strict are we in strict mode + */ + private void setValueAtArrayIndex(final long longIndex, final int index, final Object value, final boolean strict) { + final long oldLength = getArray().length(); if (longIndex >= oldLength) { if (!isExtensible()) { if (strict) { @@ -2759,7 +2802,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) { FindProperty f = find; - if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) { + if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty) && !isScope()) { + // Setting a property should not modify the property in prototype unless this is a scope object. f = null; } @@ -2779,7 +2823,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); } } else { - spill(key, value); + ScriptObject sobj = this; + // undefined scope properties are set in the global object. + if (isScope()) { + while (sobj != null && !(sobj instanceof Global)) { + sobj = sobj.getProto(); + } + assert sobj != null : "no parent global object in scope"; + } + sobj.spill(key, value); } } @@ -3228,7 +3280,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } final Property prop = find.getProperty(); - notifyPropertyDeleted(this, prop); deleteOwnProperty(prop); return true; diff --git a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java index d9705676..6df047d8 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java @@ -474,7 +474,7 @@ public final class ScriptRuntime { * @return {@link WithObject} that is the new scope */ public static ScriptObject openWith(final ScriptObject scope, final Object expression) { - final ScriptObject global = Context.getGlobalTrusted(); + final Global global = Context.getGlobal(); if (expression == UNDEFINED) { throw typeError(global, "cant.apply.with.to.undefined"); } else if (expression == null) { diff --git a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java index 777254d8..edee3748 100644 --- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java @@ -31,7 +31,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; -import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornGuards; @@ -80,7 +79,7 @@ final class SetMethodCreator { } /** - * This class encapsulates the results of looking up a setter method; it's basically a triple of a method hanle, + * This class encapsulates the results of looking up a setter method; it's basically a triple of a method handle, * a Property object, and flags for invocation. * */ @@ -104,21 +103,9 @@ final class SetMethodCreator { * @return the composed guarded invocation that represents the dynamic setter method for the property. */ GuardedInvocation createGuardedInvocation() { - return new GuardedInvocation(methodHandle, getGuard()); + return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc)); } - private MethodHandle getGuard() { - return needsNoGuard() ? null : NashornGuards.getMapGuard(getMap()); - } - - private boolean needsNoGuard() { - return NashornCallSiteDescriptor.isFastScope(desc) && - (ObjectClassGenerator.OBJECT_FIELDS_ONLY || isPropertyTypeStable()); - } - - private boolean isPropertyTypeStable() { - return property == null || !property.canChangeType(); - } } private SetMethod createSetMethod() { @@ -151,10 +138,9 @@ final class SetMethodCreator { assert methodHandle != null; assert property != null; - final ScriptObject prototype = find.getOwner(); final MethodHandle boundHandle; - if (!property.hasSetterFunction(prototype) && find.isInherited()) { - boundHandle = ScriptObject.bindTo(methodHandle, prototype); + if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) { + boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength()); } else { boundHandle = methodHandle; } @@ -162,13 +148,16 @@ final class SetMethodCreator { } private SetMethod createGlobalPropertySetter() { - final ScriptObject global = Context.getGlobalTrusted(); - return new SetMethod(ScriptObject.bindTo(global.addSpill(getName()), global), null); + final ScriptObject global = Context.getGlobal(); + return new SetMethod(MH.filterArguments(global.addSpill(getName()), 0, ScriptObject.GLOBALFILTER), null); } private SetMethod createNewPropertySetter() { final SetMethod sm = map.getFieldCount() < map.getFieldMaximum() ? createNewFieldSetter() : createNewSpillPropertySetter(); - sobj.notifyPropertyAdded(sobj, sm.property); + final PropertyListeners listeners = map.getListeners(); + if (listeners != null) { + listeners.propertyAdded(sm.property); + } return sm; } diff --git a/src/jdk/nashorn/internal/runtime/Source.java b/src/jdk/nashorn/internal/runtime/Source.java index 0455a86c..f7e890ff 100644 --- a/src/jdk/nashorn/internal/runtime/Source.java +++ b/src/jdk/nashorn/internal/runtime/Source.java @@ -27,27 +27,38 @@ package jdk.nashorn.internal.runtime; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLConnection; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Objects; +import java.util.WeakHashMap; +import jdk.nashorn.api.scripting.URLReader; import jdk.nashorn.internal.parser.Token; /** * Source objects track the origin of JavaScript entities. - * */ public final class Source { + + private static final DebugLogger DEBUG = new DebugLogger("source"); + private static final int BUF_SIZE = 8 * 1024; + private static final Cache CACHE = new Cache(); + /** * Descriptive name of the source as supplied by the user. Used for error * reporting to the user. For example, SyntaxError will use this to print message. @@ -62,49 +73,306 @@ public final class Source { */ private final String base; - /** Cached source content. */ - private final char[] content; - - /** Length of source content. */ - private final int length; + /** Source content */ + private final Data data; /** Cached hash code */ private int hash; - /** Source URL if available */ - private final URL url; + /** Message digest */ + private byte[] digest; + + // Do *not* make this public, ever! Trusts the URL and content. + private Source(final String name, final String base, final Data data) { + this.name = name; + this.base = base; + this.data = data; + } + + private static synchronized Source sourceFor(final String name, final String base, final URLData data) throws IOException { + try { + final Source newSource = new Source(name, base, data); + final Source existingSource = CACHE.get(newSource); + if (existingSource != null) { + // Force any access errors + data.checkPermissionAndClose(); + return existingSource; + } else { + // All sources in cache must be fully loaded + data.load(); + CACHE.put(newSource, newSource); + return newSource; + } + } catch (RuntimeException e) { + final Throwable cause = e.getCause(); + if (cause instanceof IOException) { + throw (IOException) cause; + } + throw e; + } + } + + private static class Cache extends WeakHashMap<Source, WeakReference<Source>> { + public Source get(final Source key) { + final WeakReference<Source> ref = super.get(key); + return ref == null ? null : ref.get(); + } + + public void put(final Source key, final Source value) { + assert !(value.data instanceof RawData); + put(key, new WeakReference<>(value)); + } + } + + // Wrapper to manage lazy loading + private static interface Data { - private static final int BUFSIZE = 8 * 1024; + URL url(); - // Do *not* make this public ever! Trusts the URL and content. So has to be called - // from other public constructors. Note that this can not be some init method as - // we initialize final fields from here. - private Source(final String name, final String base, final char[] content, final URL url) { - this.name = name; - this.base = base; - this.content = content; - this.length = content.length; - this.url = url; + int length(); + + long lastModified(); + + char[] array(); + } + + private static class RawData implements Data { + private final char[] array; + private int hash; + + private RawData(final char[] array) { + this.array = Objects.requireNonNull(array); + } + + private RawData(final String source) { + this.array = Objects.requireNonNull(source).toCharArray(); + } + + private RawData(final Reader reader) throws IOException { + this(readFully(reader)); + } + + @Override + public int hashCode() { + int h = hash; + if (h == 0) { + h = hash = Arrays.hashCode(array); + } + return h; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof RawData) { + return Arrays.equals(array, ((RawData)obj).array); + } + return false; + } + + @Override + public String toString() { + return new String(array()); + } + + @Override + public URL url() { + return null; + } + + @Override + public int length() { + return array.length; + } + + @Override + public long lastModified() { + return 0; + } + + @Override + public char[] array() { + return array; + } + + + } + + private static class URLData implements Data { + private final URL url; + protected final Charset cs; + private int hash; + protected char[] array; + protected int length; + protected long lastModified; + + private URLData(final URL url, final Charset cs) { + this.url = Objects.requireNonNull(url); + this.cs = cs; + } + + @Override + public int hashCode() { + int h = hash; + if (h == 0) { + h = hash = url.hashCode(); + } + return h; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof URLData)) { + return false; + } + + URLData otherData = (URLData) other; + + if (url.equals(otherData.url)) { + // Make sure both have meta data loaded + try { + if (isDeferred()) { + // Data in cache is always loaded, and we only compare to cached data. + assert !otherData.isDeferred(); + loadMeta(); + } else if (otherData.isDeferred()) { + otherData.loadMeta(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + // Compare meta data + return this.length == otherData.length && this.lastModified == otherData.lastModified; + } + return false; + } + + @Override + public String toString() { + return new String(array()); + } + + @Override + public URL url() { + return url; + } + + @Override + public int length() { + return length; + } + + @Override + public long lastModified() { + return lastModified; + } + + @Override + public char[] array() { + assert !isDeferred(); + return array; + } + + boolean isDeferred() { + return array == null; + } + + protected void checkPermissionAndClose() throws IOException { + try (InputStream in = url.openStream()) {} + debug("permission checked for ", url); + } + + protected void load() throws IOException { + if (array == null) { + final URLConnection c = url.openConnection(); + try (InputStream in = c.getInputStream()) { + array = cs == null ? readFully(in) : readFully(in, cs); + length = array.length; + lastModified = c.getLastModified(); + debug("loaded content for ", url); + } + } + } + + protected void loadMeta() throws IOException { + if (length == 0 && lastModified == 0) { + final URLConnection c = url.openConnection(); + length = c.getContentLength(); + lastModified = c.getLastModified(); + debug("loaded metadata for ", url); + } + } + } + + private static class FileData extends URLData { + private final File file; + + private FileData(final File file, final Charset cs) { + super(getURLFromFile(file), cs); + this.file = file; + + } + + @Override + protected void checkPermissionAndClose() throws IOException { + if (!file.canRead()) { + throw new FileNotFoundException(file + " (Permission Denied)"); + } + debug("permission checked for ", file); + } + + @Override + protected void loadMeta() { + if (length == 0 && lastModified == 0) { + length = (int) file.length(); + lastModified = file.lastModified(); + debug("loaded metadata for ", file); + } + } + + @Override + protected void load() throws IOException { + if (array == null) { + array = cs == null ? readFully(file) : readFully(file, cs); + length = array.length; + lastModified = file.lastModified(); + debug("loaded content for ", file); + } + } + } + + private static void debug(final Object... msg) { + DEBUG.info(msg); + } + + private char[] data() { + return data.array(); } /** - * Constructor + * Returns an instance * * @param name source name * @param content contents as char array */ - public Source(final String name, final char[] content) { - this(name, baseName(name, null), content, null); + public static Source sourceFor(final String name, final char[] content) { + return new Source(name, baseName(name), new RawData(content)); } /** - * Constructor + * Returns an instance * * @param name source name * @param content contents as string */ - public Source(final String name, final String content) { - this(name, content.toCharArray()); + public static Source sourceFor(final String name, final String content) { + return new Source(name, baseName(name), new RawData(content)); } /** @@ -115,8 +383,8 @@ public final class Source { * * @throws IOException if source cannot be loaded */ - public Source(final String name, final URL url) throws IOException { - this(name, baseURL(url, null), readFully(url), url); + public static Source sourceFor(final String name, final URL url) throws IOException { + return sourceFor(name, url, null); } /** @@ -128,8 +396,8 @@ public final class Source { * * @throws IOException if source cannot be loaded */ - public Source(final String name, final URL url, final Charset cs) throws IOException { - this(name, baseURL(url, null), readFully(url, cs), url); + public static Source sourceFor(final String name, final URL url, final Charset cs) throws IOException { + return sourceFor(name, baseURL(url), new URLData(url, cs)); } /** @@ -140,8 +408,8 @@ public final class Source { * * @throws IOException if source cannot be loaded */ - public Source(final String name, final File file) throws IOException { - this(name, dirName(file, null), readFully(file), getURLFromFile(file)); + public static Source sourceFor(final String name, final File file) throws IOException { + return sourceFor(name, file, null); } /** @@ -153,8 +421,25 @@ public final class Source { * * @throws IOException if source cannot be loaded */ - public Source(final String name, final File file, final Charset cs) throws IOException { - this(name, dirName(file, null), readFully(file, cs), getURLFromFile(file)); + public static Source sourceFor(final String name, final File file, final Charset cs) throws IOException { + final File absFile = file.getAbsoluteFile(); + return sourceFor(name, dirName(absFile, null), new FileData(file, cs)); + } + + /** + * Returns an instance + * + * @param name source name + * @param reader reader from which source can be loaded + * @throws IOException if source cannot be loaded + */ + public static Source sourceFor(final String name, final Reader reader) throws IOException { + // Extract URL from URLReader to defer loading and reuse cached data if available. + if (reader instanceof URLReader) { + final URLReader urlReader = (URLReader) reader; + return sourceFor(name, urlReader.getURL(), urlReader.getCharset()); + } + return new Source(name, baseName(name), new RawData(reader)); } @Override @@ -162,21 +447,18 @@ public final class Source { if (this == obj) { return true; } - if (!(obj instanceof Source)) { return false; } - - final Source src = (Source)obj; - // Only compare content as a last resort measure - return length == src.length && Objects.equals(url, src.url) && Objects.equals(name, src.name) && Arrays.equals(content, src.content); + final Source other = (Source) obj; + return Objects.equals(name, other.name) && data.equals(other.data); } @Override public int hashCode() { int h = hash; if (h == 0) { - h = hash = Arrays.hashCode(content) ^ Objects.hashCode(name); + h = hash = data.hashCode() ^ Objects.hashCode(name); } return h; } @@ -186,7 +468,7 @@ public final class Source { * @return Source content. */ public String getString() { - return new String(content, 0, length); + return data.toString(); } /** @@ -198,6 +480,14 @@ public final class Source { } /** + * Get the last modified time of this script. + * @return Last modified time. + */ + public long getLastModified() { + return data.lastModified(); + } + + /** * Get the "directory" part of the file or "base" of the URL. * @return base of file or URL. */ @@ -212,7 +502,7 @@ public final class Source { * @return Source content portion. */ public String getString(final int start, final int len) { - return new String(content, start, len); + return new String(data(), start, len); } /** @@ -223,7 +513,7 @@ public final class Source { public String getString(final long token) { final int start = Token.descPosition(token); final int len = Token.descLength(token); - return new String(content, start, len); + return new String(data(), start, len); } /** @@ -233,7 +523,7 @@ public final class Source { * @return URL source or null */ public URL getURL() { - return url; + return data.url(); } /** @@ -242,8 +532,9 @@ public final class Source { * @return Index of first character of line. */ private int findBOLN(final int position) { + final char[] data = data(); for (int i = position - 1; i > 0; i--) { - final char ch = content[i]; + final char ch = data[i]; if (ch == '\n' || ch == '\r') { return i + 1; @@ -259,8 +550,10 @@ public final class Source { * @return Index of last character of line. */ private int findEOLN(final int position) { - for (int i = position; i < length; i++) { - final char ch = content[i]; + final char[] data = data(); + final int length = data.length; + for (int i = position; i < length; i++) { + final char ch = data[i]; if (ch == '\n' || ch == '\r') { return i - 1; @@ -280,11 +573,12 @@ public final class Source { * @return Line number. */ public int getLine(final int position) { + final char[] data = data(); // Line count starts at 1. int line = 1; for (int i = 0; i < position; i++) { - final char ch = content[i]; + final char ch = data[i]; // Works for both \n and \r\n. if (ch == '\n') { line++; @@ -315,7 +609,7 @@ public final class Source { // Find end of this line. final int last = findEOLN(position); - return new String(content, first, last - first + 1); + return new String(data(), first, last - first + 1); } /** @@ -323,7 +617,7 @@ public final class Source { * @return content */ public char[] getContent() { - return content.clone(); + return data().clone(); } /** @@ -331,19 +625,18 @@ public final class Source { * @return length */ public int getLength() { - return length; + return data.length(); } /** * Read all of the source until end of file. Return it as char array * - * @param reader reader opened to source stream + * @param reader reader opened to source stream * @return source as content - * * @throws IOException if source could not be read */ public static char[] readFully(final Reader reader) throws IOException { - final char[] arr = new char[BUFSIZE]; + final char[] arr = new char[BUF_SIZE]; final StringBuilder sb = new StringBuilder(); try { @@ -361,9 +654,8 @@ public final class Source { /** * Read all of the source until end of file. Return it as char array * - * @param file source file + * @param file source file * @return source as content - * * @throws IOException if source could not be read */ public static char[] readFully(final File file) throws IOException { @@ -376,10 +668,9 @@ public final class Source { /** * Read all of the source until end of file. Return it as char array * - * @param file source file + * @param file source file * @param cs Charset used to convert bytes to chars * @return source as content - * * @throws IOException if source could not be read */ public static char[] readFully(final File file, final Charset cs) throws IOException { @@ -388,7 +679,7 @@ public final class Source { } final byte[] buf = Files.readAllBytes(file.toPath()); - return (cs != null)? new String(buf, cs).toCharArray() : byteToCharArray(buf); + return (cs != null) ? new String(buf, cs).toCharArray() : byteToCharArray(buf); } /** @@ -396,7 +687,6 @@ public final class Source { * * @param url URL to read content from * @return source as content - * * @throws IOException if source could not be read */ public static char[] readFully(final URL url) throws IOException { @@ -409,7 +699,6 @@ public final class Source { * @param url URL to read content from * @param cs Charset used to convert bytes to chars * @return source as content - * * @throws IOException if source could not be read */ public static char[] readFully(final URL url, final Charset cs) throws IOException { @@ -417,55 +706,85 @@ public final class Source { } /** + * Get a message digest for this source. + * + * @return a message digest for this source + */ + public synchronized byte[] getDigest() { + if (digest == null) { + final char[] content = data(); + final byte[] bytes = new byte[content.length * 2]; + + for (int i = 0; i < content.length; i++) { + bytes[i * 2] = (byte) (content[i] & 0x00ff); + bytes[i * 2 + 1] = (byte) ((content[i] & 0xff00) >> 8); + } + + try { + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + if (name != null) { + md.update(name.getBytes(StandardCharsets.UTF_8)); + } + if (base != null) { + md.update(base.getBytes(StandardCharsets.UTF_8)); + } + if (getURL() != null) { + md.update(getURL().toString().getBytes(StandardCharsets.UTF_8)); + } + digest = md.digest(bytes); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + return digest; + } + + /** * Get the base url. This is currently used for testing only * @param url a URL * @return base URL for url */ public static String baseURL(final URL url) { - return baseURL(url, null); - } - - private static String baseURL(final URL url, final String defaultValue) { if (url.getProtocol().equals("file")) { try { final Path path = Paths.get(url.toURI()); final Path parent = path.getParent(); - return (parent != null) ? (parent + File.separator) : defaultValue; + return (parent != null) ? (parent + File.separator) : null; } catch (final SecurityException | URISyntaxException | IOError e) { - return defaultValue; + return null; } } // FIXME: is there a better way to find 'base' URL of a given URL? String path = url.getPath(); if (path.isEmpty()) { - return defaultValue; + return null; } path = path.substring(0, path.lastIndexOf('/') + 1); final int port = url.getPort(); try { return new URL(url.getProtocol(), url.getHost(), port, path).toString(); } catch (final MalformedURLException e) { - return defaultValue; + return null; } } - private static String dirName(final File file, final String defaultValue) { + private static String dirName(final File file, final String DEFAULT_BASE_NAME) { final String res = file.getParent(); - return (res != null)? (res + File.separator) : defaultValue; + return (res != null) ? (res + File.separator) : DEFAULT_BASE_NAME; } // fake directory like name - private static String baseName(final String name, final String defaultValue) { + private static String baseName(final String name) { int idx = name.lastIndexOf('/'); if (idx == -1) { idx = name.lastIndexOf('\\'); } - return (idx != -1)? name.substring(0, idx + 1) : defaultValue; + return (idx != -1) ? name.substring(0, idx + 1) : null; } private static char[] readFully(final InputStream is, final Charset cs) throws IOException { - return (cs != null)? new String(readBytes(is), cs).toCharArray() : readFully(is); + return (cs != null) ? new String(readBytes(is), cs).toCharArray() : readFully(is); } private static char[] readFully(final InputStream is) throws IOException { @@ -476,19 +795,19 @@ public final class Source { Charset cs = StandardCharsets.UTF_8; int start = 0; // BOM detection. - if (bytes.length > 1 && bytes[0] == (byte)0xFE && bytes[1] == (byte)0xFF) { + if (bytes.length > 1 && bytes[0] == (byte) 0xFE && bytes[1] == (byte) 0xFF) { start = 2; cs = StandardCharsets.UTF_16BE; - } else if (bytes.length > 1 && bytes[0] == (byte)0xFF && bytes[1] == (byte)0xFE) { + } else if (bytes.length > 1 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE) { start = 2; cs = StandardCharsets.UTF_16LE; - } else if (bytes.length > 2 && bytes[0] == (byte)0xEF && bytes[1] == (byte)0xBB && bytes[2] == (byte)0xBF) { + } else if (bytes.length > 2 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF) { start = 3; cs = StandardCharsets.UTF_8; - } else if (bytes.length > 3 && bytes[0] == (byte)0xFF && bytes[1] == (byte)0xFE && bytes[2] == 0 && bytes[3] == 0) { + } else if (bytes.length > 3 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE && bytes[2] == 0 && bytes[3] == 0) { start = 4; cs = Charset.forName("UTF-32LE"); - } else if (bytes.length > 3 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == (byte)0xFE && bytes[3] == (byte)0xFF) { + } else if (bytes.length > 3 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == (byte) 0xFE && bytes[3] == (byte) 0xFF) { start = 4; cs = Charset.forName("UTF-32BE"); } @@ -497,7 +816,7 @@ public final class Source { } static byte[] readBytes(final InputStream is) throws IOException { - final byte[] arr = new byte[BUFSIZE]; + final byte[] arr = new byte[BUF_SIZE]; try { try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) { int numBytes; diff --git a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java index 931d712b..5b21f6ee 100644 --- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java +++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java @@ -34,6 +34,7 @@ import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.linker.Bootstrap; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; +import jdk.nashorn.internal.objects.Global; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; @@ -73,7 +74,7 @@ public final class UserAccessorProperty extends Property { private static MethodHandle getINVOKE_UA_GETTER() { - return ((GlobalObject)Context.getGlobal()).getDynamicInvoker(INVOKE_UA_GETTER, + return Context.getGlobal().getDynamicInvoker(INVOKE_UA_GETTER, new Callable<MethodHandle>() { @Override public MethodHandle call() { @@ -86,7 +87,7 @@ public final class UserAccessorProperty extends Property { /** Dynamic invoker for setter */ private static Object INVOKE_UA_SETTER = new Object(); private static MethodHandle getINVOKE_UA_SETTER() { - return ((GlobalObject)Context.getGlobal()).getDynamicInvoker(INVOKE_UA_SETTER, + return Context.getGlobal().getDynamicInvoker(INVOKE_UA_SETTER, new Callable<MethodHandle>() { @Override public MethodHandle call() { @@ -186,6 +187,11 @@ public final class UserAccessorProperty extends Property { } @Override + void initMethodHandles(final Class<?> structure) { + throw new UnsupportedOperationException(); + } + + @Override public ScriptFunction getGetterFunction(final ScriptObject obj) { final Object value = obj.getSpill(getterSlot); return (value instanceof ScriptFunction) ? (ScriptFunction) value : null; diff --git a/src/jdk/nashorn/internal/runtime/WithObject.java b/src/jdk/nashorn/internal/runtime/WithObject.java index 6e03dc60..dc48d727 100644 --- a/src/jdk/nashorn/internal/runtime/WithObject.java +++ b/src/jdk/nashorn/internal/runtime/WithObject.java @@ -36,6 +36,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; +import jdk.nashorn.internal.runtime.linker.NashornGuards; /** * This class supports the handling of scope in a with body. @@ -87,6 +88,11 @@ public final class WithObject extends ScriptObject implements Scope { @Override public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) { + if (request.isCallSiteUnstable()) { + // Fall back to megamorphic invocation which performs a complete lookup each time without further relinking. + return super.lookup(desc, request); + } + // With scopes can never be observed outside of Nashorn code, so all call sites that can address it will of // necessity have a Nashorn descriptor - it is safe to cast. final NashornCallSiteDescriptor ndesc = (NashornCallSiteDescriptor)desc; @@ -123,7 +129,7 @@ public final class WithObject extends ScriptObject implements Scope { } if (find != null) { - return fixScopeCallSite(scope.lookup(desc, request), name); + return fixScopeCallSite(scope.lookup(desc, request), name, find.getOwner()); } // the property is not found - now check for @@ -175,7 +181,7 @@ public final class WithObject extends ScriptObject implements Scope { link = scope.lookup(desc, request); if (link != null) { - return fixScopeCallSite(link, name); + return fixScopeCallSite(link, name, null); } return null; @@ -252,13 +258,10 @@ public final class WithObject extends ScriptObject implements Scope { filterGuard(link, WITHEXPRESSIONFILTER)); } - private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name) { + private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name, final ScriptObject owner) { final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER); return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER), - MH.guardWithTest( - expressionGuard(name), - filterGuard(newLink, WITHSCOPEFILTER), - MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class))); + NashornGuards.combineGuards(expressionGuard(name, owner), filterGuard(newLink, WITHSCOPEFILTER))); } private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) { @@ -267,7 +270,7 @@ public final class WithObject extends ScriptObject implements Scope { } private static MethodHandle filter(final MethodHandle mh, final MethodHandle filter) { - return MH.filterArguments(mh, 0, filter); + return MH.filterArguments(mh, 0, filter.asType(filter.type().changeReturnType(mh.type().parameterType(0)))); } /** @@ -288,9 +291,9 @@ public final class WithObject extends ScriptObject implements Scope { return fn.makeBoundFunction(withFilterExpression(receiver), new Object[0]); } - private MethodHandle expressionGuard(final String name) { + private MethodHandle expressionGuard(final String name, final ScriptObject owner) { final PropertyMap map = expression.getMap(); - final SwitchPoint sp = map.getProtoGetSwitchPoint(expression.getProto(), name); + final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner); return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp); } diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java index 5c0c0476..58f32968 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java @@ -26,7 +26,8 @@ package jdk.nashorn.internal.runtime.arrays; import java.lang.invoke.MethodHandle; -import jdk.nashorn.internal.runtime.GlobalObject; +import java.nio.ByteBuffer; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.PropertyDescriptor; @@ -144,6 +145,16 @@ public abstract class ArrayData { } /** + * Allocate an ArrayData wrapping a given nio ByteBuffer + * + * @param buf the nio ByteBuffer to wrap + * @return the ArrayData + */ + public static ArrayData allocate(final ByteBuffer buf) { + return new ByteBufferArrayData((ByteBuffer)buf); + } + + /** * Apply a freeze filter to an ArrayData. * * @param underlying the underlying ArrayData to wrap in the freeze filter @@ -388,7 +399,7 @@ public abstract class ArrayData { * * @return property descriptor for element */ - public PropertyDescriptor getDescriptor(final GlobalObject global, final int index) { + public PropertyDescriptor getDescriptor(final Global global, final int index) { return global.newDataDescriptor(getObject(index), true, true, true); } diff --git a/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java new file mode 100644 index 00000000..33c8679f --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 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.arrays; + +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; + +import java.nio.ByteBuffer; +import jdk.nashorn.internal.objects.Global; +import jdk.nashorn.internal.runtime.PropertyDescriptor; +import jdk.nashorn.internal.runtime.ScriptRuntime; + +/** + * Implementation of {@link ArrayData} that wraps a nio ByteBuffer + */ +final class ByteBufferArrayData extends ArrayData { + private final ByteBuffer buf; + + ByteBufferArrayData(final int length) { + super(length); + this.buf = ByteBuffer.allocateDirect(length); + } + + /** + * Constructor + * + * @param buf ByteBuffer to create array data with. + */ + ByteBufferArrayData(final ByteBuffer buf) { + super(buf.capacity()); + this.buf = buf; + } + + /** + * Returns property descriptor for element at a given index + * + * @param global the global object + * @param index the index + * + * @return property descriptor for element + */ + @Override + public PropertyDescriptor getDescriptor(final Global global, final int index) { + // make the index properties not configurable + return global.newDataDescriptor(getObject(index), false, true, true); + } + + @Override + public ArrayData copy() { + throw unsupported("copy"); + } + + @Override + public Object[] asObjectArray() { + throw unsupported("asObjectArray"); + } + + @Override + public void setLength(final long length) { + throw new UnsupportedOperationException("setLength"); + } + + @Override + public void shiftLeft(int by) { + throw unsupported("shiftLeft"); + } + + @Override + public ArrayData shiftRight(int by) { + throw unsupported("shiftRight"); + } + + @Override + public ArrayData ensure(long safeIndex) { + if (safeIndex < buf.capacity()) { + return this; + } + + throw unsupported("ensure"); + } + + @Override + public ArrayData shrink(long newLength) { + throw unsupported("shrink"); + } + + @Override + public ArrayData set(int index, Object value, boolean strict) { + if (value instanceof Number) { + buf.put(index, ((Number)value).byteValue()); + return this; + } + + throw typeError("not.a.number", ScriptRuntime.safeToString(value)); + } + + @Override + public ArrayData set(int index, int value, boolean strict) { + buf.put(index, (byte)value); + return this; + } + + @Override + public ArrayData set(int index, long value, boolean strict) { + buf.put(index, (byte)value); + return this; + } + + @Override + public ArrayData set(int index, double value, boolean strict) { + buf.put(index, (byte)value); + return this; + } + + @Override + public int getInt(int index) { + return 0x0ff & buf.get(index); + } + + @Override + public long getLong(int index) { + return 0x0ff & buf.get(index); + } + + @Override + public double getDouble(int index) { + return 0x0ff & buf.get(index); + } + + @Override + public Object getObject(int index) { + return (int)(0x0ff & buf.get(index)); + } + + @Override + public boolean has(int index) { + return index > -1 && index < buf.capacity(); + } + + @Override + public boolean canDelete(final int index, final boolean strict) { + return false; + } + + @Override + public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { + return false; + } + + @Override + public ArrayData delete(int index) { + throw unsupported("delete"); + } + + @Override + public ArrayData delete(long fromIndex, long toIndex) { + throw unsupported("delete"); + } + + @Override + public ArrayData push(final boolean strict, final Object... items) { + throw unsupported("push"); + } + + @Override + public Object pop() { + throw unsupported("pop"); + } + + @Override + public ArrayData slice(long from, long to) { + throw unsupported("slice"); + } + + @Override + public ArrayData convert(final Class<?> type) { + throw unsupported("convert"); + } + + private UnsupportedOperationException unsupported(final String method) { + return new UnsupportedOperationException(method); + } +} diff --git a/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java index a1f772b8..e15541db 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java +++ b/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java @@ -25,9 +25,9 @@ package jdk.nashorn.internal.runtime.arrays; +import jdk.nashorn.internal.objects.Global; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; -import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.PropertyDescriptor; /** @@ -44,7 +44,7 @@ final class FrozenArrayFilter extends SealedArrayFilter { } @Override - public PropertyDescriptor getDescriptor(final GlobalObject global, final int index) { + public PropertyDescriptor getDescriptor(final Global global, final int index) { return global.newDataDescriptor(getObject(index), false, true, false); } diff --git a/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java index 6f77ddc0..bc6de9f0 100644 --- a/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java +++ b/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java @@ -25,9 +25,9 @@ package jdk.nashorn.internal.runtime.arrays; +import jdk.nashorn.internal.objects.Global; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; -import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.PropertyDescriptor; /** @@ -62,7 +62,7 @@ class SealedArrayFilter extends ArrayFilter { } @Override - public PropertyDescriptor getDescriptor(final GlobalObject global, final int index) { + public PropertyDescriptor getDescriptor(final Global global, final int index) { return global.newDataDescriptor(getObject(index), false, true, true); } } diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java index 14d0e14e..6b3acf63 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java @@ -131,7 +131,7 @@ import sun.reflect.CallerSensitive; * implemented securely. */ final class JavaAdapterBytecodeGenerator { - static final Type OBJECT_TYPE = Type.getType(Object.class); + static final Type OBJECT_TYPE = Type.getType(Object.class); static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName(); @@ -139,6 +139,7 @@ final class JavaAdapterBytecodeGenerator { static final String GLOBAL_FIELD_NAME = "global"; + // "global" is declared as Object instead of Global - avoid static references to internal Nashorn classes when possible. static final String GLOBAL_TYPE_DESCRIPTOR = OBJECT_TYPE.getDescriptor(); static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE); @@ -318,7 +319,7 @@ final class JavaAdapterBytecodeGenerator { final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT, Type.getMethodDescriptor(Type.VOID_TYPE), null, null)); - mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR, false); final Label initGlobal; if(samName != null) { // If the class is a SAM, allow having a ScriptFunction passed as class overrides @@ -334,7 +335,7 @@ final class JavaAdapterBytecodeGenerator { if(mi.getName().equals(samName)) { mv.dup(); mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString())); - mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_FUNCTION_DESCRIPTOR); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_FUNCTION_DESCRIPTOR, false); } else { mv.visitInsn(ACONST_NULL); } @@ -351,7 +352,7 @@ final class JavaAdapterBytecodeGenerator { mv.dup(); mv.aconst(mi.getName()); mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString())); - mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR, false); mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); } @@ -368,7 +369,7 @@ final class JavaAdapterBytecodeGenerator { private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) { invokeGetGlobal(mv); mv.dup(); - mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context + mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR, false); // check against null Context mv.pop(); } @@ -425,7 +426,7 @@ final class JavaAdapterBytecodeGenerator { mv.load(offset, argType); offset += argType.getSize(); } - mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor()); + mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false); endInitMethod(mv); } @@ -478,7 +479,7 @@ final class JavaAdapterBytecodeGenerator { mv.load(offset, argType); offset += argType.getSize(); } - mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor()); + mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false); // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method. final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR; @@ -497,7 +498,7 @@ final class JavaAdapterBytecodeGenerator { mv.aconst(mi.getName()); } mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString())); - mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor, false); } mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); } @@ -521,11 +522,11 @@ final class JavaAdapterBytecodeGenerator { } private static void invokeGetGlobal(final InstructionAdapter mv) { - mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR, false); } private static void invokeSetGlobal(final InstructionAdapter mv) { - mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, false); } /** @@ -638,11 +639,11 @@ final class JavaAdapterBytecodeGenerator { // If the super method is abstract, throw an exception mv.anew(UNSUPPORTED_OPERATION_TYPE); mv.dup(); - mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR); + mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR, false); mv.athrow(); } else { // If the super method is not abstract, delegate to it. - emitSuperCall(mv, name, methodDesc); + emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); } mv.visitLabel(handleDefined); @@ -671,7 +672,7 @@ final class JavaAdapterBytecodeGenerator { // stack: [creatingGlobal, creatingGlobal, handle] // Emit code for switching to the creating global - // ScriptObject currentGlobal = Context.getGlobal(); + // Global currentGlobal = Context.getGlobal(); invokeGetGlobal(mv); mv.dup(); @@ -712,7 +713,7 @@ final class JavaAdapterBytecodeGenerator { // Invoke the target method handle final Label tryBlockStart = new Label(); mv.visitLabel(tryBlockStart); - mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString()); + mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString(), false); final Label tryBlockEnd = new Label(); mv.visitLabel(tryBlockEnd); emitFinally(mv, currentGlobalVar, globalsDifferVar); @@ -728,7 +729,7 @@ final class JavaAdapterBytecodeGenerator { mv.anew(RUNTIME_EXCEPTION_TYPE); mv.dupX1(); mv.swap(); - mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE)); + mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE), false); // Fall through to rethrow handler } else { throwableHandler = null; @@ -814,12 +815,12 @@ final class JavaAdapterBytecodeGenerator { SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes()))); mv.visitCode(); - emitSuperCall(mv, name, methodDesc); + emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); endMethod(mv); } - private void emitSuperCall(final InstructionAdapter mv, final String name, final String methodDesc) { + private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) { mv.visitVarInsn(ALOAD, 0); int nextParam = 1; final Type methodType = Type.getMethodType(methodDesc); @@ -827,7 +828,13 @@ final class JavaAdapterBytecodeGenerator { mv.load(nextParam, t); nextParam += t.getSize(); } - mv.invokespecial(superClassName, name, methodDesc); + + // default method - non-abstract, interface method + if (Modifier.isInterface(owner.getModifiers())) { + mv.invokespecial(Type.getInternalName(owner), name, methodDesc, false); + } else { + mv.invokespecial(superClassName, name, methodDesc, false); + } mv.areturn(methodType.getReturnType()); } diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java index c3acb7c5..1b5345b0 100644 --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java @@ -48,7 +48,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.support.LinkRequestImpl; -import jdk.nashorn.internal.objects.NativeJava; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.ScriptFunction; @@ -68,8 +67,8 @@ import jdk.nashorn.internal.runtime.ScriptObject; * generate the adapter class itself; see its documentation for details about the generated class. * </p><p> * You normally don't use this class directly, but rather either create adapters from script using - * {@link NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see - * {@link NativeJava#type(Object, Object)}), or implicitly when passing script functions to Java methods expecting SAM + * {@link jdk.nashorn.internal.objects.NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see + * {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)}), or implicitly when passing script functions to Java methods expecting SAM * types. * </p> */ @@ -337,6 +336,7 @@ public final class JavaAdapterFactory { private static ProtectionDomain createMinimalPermissionDomain() { // Generated classes need to have at least the permission to access Nashorn runtime and runtime.linker packages. final Permissions permissions = new Permissions(); + permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.objects")); permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime")); permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime.linker")); return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions); diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java b/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java index a8e79184..ca3e10c4 100644 --- a/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java +++ b/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java @@ -29,6 +29,11 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.ref.WeakReference; +import jdk.internal.dynalink.CallSiteDescriptor; +import jdk.nashorn.internal.codegen.ObjectClassGenerator; +import jdk.nashorn.internal.objects.Global; +import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -37,10 +42,11 @@ import jdk.nashorn.internal.runtime.ScriptObject; * Constructor of method handles used to guard call sites. */ public final class NashornGuards { - private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class); - private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class); - private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class); - private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class); + private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class); + private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class); + private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class); + private static final MethodHandle SAME_OBJECT = findOwnMH("sameObject", boolean.class, Object.class, WeakReference.class); + private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class); // don't create me! private NashornGuards() { @@ -75,6 +81,55 @@ public final class NashornGuards { } /** + * Determine whether the given callsite needs a guard. + * @param property the property, or null + * @param desc the callsite descriptor + * @return true if a guard should be used for this callsite + */ + static boolean needsGuard(final Property property, final CallSiteDescriptor desc) { + return property == null || property.isConfigurable() + || property.isBound() || !ObjectClassGenerator.OBJECT_FIELDS_ONLY + || !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType(); + } + + /** + * Get the guard for a property access. This returns an identity guard for non-configurable global properties + * and a map guard for everything else. + * + * @param sobj the first object in the prototype chain + * @param property the property + * @param desc the callsite descriptor + * @return method handle for guard + */ + public static MethodHandle getGuard(final ScriptObject sobj, final Property property, final CallSiteDescriptor desc) { + if (!needsGuard(property, desc)) { + return null; + } + if (NashornCallSiteDescriptor.isScope(desc)) { + if (property != null && property.isBound()) { + // This is a declared top level variables in main script or eval, use identity guard. + return getIdentityGuard(sobj); + } + if (!(sobj instanceof Global) && (property == null || property.isConfigurable())) { + // Undeclared variables in nested evals need stronger guards + return combineGuards(getIdentityGuard(sobj), getMapGuard(sobj.getMap())); + } + } + return getMapGuard(sobj.getMap()); + } + + + /** + * Get a guard that checks referential identity of the current object. + * + * @param sobj the self object + * @return true if same self object instance + */ + public static MethodHandle getIdentityGuard(final ScriptObject sobj) { + return MH.insertArguments(SAME_OBJECT, 1, new WeakReference<>(sobj)); + } + + /** * Get a guard that checks if in item is an instance of either of two classes. * * @param class1 the first class @@ -85,6 +140,17 @@ public final class NashornGuards { return MH.insertArguments(IS_INSTANCEOF_2, 1, class1, class2); } + /** + * Combine two method handles of type {@code (Object)boolean} using logical AND. + * + * @param guard1 the first guard + * @param guard2 the second guard, only invoked if guard1 returns true + * @return true if both guard1 and guard2 returned true + */ + public static MethodHandle combineGuards(final MethodHandle guard1, final MethodHandle guard2) { + return MH.guardWithTest(guard1, guard2, MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class)); + } + @SuppressWarnings("unused") private static boolean isScriptObject(final Object self) { return self instanceof ScriptObject; @@ -101,6 +167,11 @@ public final class NashornGuards { } @SuppressWarnings("unused") + private static boolean sameObject(final Object self, final WeakReference<ScriptObject> ref) { + return self == ref.get(); + } + + @SuppressWarnings("unused") private static boolean isInstanceOf2(final Object self, final Class<?> class1, final Class<?> class2) { return class1.isInstance(self) || class2.isInstance(self); } diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java index 5cc1cd2c..22135bc1 100644 --- a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java @@ -37,9 +37,9 @@ import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.internal.dynalink.support.TypeUtilities; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.GlobalObject; /** * Internal linker for String, Boolean, and Number objects, only ever used by Nashorn engine and not exposed to other @@ -62,7 +62,7 @@ final class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, Gu final LinkRequest request = origRequest.withoutRuntimeContext(); // Nashorn has no runtime context final Object self = request.getReceiver(); - final GlobalObject global = (GlobalObject) Context.getGlobal(); + final Global global = Context.getGlobal(); final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor) request.getCallSiteDescriptor(); return Bootstrap.asType(global.primitiveLookup(request, self), linkerServices, desc); diff --git a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java index c9b5eaa3..7665be7f 100644 --- a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java +++ b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java @@ -35,6 +35,7 @@ import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.internal.dynalink.support.Guards; import jdk.nashorn.internal.lookup.Lookup; +import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.ScriptObject; /** @@ -61,8 +62,9 @@ public final class PrimitiveLookup { * type {@code receiverClass}. */ public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Class<?> receiverClass, - final ScriptObject wrappedReceiver, final MethodHandle wrapFilter) { - return lookupPrimitive(request, Guards.getInstanceOfGuard(receiverClass), wrappedReceiver, wrapFilter); + final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, + final MethodHandle protoFilter) { + return lookupPrimitive(request, Guards.getInstanceOfGuard(receiverClass), wrappedReceiver, wrapFilter, protoFilter); } /** @@ -79,7 +81,8 @@ public final class PrimitiveLookup { * type (that is implied by both {@code guard} and {@code wrappedReceiver}). */ public static GuardedInvocation lookupPrimitive(final LinkRequest request, final MethodHandle guard, - final ScriptObject wrappedReceiver, final MethodHandle wrapFilter) { + final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, + final MethodHandle protoFilter) { final CallSiteDescriptor desc = request.getCallSiteDescriptor(); final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); if ("setProp".equals(operator) || "setElem".equals(operator)) { @@ -93,9 +96,23 @@ public final class PrimitiveLookup { if(desc.getNameTokenCount() > 2) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - if(wrappedReceiver.findProperty(name, true) == null) { + final FindProperty find = wrappedReceiver.findProperty(name, true); + if(find == null) { // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. return null; + } else if (find.isInherited() && !find.getProperty().hasGetterFunction(find.getOwner())) { + // If property is found in the prototype object bind the method handle directly to + // the proto filter instead of going through wrapper instantiation below. + final ScriptObject proto = wrappedReceiver.getProto(); + final GuardedInvocation link = proto.lookup(desc, request); + + if (link != null) { + final MethodHandle invocation = link.getInvocation(); + final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class)); + final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter); + final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter); + return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard)); + } } } final GuardedInvocation link = wrappedReceiver.lookup(desc, request); diff --git a/src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java b/src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java index 5997ec54..66720c23 100644 --- a/src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java @@ -115,7 +115,8 @@ final class ReflectionCheckLinker implements TypeBasedGuardingDynamicLinker{ if ((self instanceof Class) && Modifier.isPublic(((Class<?>)self).getModifiers())) { final CallSiteDescriptor desc = requestWithoutContext.getCallSiteDescriptor(); if(CallSiteDescriptorFactory.tokenizeOperators(desc).contains("getProp")) { - if ("static".equals(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND))) { + if (desc.getNameTokenCount() > CallSiteDescriptor.NAME_OPERAND && + "static".equals(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND))) { if (Context.isAccessibleClass((Class<?>)self) && !isReflectionClass((Class<?>)self)) { // If "getProp:static" passes access checks, allow access. diff --git a/src/jdk/nashorn/internal/runtime/regexp/JdkRegExp.java b/src/jdk/nashorn/internal/runtime/regexp/JdkRegExp.java index 9250331d..f5158021 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/JdkRegExp.java +++ b/src/jdk/nashorn/internal/runtime/regexp/JdkRegExp.java @@ -46,9 +46,6 @@ public class JdkRegExp extends RegExp { /** Java regexp pattern to use for match. We compile to one of these */ private Pattern pattern; - /** The matcher */ - private RegExpMatcher matcher; - /** * Construct a Regular expression from the given {@code source} and {@code flags} strings. * @@ -95,14 +92,7 @@ public class JdkRegExp extends RegExp { return null; // never matches or similar, e.g. a[] } - RegExpMatcher currentMatcher = this.matcher; - - if (currentMatcher == null || matcher.getInput() != str) { - currentMatcher = new DefaultMatcher(str); - this.matcher = currentMatcher; - } - - return currentMatcher; + return new DefaultMatcher(str); } class DefaultMatcher implements RegExpMatcher { diff --git a/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java b/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java index 5ff98ac5..47dbd7c4 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java +++ b/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java @@ -44,9 +44,6 @@ public class JoniRegExp extends RegExp { /** Compiled Joni Regex */ private Regex regex; - /** Matcher */ - private RegExpMatcher matcher; - /** * Construct a Regular expression from the given {@code pattern} and {@code flags} strings. * @@ -95,14 +92,7 @@ public class JoniRegExp extends RegExp { return null; } - RegExpMatcher currentMatcher = this.matcher; - - if (currentMatcher == null || input != currentMatcher.getInput()) { - currentMatcher = new JoniMatcher(input); - this.matcher = currentMatcher; - } - - return currentMatcher; + return new JoniMatcher(input); } /** diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java b/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java index 360c11eb..91117d1d 100644 --- a/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java +++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java @@ -131,12 +131,13 @@ public final class Regex implements RegexState { this.warnings = null; } - public void compile() { + public synchronized MatcherFactory compile() { if (factory == null && analyser != null) { - Compiler compiler = new ArrayCompiler(analyser); + new ArrayCompiler(analyser).compile(); analyser = null; // only do this once - compiler.compile(); } + assert factory != null; + return factory; } public Matcher matcher(char[] chars) { @@ -144,8 +145,11 @@ public final class Regex implements RegexState { } public Matcher matcher(char[] chars, int p, int end) { - compile(); - return factory.create(this, chars, p, end); + MatcherFactory matcherFactory = factory; + if (matcherFactory == null) { + matcherFactory = compile(); + } + return matcherFactory.create(this, chars, p, end); } public WarnCallback getWarnings() { diff --git a/src/jdk/nashorn/internal/runtime/resources/Messages.properties b/src/jdk/nashorn/internal/runtime/resources/Messages.properties index 95993c9f..47248fce 100644 --- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties @@ -78,6 +78,8 @@ type.error.not.a.string={0} is not a String type.error.not.a.function={0} 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.bytebuffer={0} is not a java.nio.ByteBuffer +type.error.not.an.arraybuffer.in.dataview=First arg to DataView constructor must be an ArrayBuffer # operations not permitted on undefined type.error.cant.call.undefined=Cannot call undefined @@ -136,6 +138,9 @@ type.error.no.method.matches.args=Can not invoke method {0} with the passed argu type.error.method.not.constructor=Java method {0} can't 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}. + +range.error.dataview.constructor.offset=Wrong offset or length in DataView constructor +range.error.dataview.offset=Offset is outside the bounds of the DataView range.error.inappropriate.array.length=inappropriate array length: {0} range.error.inappropriate.array.buffer.length=inappropriate array buffer length: {0} range.error.invalid.fraction.digits=fractionDigits argument to {0} must be in [0, 20] diff --git a/src/jdk/nashorn/internal/runtime/resources/Options.properties b/src/jdk/nashorn/internal/runtime/resources/Options.properties index 27262111..9262a401 100644 --- a/src/jdk/nashorn/internal/runtime/resources/Options.properties +++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties @@ -102,6 +102,13 @@ nashorn.option.compile.only = { \ type=Boolean \ } +nashorn.option.const.as.var = { \ + name="--const-as-var", \ + is_undocumented=true, \ + desc="Replace 'const' with 'var'.", \ + type=Boolean \ +} + nashorn.option.d = { \ name="--dump-debug-dir", \ short_name="-d", \ @@ -230,6 +237,14 @@ nashorn.option.parse.only = { \ desc="Parse without compiling." \ } +nashorn.option.persistent.code.cache = { \ + name="--persistent-code-cache", \ + short_name="-pcc", \ + desc="Enable disk cache for compiled scripts.", \ + is_undocumented=true, \ + default=false \ +} + nashorn.option.profile.callsites = { \ name="--profile-callsites", \ short_name="-pcs", \ diff --git a/src/jdk/nashorn/internal/scripts/JO.java b/src/jdk/nashorn/internal/scripts/JO.java index f2f00062..d6173918 100644 --- a/src/jdk/nashorn/internal/scripts/JO.java +++ b/src/jdk/nashorn/internal/scripts/JO.java @@ -33,7 +33,7 @@ import jdk.nashorn.internal.runtime.ScriptObject; */ public class JO extends ScriptObject { - private static final PropertyMap map$ = PropertyMap.newMap().setIsShared(); + private static final PropertyMap map$ = PropertyMap.newMap(); /** * Returns the initial property map to be used. diff --git a/src/jdk/nashorn/tools/Shell.java b/src/jdk/nashorn/tools/Shell.java index ad19484c..33e0afd7 100644 --- a/src/jdk/nashorn/tools/Shell.java +++ b/src/jdk/nashorn/tools/Shell.java @@ -25,6 +25,8 @@ package jdk.nashorn.tools; +import static jdk.nashorn.internal.runtime.Source.sourceFor; + import java.io.BufferedReader; import java.io.File; import java.io.FileReader; @@ -42,6 +44,7 @@ import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.parser.Parser; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; @@ -49,7 +52,6 @@ import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.options.Options; @@ -148,7 +150,7 @@ public class Shell { return COMMANDLINE_ERROR; } - final ScriptObject global = context.createGlobal(); + final Global global = context.createGlobal(); final ScriptEnvironment env = context.getEnv(); final List<String> files = env.getFiles(); if (files.isEmpty()) { @@ -231,8 +233,8 @@ public class Shell { * @return error code * @throws IOException when any script file read results in I/O error */ - private static int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException { - final ScriptObject oldGlobal = Context.getGlobal(); + private static int compileScripts(final Context context, final Global global, final List<String> files) throws IOException { + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); final ScriptEnvironment env = context.getEnv(); try { @@ -243,7 +245,7 @@ public class Shell { // For each file on the command line. for (final String fileName : files) { - final FunctionNode functionNode = new Parser(env, new Source(fileName, new File(fileName)), errors).parse(); + final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors).parse(); if (errors.getNumberOfErrors() != 0) { return COMPILATION_ERROR; @@ -281,8 +283,8 @@ public class Shell { * @return error code * @throws IOException when any script file read results in I/O error */ - private int runScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException { - final ScriptObject oldGlobal = Context.getGlobal(); + private int runScripts(final Context context, final Global global, final List<String> files) throws IOException { + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { if (globalChanged) { @@ -301,7 +303,7 @@ public class Shell { } final File file = new File(fileName); - final ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global); + final ScriptFunction script = context.compileScript(sourceFor(fileName, file), global); if (script == null || errors.getNumberOfErrors() != 0) { return COMPILATION_ERROR; } @@ -339,8 +341,8 @@ public class Shell { * @return error code * @throws IOException when any script file read results in I/O error */ - private static int runFXScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException { - final ScriptObject oldGlobal = Context.getGlobal(); + private static int runFXScripts(final Context context, final Global global, final List<String> files) throws IOException { + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { if (globalChanged) { @@ -389,11 +391,11 @@ public class Shell { * @return return code */ @SuppressWarnings("resource") - private static int readEvalPrint(final Context context, final ScriptObject global) { + private static int readEvalPrint(final Context context, final Global global) { final String prompt = bundle.getString("shell.prompt"); final BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); final PrintWriter err = context.getErr(); - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); final ScriptEnvironment env = context.getEnv(); @@ -404,7 +406,7 @@ public class Shell { // initialize with "shell.js" script try { - final Source source = new Source("<shell.js>", Shell.class.getResource("resources/shell.js")); + final Source source = sourceFor("<shell.js>", Shell.class.getResource("resources/shell.js")); context.eval(global, source.getString(), global, "<shell.js>", false); } catch (final Exception e) { err.println(e); @@ -451,7 +453,7 @@ public class Shell { } } finally { if (globalChanged) { - Context.setGlobal(global); + Context.setGlobal(oldGlobal); } } diff --git a/test/script/basic/JDK-8008448.js b/test/script/basic/JDK-8008448.js index b30e3417..240efd61 100644 --- a/test/script/basic/JDK-8008448.js +++ b/test/script/basic/JDK-8008448.js @@ -26,6 +26,7 @@ * Ensure that all parseable files can be parsed using parser API. * * @test + * @option --const-as-var * @option -scripting * @run */ diff --git a/test/script/basic/JDK-8011964.js b/test/script/basic/JDK-8011964.js new file mode 100644 index 00000000..a46023eb --- /dev/null +++ b/test/script/basic/JDK-8011964.js @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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. + * + * 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-8011964: need indexed access to externally-managed ByteBuffer + * + * @test + * @run + */ + + +var ByteBuffer = Java.type("java.nio.ByteBuffer"); +var buf = ByteBuffer.allocate(5); + +var obj = {} +Object.setIndexedPropertiesToExternalArrayData(obj, buf); + +obj[0] = 'A'.charCodeAt(0); +obj[1] = 'B'.charCodeAt(0); +obj[2] = 'C'.charCodeAt(0); +obj[3] = 'D'.charCodeAt(0); +obj[4] = 'E'.charCodeAt(0); + +for (var i = 0; i < buf.capacity(); i++) { + print("obj[" + i + "] = " + obj[i]); + print("buf.get(" + i + ") = " + buf.get(i)); +} + +var arr = []; +Object.setIndexedPropertiesToExternalArrayData(arr, buf); +obj[0] = 'a'.charCodeAt(0); +obj[1] = 'b'.charCodeAt(0); +obj[2] = 'c'.charCodeAt(0); +obj[3] = 'd'.charCodeAt(0); +obj[4] = 'e'.charCodeAt(0); + +for (var i in arr) { + print("arr[" + i + "] = " + arr[i]); + print("buf.get(" + i + ") = " + buf.get(i)); +} diff --git a/test/script/basic/JDK-8011964.js.EXPECTED b/test/script/basic/JDK-8011964.js.EXPECTED new file mode 100644 index 00000000..5bec2bdf --- /dev/null +++ b/test/script/basic/JDK-8011964.js.EXPECTED @@ -0,0 +1,20 @@ +obj[0] = 65 +buf.get(0) = 65 +obj[1] = 66 +buf.get(1) = 66 +obj[2] = 67 +buf.get(2) = 67 +obj[3] = 68 +buf.get(3) = 68 +obj[4] = 69 +buf.get(4) = 69 +arr[0] = 97 +buf.get(0) = 97 +arr[1] = 98 +buf.get(1) = 98 +arr[2] = 99 +buf.get(2) = 99 +arr[3] = 100 +buf.get(3) = 100 +arr[4] = 101 +buf.get(4) = 101 diff --git a/test/script/basic/JDK-8024120.js b/test/script/basic/JDK-8024120.js index 0ac4ff98..d2913d52 100644 --- a/test/script/basic/JDK-8024120.js +++ b/test/script/basic/JDK-8024120.js @@ -32,10 +32,6 @@ var obj = {}; obj.__proto__ = null; -if (obj.__proto__ !== null || typeof(obj.__proto__) != 'object') { - fail("obj.__proto__ is expected to be null"); -} - var p = Object.getPrototypeOf(obj); if (p !== null || typeof(p) != 'object') { fail("Object.getPrototypeOf(obj) is expected to be null"); diff --git a/test/script/basic/JDK-8024174.js b/test/script/basic/JDK-8024174.js index 5fd48e17..bd9206a6 100644 --- a/test/script/basic/JDK-8024174.js +++ b/test/script/basic/JDK-8024174.js @@ -46,6 +46,6 @@ var obj2 = { __proto__: null }; -if (obj2.__proto__ !== null || Object.getPrototypeOf(obj2) !== null) { +if (Object.getPrototypeOf(obj2) !== null) { fail("obj2.__proto__ was not set to null inside literal"); } diff --git a/test/script/basic/JDK-8025515.js b/test/script/basic/JDK-8025515.js index f3d7cee4..29574d2a 100644 --- a/test/script/basic/JDK-8025515.js +++ b/test/script/basic/JDK-8025515.js @@ -30,13 +30,23 @@ // Make sure synthetic names of anonymous functions have correct line numbers +function getFirstScriptFrame(stack) { + for (frameNum in stack) { + var frame = stack[frameNum]; + if (frame.className.startsWith("jdk.nashorn.internal.scripts.Script$")) { + return frame; + } + } +} + function testMethodName(f, expected) { try { f(); fail("expected error"); } catch (e) { - var stack = e.getStackTrace(); - if (stack[0].methodName !== expected) { + var stack = e.nashornException.getStackTrace(); + var name = getFirstScriptFrame(stack).methodName; + if (name !== expected) { fail("got " + stack[0].methodName + ", expected " + expected); } } @@ -44,15 +54,15 @@ function testMethodName(f, expected) { testMethodName(function() { return a.b.c; -}, "_L45"); +}, "L:55"); -testMethodName(function() { throw new Error() }, "_L49"); +testMethodName(function() { throw new Error() }, "L:59"); var f = (function() { return function() { a.b.c; }; })(); -testMethodName(f, "_L51$_L52"); +testMethodName(f, "L:61$L:62"); testMethodName((function() { return function() { return a.b.c; }; -})(), "_L56$_L57"); +})(), "L:66$L:67"); diff --git a/test/script/basic/JDK-8026161.js b/test/script/basic/JDK-8026161.js index 49f888b7..e3e88486 100644 --- a/test/script/basic/JDK-8026161.js +++ b/test/script/basic/JDK-8026161.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,5 +28,5 @@ * @run */ -print(new java.awt.Color(1, 1, 1)) // creates Color[r=1,g=1,b=1] -print(new java.awt.Color(1.0, 1.0, 1.0)) // Color[r=255,g=255,b=255] +print(Java.type("jdk.nashorn.test.models.IntFloatOverloadSelection").overloadedMethod(1)) +print(Java.type("jdk.nashorn.test.models.IntFloatOverloadSelection").overloadedMethod(1.0)) diff --git a/test/script/basic/JDK-8026161.js.EXPECTED b/test/script/basic/JDK-8026161.js.EXPECTED index c7b00b08..ac032cff 100644 --- a/test/script/basic/JDK-8026161.js.EXPECTED +++ b/test/script/basic/JDK-8026161.js.EXPECTED @@ -1,2 +1,2 @@ -java.awt.Color[r=1,g=1,b=1] -java.awt.Color[r=255,g=255,b=255] +int +float diff --git a/test/script/basic/JDK-8027933.js b/test/script/basic/JDK-8027933.js new file mode 100644 index 00000000..506abc23 --- /dev/null +++ b/test/script/basic/JDK-8027933.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 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. + * + * 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-8027933: Add const.as.var option + * + * @test + * @option --const-as-var + * @run + */ + +const THE_ANSWER = 42; +print("Answer to all questions: " + THE_ANSWER); + +print((function () { + const FORTY_TWO = 42; + return FORTY_TWO +})()) diff --git a/test/script/basic/JDK-8027933.js.EXPECTED b/test/script/basic/JDK-8027933.js.EXPECTED new file mode 100644 index 00000000..cb920ab6 --- /dev/null +++ b/test/script/basic/JDK-8027933.js.EXPECTED @@ -0,0 +1,2 @@ +Answer to all questions: 42 +42 diff --git a/test/script/basic/JDK-8029364.js b/test/script/basic/JDK-8029364.js new file mode 100644 index 00000000..dfa8ea9d --- /dev/null +++ b/test/script/basic/JDK-8029364.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8029364: NashornException to expose thrown object + * + * @test + * @run + */ + +var m = new javax.script.ScriptEngineManager(); +var e = m.getEngineByName("nashorn"); +var g = e.eval("this"); +try { + e.eval("var e = new Error('foo'); e.bar = 33; throw e"); +} catch (se) { + // ScriptException instance's cause is a NashornException + print(se.getClass()); + var cause = se.cause; + print(cause.getClass()); + // NashornException instance has 'ecmaError' bean getter + print(cause.ecmaError); + // access to underlying ECMA Error object + print(cause.ecmaError instanceof g.Error); + print(cause.ecmaError.name); + print(cause.ecmaError.message); + print(cause.ecmaError.bar); +} + diff --git a/test/script/basic/JDK-8029364.js.EXPECTED b/test/script/basic/JDK-8029364.js.EXPECTED new file mode 100644 index 00000000..d01bb9ee --- /dev/null +++ b/test/script/basic/JDK-8029364.js.EXPECTED @@ -0,0 +1,7 @@ +class javax.script.ScriptException +class jdk.nashorn.internal.runtime.ECMAException +Error: foo +true +Error +foo +33 diff --git a/test/script/basic/JDK-8029667.js b/test/script/basic/JDK-8029667.js new file mode 100644 index 00000000..c0c2d156 --- /dev/null +++ b/test/script/basic/JDK-8029667.js @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8029667: Prototype linking is incorrect + * + * @test + * @run + */ + +function f(x) { + return (function inner() { + var y; (function dummy() { return y })() // force own scope for the inner function + with({}) { // 'with' block turns off fast scopes + return x + } + })(); +} +print(f(1)); +print(f(2)); + +function g(x) { + (function inner() { + var y; (function dummy() { return y })() // force own scope for the inner function + with({}) { // 'with' block turns off fast scopes + // Test setter as well as getter + x = x + 2; + } + })(); + print(x); +} + +g(1); +g(2); + +var withScopes = [{ func: function() { print("called 1");} }, { func: function() { print("called 2");} }]; + +for(var i in withScopes) { + with (withScopes[i]) { + var main = function() { + var frame; // <---- this local variable caused scope to be not set properly prior to fix + + function callFunc() { + frame = func(); + } + + callFunc(); + } + } + main(); +} + +for(var i in withScopes) { + with (withScopes[i]) { + var main = function() { + var frame; // <---- this local variable caused scope to be not set properly prior to fix + + function callFunc() { + frame = func = i; + } + + callFunc(); + } + } + main(); +} + +print(withScopes[0].func); +print(withScopes[1].func); + + diff --git a/test/script/basic/JDK-8029667.js.EXPECTED b/test/script/basic/JDK-8029667.js.EXPECTED new file mode 100644 index 00000000..8aa78efe --- /dev/null +++ b/test/script/basic/JDK-8029667.js.EXPECTED @@ -0,0 +1,8 @@ +1 +2 +3 +4 +called 1 +called 2 +0 +1 diff --git a/test/script/basic/JDK-8030182.js b/test/script/basic/JDK-8030182.js new file mode 100644 index 00000000..f3c492d4 --- /dev/null +++ b/test/script/basic/JDK-8030182.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8030182: scopeCall with -1 as line number + * + * @test + * @run + */ + +function func() { + throw new Error("Strange..."); +} + +var f2 = func; +var f3 = func; +var f4 = func; +var f5 = func; + +// check that "scopeCall" or some such internal method +// does not appear in script stack trace. +try { + func(); +} catch(err) { + print(err.stack.replace(/\\/g, '/')); +} diff --git a/test/script/basic/JDK-8030182.js.EXPECTED b/test/script/basic/JDK-8030182.js.EXPECTED new file mode 100644 index 00000000..d12b6d7f --- /dev/null +++ b/test/script/basic/JDK-8030182.js.EXPECTED @@ -0,0 +1,3 @@ +Error: Strange... + at func (test/script/basic/JDK-8030182.js:32) + at <program> (test/script/basic/JDK-8030182.js:43) diff --git a/test/script/basic/JDK-8030182_2.js b/test/script/basic/JDK-8030182_2.js new file mode 100644 index 00000000..de507e1d --- /dev/null +++ b/test/script/basic/JDK-8030182_2.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8030182: scopeCall with -1 as line number + * + * @test + * @run + */ + +var str = ""; + +// large code to force splitting +for (i = 0; i < 1000; ++i) + str +="o = new Object()\n"; + +str +="g()"; + +// check that "$split" or some such internal method +// does not appear in script stack trace!! +try { + eval(str); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + diff --git a/test/script/basic/JDK-8030182_2.js.EXPECTED b/test/script/basic/JDK-8030182_2.js.EXPECTED new file mode 100644 index 00000000..772e504d --- /dev/null +++ b/test/script/basic/JDK-8030182_2.js.EXPECTED @@ -0,0 +1,3 @@ +ReferenceError: "g" is not defined + at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>@0:-1) + at <program> (test/script/basic/JDK-8030182_2.js:42) diff --git a/test/script/basic/JDK-8030197.js b/test/script/basic/JDK-8030197.js new file mode 100644 index 00000000..366d33df --- /dev/null +++ b/test/script/basic/JDK-8030197.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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. + * + * 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-8030197: Nashorn: Object.defineProperty() can be lured to change fixed NaN property + * + * @test + * @run + */ + +function str(n) { + var a = new Uint8Array(new Float64Array([n]).buffer); + return Array.apply(null, a).reduceRight( + function(acc, v){ + return acc + (v < 10 ? "0" : "") + v.toString(16); + }, ""); +} + +var o = Object.defineProperty({}, "NaN", { value: NaN }) +var str1 = str(o.NaN); +Object.defineProperty(o, "NaN", { value: 0/0 }) +var str2 = str(o.NaN); +if (str1 != str2) { + fail("NaN bit pattern changed"); +} diff --git a/test/script/basic/JDK-8030199.js b/test/script/basic/JDK-8030199.js new file mode 100644 index 00000000..b6d32fb1 --- /dev/null +++ b/test/script/basic/JDK-8030199.js @@ -0,0 +1,50 @@ +/* + * 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. + * + * 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-8030199: Nashorn: Uint8ClampedArray - Incorrect ToUint8Clamp implementation + * + * @test + * @run + */ + +function testTypedArray(ArrayType) { + print(ArrayType.BYTES_PER_ELEMENT); + var a = new ArrayType(7); + a[0] = 4294967296; + a[1] = -4294967295; + a[2] = 4294967298; + a[3] = -4294967298; + a[4] = Infinity; + a[5] = -Infinity; + a[6] = NaN; + print(Array.prototype.join.call(a)); +} + +testTypedArray(Uint8ClampedArray); +testTypedArray(Uint8Array); +testTypedArray(Int8Array); +testTypedArray(Uint16Array); +testTypedArray(Int16Array); +testTypedArray(Uint32Array); +testTypedArray(Int32Array); diff --git a/test/script/basic/JDK-8030199.js.EXPECTED b/test/script/basic/JDK-8030199.js.EXPECTED new file mode 100644 index 00000000..0b2cf627 --- /dev/null +++ b/test/script/basic/JDK-8030199.js.EXPECTED @@ -0,0 +1,14 @@ +1 +255,0,255,0,255,0,0 +1 +0,1,2,254,0,0,0 +1 +0,1,2,-2,0,0,0 +2 +0,1,2,65534,0,0,0 +2 +0,1,2,-2,0,0,0 +4 +0,1,2,4294967294,0,0,0 +4 +0,1,2,-2,0,0,0 diff --git a/test/script/basic/JDK-8030200.js b/test/script/basic/JDK-8030200.js new file mode 100644 index 00000000..e49eec7d --- /dev/null +++ b/test/script/basic/JDK-8030200.js @@ -0,0 +1,36 @@ +/* + * 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. + * + * 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-8030200: Wrong result for Number.prototype.toString() for certain radix/inputs + * + * @test + * @run + */ + +var n = 0x8000000000000800; +print(n); +var s = n.toString(5); +var m = parseInt(s, 5); +print(m === n); +print(n); diff --git a/test/script/basic/JDK-8030200.js.EXPECTED b/test/script/basic/JDK-8030200.js.EXPECTED new file mode 100644 index 00000000..38b73bc2 --- /dev/null +++ b/test/script/basic/JDK-8030200.js.EXPECTED @@ -0,0 +1,3 @@ +9223372036854778000 +true +9223372036854778000 diff --git a/test/script/basic/JDK-8030809.js b/test/script/basic/JDK-8030809.js new file mode 100644 index 00000000..01c7687c --- /dev/null +++ b/test/script/basic/JDK-8030809.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8030809: Anonymous functions should not be shown with internal names in script stack trace + * + * @test + * @run + */ + +function func() { + (function() { + throw new Error(); + })(); +} + +try { + func(); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} diff --git a/test/script/basic/JDK-8030809.js.EXPECTED b/test/script/basic/JDK-8030809.js.EXPECTED new file mode 100644 index 00000000..6d2ee69b --- /dev/null +++ b/test/script/basic/JDK-8030809.js.EXPECTED @@ -0,0 +1,4 @@ +Error + at <anonymous> (test/script/basic/JDK-8030809.js:33) + at func (test/script/basic/JDK-8030809.js:32) + at <program> (test/script/basic/JDK-8030809.js:38) diff --git a/test/script/basic/JDK-8031317.js b/test/script/basic/JDK-8031317.js new file mode 100644 index 00000000..240a6337 --- /dev/null +++ b/test/script/basic/JDK-8031317.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8031317: SyntaxError when property setter has no parameter + * + * @test + * @run + */ + +var obj = { + get toto() { + print("in getter for 'toto'"); + }, + set toto() { + print("in setter for 'toto'"); + } +} + +obj.toto; +obj.toto = 344; diff --git a/test/script/basic/JDK-8031317.js.EXPECTED b/test/script/basic/JDK-8031317.js.EXPECTED new file mode 100644 index 00000000..dda72eae --- /dev/null +++ b/test/script/basic/JDK-8031317.js.EXPECTED @@ -0,0 +1,2 @@ +in getter for 'toto' +in setter for 'toto' diff --git a/test/script/basic/JDK-8031359.js b/test/script/basic/JDK-8031359.js new file mode 100644 index 00000000..278fd7d2 --- /dev/null +++ b/test/script/basic/JDK-8031359.js @@ -0,0 +1,62 @@ +/* + * 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. + * + * 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-8031359: Invocable.getInterface() works incorrectly if interface has default methods + * + * @test + * @run + */ + +var func = new java.util.function.Function() { + apply: function(arg) { + print("func called with " + arg); + return arg.toUpperCase(); + } +}; + +// Function.andThen is a default method +func.andThen(func)("hello"); + +// Function.compose is another default method +func.compose(new java.util.function.Function() { + apply: function(arg) { + print("compose called with " + arg); + return arg.charAt(0); + } +})("hello"); + +var func2 = new java.util.function.Function() { + apply: function(arg) { + print("I am func2: " + arg); + return arg; + }, + + andThen: function(func) { + print("This is my andThen!"); + return func; + } +}; + +func2.apply("hello"); +func2.andThen(func); diff --git a/test/script/basic/JDK-8031359.js.EXPECTED b/test/script/basic/JDK-8031359.js.EXPECTED new file mode 100644 index 00000000..3fb72f1b --- /dev/null +++ b/test/script/basic/JDK-8031359.js.EXPECTED @@ -0,0 +1,6 @@ +func called with hello +func called with HELLO +compose called with hello +func called with h +I am func2: hello +This is my andThen! diff --git a/test/script/basic/JDK-8031715.js b/test/script/basic/JDK-8031715.js new file mode 100644 index 00000000..19994f51 --- /dev/null +++ b/test/script/basic/JDK-8031715.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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. + * + * 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-8031715: Indexed access to java package not working + * @test + * @run + */ + +print(java["net"]); +print(java["net"]["URL"]); +print(java["net"].URL); +print(java.net["URL"]); + +var is = "InputStream"; +var io = "io"; + +print(java.io[is]); +print(java[io]); +print(java[io][is]); + +var ji = new JavaImporter(java.util, java.io); +print(ji["InputStream"]); +print(ji['Vector']); + +var hash = "Hashtable"; +var printStream = "PrintStream"; +print(ji[hash]); +print(ji[printStream]); diff --git a/test/script/basic/JDK-8031715.js.EXPECTED b/test/script/basic/JDK-8031715.js.EXPECTED new file mode 100644 index 00000000..1a71de34 --- /dev/null +++ b/test/script/basic/JDK-8031715.js.EXPECTED @@ -0,0 +1,11 @@ +[JavaPackage java.net] +[JavaClass java.net.URL] +[JavaClass java.net.URL] +[JavaClass java.net.URL] +[JavaClass java.io.InputStream] +[JavaPackage java.io] +[JavaClass java.io.InputStream] +[JavaClass java.io.InputStream] +[JavaClass java.util.Vector] +[JavaClass java.util.Hashtable] +[JavaClass java.io.PrintStream] diff --git a/test/script/basic/JDK-8031983.js b/test/script/basic/JDK-8031983.js new file mode 100644 index 00000000..f0bfca13 --- /dev/null +++ b/test/script/basic/JDK-8031983.js @@ -0,0 +1,55 @@ +/* + * Copyright (c) 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. + * + * 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-8031983: Error objects should capture stack at the constructor + * + * @test + * @run + */ + +var e = new Error(); +print("hello"); + +try { + throw e; +} catch (e) { + print(e.lineNumber); + print(e.stack.replace(/\\/g, '/')); +} + +Error.captureStackTrace(e); +try { + throw e; +} catch (e) { + print(e.lineNumber); + print(e.stack.replace(/\\/g, '/')); +} + +var obj = {}; +Error.captureStackTrace(obj); +try { + throw obj; +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} diff --git a/test/script/basic/JDK-8031983.js.EXPECTED b/test/script/basic/JDK-8031983.js.EXPECTED new file mode 100644 index 00000000..9d62db31 --- /dev/null +++ b/test/script/basic/JDK-8031983.js.EXPECTED @@ -0,0 +1,9 @@ +hello +35 +Error + at <program> (test/script/basic/JDK-8031983.js:31) +43 +Error + at <program> (test/script/basic/JDK-8031983.js:41) +[object Object] + at <program> (test/script/basic/JDK-8031983.js:50) diff --git a/test/script/basic/JDK-8032004.js b/test/script/basic/JDK-8032004.js new file mode 100644 index 00000000..2995566c --- /dev/null +++ b/test/script/basic/JDK-8032004.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 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. + * + * 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-8032004: instance property "message" of Error objects should be non-enumerable + * + * @test + * @run + */ + +function check(obj) { + if (obj.propertyIsEnumerable("message")) { + fail(obj.name + " object's message property is enumerable!"); + } +} + +check(new Error("test")); +check(new EvalError("test")); +check(new RangeError("test")); +check(new ReferenceError("test")); +check(new SyntaxError("test")); +check(new TypeError("test")); +check(new URIError("test")); diff --git a/test/script/basic/JDK-8032068.js b/test/script/basic/JDK-8032068.js new file mode 100644 index 00000000..570789d7 --- /dev/null +++ b/test/script/basic/JDK-8032068.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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. + * + * 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-8032068: implement @sourceURL and #sourceURL directives. + * + * @test + * @run + */ + + +try { + Function("throw new Error();\n//# sourceURL=foo.js")(); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + +try { + eval("function g() { throw Error('x');\n } g();\n//# sourceURL=bar.js"); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + +// check @sourceURL for compatibility +try { + Function("throw new Error();\n//@ sourceURL=foo2.js")(); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + +try { + eval("function g() { throw Error('x');\n } g();\n//@ sourceURL=bar2.js"); +} catch (e) { + print(e.stack.replace(/\\/g, '/')); +} + diff --git a/test/script/basic/JDK-8032068.js.EXPECTED b/test/script/basic/JDK-8032068.js.EXPECTED new file mode 100644 index 00000000..b8891eee --- /dev/null +++ b/test/script/basic/JDK-8032068.js.EXPECTED @@ -0,0 +1,14 @@ +Error + at <anonymous> (foo.js:2) + at <program> (test/script/basic/JDK-8032068.js:33) +Error: x + at g (bar.js:1) + at <program> (bar.js:2) + at <program> (test/script/basic/JDK-8032068.js:39) +Error + at <anonymous> (foo2.js:2) + at <program> (test/script/basic/JDK-8032068.js:46) +Error: x + at g (bar2.js:1) + at <program> (bar2.js:2) + at <program> (test/script/basic/JDK-8032068.js:52) diff --git a/test/script/basic/JDK-8034055.js b/test/script/basic/JDK-8034055.js new file mode 100644 index 00000000..0a21d9f9 --- /dev/null +++ b/test/script/basic/JDK-8034055.js @@ -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. + * + * 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-8034055: delete on global object not properly guarded + * + * @test + * @run + */ + + +var global = this; +var x; + +function test(defineGlobals) { + if (defineGlobals) { + global.x = 1; + global.y = 2; + } + try { + print(x); + print(y); + } catch (e) { + print(e); + } finally { + print(delete global.x); + print(delete global.y); + } +} + +// Repeatedly set and delete global variables +test(true); +test(false); +test(true); +test(false); diff --git a/test/script/basic/JDK-8034055.js.EXPECTED b/test/script/basic/JDK-8034055.js.EXPECTED new file mode 100644 index 00000000..c947f4ae --- /dev/null +++ b/test/script/basic/JDK-8034055.js.EXPECTED @@ -0,0 +1,16 @@ +1 +2 +false +true +1 +ReferenceError: "y" is not defined +false +true +1 +2 +false +true +1 +ReferenceError: "y" is not defined +false +true diff --git a/test/script/basic/JDK-8037562.js b/test/script/basic/JDK-8037562.js new file mode 100644 index 00000000..7534a0da --- /dev/null +++ b/test/script/basic/JDK-8037562.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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. + * + * 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-8037562: Nashorn: JSON.parse comes up with nonexistent entries if there are gaps between the keys + * + * @test + * @run + */ + +var strs = [ + '{ "0":0, "2":2 }', + '{ "0":"", "2":"" }', + '{ "0":0, "5":"hello" }', + '{ "0":"", "15":3234 }', +] + +for (var i in strs) { + print(JSON.stringify(JSON.parse(strs[i]))); +} diff --git a/test/script/basic/JDK-8037562.js.EXPECTED b/test/script/basic/JDK-8037562.js.EXPECTED new file mode 100644 index 00000000..ea671713 --- /dev/null +++ b/test/script/basic/JDK-8037562.js.EXPECTED @@ -0,0 +1,4 @@ +{"0":0,"2":2} +{"0":"","2":""} +{"0":0,"5":"hello"} +{"0":"","15":3234} diff --git a/test/script/basic/JDK-8039387.js b/test/script/basic/JDK-8039387.js new file mode 100644 index 00000000..8f903c1a --- /dev/null +++ b/test/script/basic/JDK-8039387.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 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. + * + * 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-8039387: Nashorn supports indexed access of List elements, but length property is not supported + * + * @test + * @run + */ + +var ArrayList = Java.type("java.util.ArrayList") +var list = new ArrayList(3) +list.add("nashorn") +list.add("js") +list.add("ecmascript") +var len = list.length +print("length = " + len) +for (var i = 0; i < len; i++) + print(list[i]) diff --git a/test/script/basic/JDK-8039387.js.EXPECTED b/test/script/basic/JDK-8039387.js.EXPECTED new file mode 100644 index 00000000..5b9ce56d --- /dev/null +++ b/test/script/basic/JDK-8039387.js.EXPECTED @@ -0,0 +1,4 @@ +length = 3 +nashorn +js +ecmascript diff --git a/test/script/basic/JDK-8041998.js b/test/script/basic/JDK-8041998.js new file mode 100644 index 00000000..33e4d136 --- /dev/null +++ b/test/script/basic/JDK-8041998.js @@ -0,0 +1,52 @@ +/* + * 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. + * + * 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-8041998: RegExp implementation is not thread-safe + * + * @test + * @run + */ + +var Thread = java.lang.Thread; + +function run() { + var line = 'content-type: text/html'; + for (var i = 0; i < 300; i++) { + Thread.sleep(1); + line.split(/: /); + } + print("done"); +} + +var threads = []; + +for (var i = 0; i < 4; i++) { + var thread = new Thread(run); + thread.start(); + threads.push(thread); +} + +for (var i = 0; i < 4; i++) { + threads[i].join(); +} diff --git a/test/script/basic/JDK-8041998.js.EXPECTED b/test/script/basic/JDK-8041998.js.EXPECTED new file mode 100644 index 00000000..8f92fbfa --- /dev/null +++ b/test/script/basic/JDK-8041998.js.EXPECTED @@ -0,0 +1,4 @@ +done +done +done +done diff --git a/test/script/basic/JDK-8042364.js b/test/script/basic/JDK-8042364.js new file mode 100644 index 00000000..d8b81e24 --- /dev/null +++ b/test/script/basic/JDK-8042364.js @@ -0,0 +1,65 @@ +/* + * Copyright (c) 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. + * + * 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-8042364: Make __proto__ ES6 draft compliant + * + * @test + * @run + */ + +// check for Object.prototype.__proto__ accessor property +print("Object.prototype has __proto__?", + Object.prototype.hasOwnProperty("__proto__")) + +var desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__") +print("descriptor"); +print(JSON.stringify(desc)) +print("getter", desc.get) +print("setter", desc.set) + +// no computed "__proto__" name, only identifier! +var p = {} +var obj = { + "__proto__" : p +} + +if (Object.getPrototypeOf(obj) === p) { + fail("obj has wrong __proto__, allows computed __proto__!") +} + +if (obj.__proto__ !== p) { + fail("__proto__ not created as normal property!") +} + +if (Object.getPrototypeOf(obj) !== Object.prototype) { + fail("obj has wrong __proto__") +} + +var obj2 = { + __proto__: p +} + +if (Object.getPrototypeOf(obj2) !== p) { + fail("can't set __proto__ in object literal") +} diff --git a/test/script/basic/JDK-8042364.js.EXPECTED b/test/script/basic/JDK-8042364.js.EXPECTED new file mode 100644 index 00000000..b92947a8 --- /dev/null +++ b/test/script/basic/JDK-8042364.js.EXPECTED @@ -0,0 +1,5 @@ +Object.prototype has __proto__? true +descriptor +{"configurable":true,"enumerable":false} +getter function getProto() { [native code] } +setter function setProto() { [native code] } diff --git a/test/script/basic/NASHORN-111.js.EXPECTED b/test/script/basic/NASHORN-111.js.EXPECTED index 287f2558..0967ef42 100644 --- a/test/script/basic/NASHORN-111.js.EXPECTED +++ b/test/script/basic/NASHORN-111.js.EXPECTED @@ -1 +1 @@ -{"message":"type error"} +{} diff --git a/test/script/basic/NASHORN-173.js.EXPECTED b/test/script/basic/NASHORN-173.js.EXPECTED index a0781a88..5375ecb1 100644 --- a/test/script/basic/NASHORN-173.js.EXPECTED +++ b/test/script/basic/NASHORN-173.js.EXPECTED @@ -132,7 +132,7 @@ RangeError 2.3423446609034533e+21 2.3423446609034533e+21 11111101111101010001111111010101101000101011011001001000000000000000000 -2224143002343343220233144213324 +2224143002343343220233044213324 375752177255053311000000 73b92b9962990aa44400 7efa8fead15b240000 diff --git a/test/script/basic/NASHORN-441.js.EXPECTED b/test/script/basic/NASHORN-441.js.EXPECTED index de9ea224..c0038426 100644 --- a/test/script/basic/NASHORN-441.js.EXPECTED +++ b/test/script/basic/NASHORN-441.js.EXPECTED @@ -12,6 +12,6 @@ finally 4 try 5 rethrow 5 finally 5 -Error: try 5 thrown in line 71 +Error: try 5 thrown in line 74 try 6 finally 6 diff --git a/test/script/basic/dataview_endian.js b/test/script/basic/dataview_endian.js new file mode 100644 index 00000000..f7607c0e --- /dev/null +++ b/test/script/basic/dataview_endian.js @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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. + * + * 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-8015958: DataView constructor is not defined + * + * @test + * @run + */ + +// set/get endianess checks + +var buffer = new ArrayBuffer(4); +var dv = new DataView(buffer); + +// write (default) big endian, read big/little endian +dv.setUint16(0, 0xABCD); +Assert.assertEquals(dv.getUint16(0), 0xABCD); +Assert.assertEquals(dv.getUint16(0, false), 0xABCD); +Assert.assertEquals(dv.getUint16(0, true), 0xCDAB); + +// write little endian, read big/little endian +dv.setUint16(0, 0xABCD, true); +Assert.assertEquals(dv.getUint16(0), 0xCDAB); +Assert.assertEquals(dv.getUint16(0, false), 0xCDAB); +Assert.assertEquals(dv.getUint16(0, true), 0xABCD); + +// write explicit big endian, read big/little endian +dv.setUint16(0, 0xABCD, false); +Assert.assertEquals(dv.getUint16(0), 0xABCD); +Assert.assertEquals(dv.getUint16(0, false), 0xABCD); +Assert.assertEquals(dv.getUint16(0, true), 0xCDAB); + +// write (default) big endian, read big/little endian +dv.setUint32(0, 0xABCDEF89); +Assert.assertEquals(dv.getUint32(0), 0xABCDEF89); +Assert.assertEquals(dv.getUint32(0, false), 0xABCDEF89); +Assert.assertEquals(dv.getUint32(0, true), 0x89EFCDAB); + +// write little endian, read big/little endian +dv.setUint32(0, 0xABCDEF89, true); +Assert.assertEquals(dv.getUint32(0), 0x89EFCDAB); +Assert.assertEquals(dv.getUint32(0, false), 0x89EFCDAB); +Assert.assertEquals(dv.getUint32(0, true), 0xABCDEF89); + +// write explicit big endian, read big/little endian +dv.setUint32(0, 0xABCDEF89, false); +Assert.assertEquals(dv.getUint32(0), 0xABCDEF89); +Assert.assertEquals(dv.getUint32(0, false), 0xABCDEF89); +Assert.assertEquals(dv.getUint32(0, true), 0x89EFCDAB); diff --git a/test/script/basic/dataview_getset.js b/test/script/basic/dataview_getset.js new file mode 100644 index 00000000..8c7a994c --- /dev/null +++ b/test/script/basic/dataview_getset.js @@ -0,0 +1,93 @@ +/* + * Copyright (c) 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. + * + * 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-8015958: DataView constructor is not defined + * + * @test + * @run + */ + +// checking get/set of values of various types +// Also basic endianess check. + +var Float = Java.type("java.lang.Float"); +var Double = Java.type("java.lang.Double"); + +var DOUBLE_MIN = Double.MIN_VALUE; +var DOUBLE_MIN_NORMAL = Double.MIN_NORMAL; +var FLOAT_MIN = Float.MIN_VALUE; +var FLOAT_MIN_NORMAL = Float.MIN_NORMAL; + +var buffer = new ArrayBuffer(12); +var dv = new DataView(buffer); + +dv.setInt8(1, 123); +Assert.assertEquals(dv.getInt8(1), 123); +dv.setInt8(1, 123, true); +Assert.assertEquals(dv.getInt8(1, true), 123); + +dv.setUint8(1, 255); +Assert.assertEquals(dv.getUint8(1), 255); +dv.setUint8(1, 255, true); +Assert.assertEquals(dv.getUint8(1, true), 255); + +dv.setInt16(1, 1234); +Assert.assertEquals(dv.getInt16(1), 1234); +dv.setInt16(1, 1234, true); +Assert.assertEquals(dv.getInt16(1, true), 1234); + +dv.setUint16(1, 65535); +Assert.assertEquals(dv.getUint16(1), 65535); +dv.setUint16(1, 65535, true); +Assert.assertEquals(dv.getUint16(1, true), 65535); + +dv.setInt32(1, 1234); +Assert.assertEquals(dv.getInt32(1), 1234); +dv.setInt32(1, 1234, true); +Assert.assertEquals(dv.getInt32(1, true), 1234); + +dv.setUint32(1, 4294967295); +Assert.assertEquals(dv.getUint32(1), 4294967295); +dv.setUint32(1, 4294967295, true); +Assert.assertEquals(dv.getUint32(1, true), 4294967295); + +dv.setFloat64(1, Math.PI); +Assert.assertEquals(dv.getFloat64(1), Math.PI, DOUBLE_MIN); +dv.setFloat64(1, Math.PI, true); +Assert.assertEquals(dv.getFloat64(1, true), Math.PI, DOUBLE_MIN); + +dv.setFloat64(1, DOUBLE_MIN_NORMAL); +Assert.assertEquals(dv.getFloat64(1), DOUBLE_MIN_NORMAL, DOUBLE_MIN); +dv.setFloat64(1, DOUBLE_MIN_NORMAL, true); +Assert.assertEquals(dv.getFloat64(1, true), DOUBLE_MIN_NORMAL, DOUBLE_MIN); + +dv.setFloat32(1, 1.414); +Assert["assertEquals(float, float, float)"](dv.getFloat32(1), 1.414, FLOAT_MIN); +dv.setFloat32(1, 1.414, true); +Assert["assertEquals(float, float, float)"](dv.getFloat32(1, true), 1.414, FLOAT_MIN); + +dv.setFloat32(1, FLOAT_MIN_NORMAL); +Assert["assertEquals(float, float, float)"](dv.getFloat32(1), FLOAT_MIN_NORMAL, FLOAT_MIN); +dv.setFloat32(1, FLOAT_MIN_NORMAL, true); +Assert["assertEquals(float, float, float)"](dv.getFloat32(1, true), FLOAT_MIN_NORMAL, FLOAT_MIN); diff --git a/test/script/basic/dataview_new.js b/test/script/basic/dataview_new.js new file mode 100644 index 00000000..78f8183d --- /dev/null +++ b/test/script/basic/dataview_new.js @@ -0,0 +1,71 @@ +/* + * Copyright (c) 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. + * + * 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-8015958: DataView constructor is not defined + * + * @test + * @run + */ + +// basic DataView constructor checks. + +// check ArrayBufferView property values of DataView instance +function check(dv, buf, offset, length) { + if (dv.buffer !== buf) { + fail("DataView.buffer is wrong"); + } + + if (dv.byteOffset != offset) { + fail("DataView.byteOffset = " + dv.byteOffset + ", expected " + offset); + } + + if (dv.byteLength != length) { + fail("DataView.byteLength = " + dv.byteLength + ", expected " + length); + } +} + +var buffer = new ArrayBuffer(12); +check(new DataView(buffer), buffer, 0, 12); +check(new DataView(buffer, 2), buffer, 2, 10); +check(new DataView(buffer, 4, 8), buffer, 4, 8); + +// make sure expected error is thrown +function checkError(callback, ErrorType) { + try { + callback(); + fail("Should have thrown " + ErrorType.name); + } catch (e) { + if (! (e instanceof ErrorType)) { + fail("Expected " + ErrorType.name + " got " + e); + } + } +} + +// non ArrayBuffer as first arg +checkError(function() { new DataView(344) }, TypeError); + +// illegal offset/length values +checkError(function() { new DataView(buffer, -1) }, RangeError); +checkError(function() { new DataView(buffer, 15) }, RangeError); +checkError(function() { new DataView(buffer, 1, 32) }, RangeError); diff --git a/test/script/basic/list.js b/test/script/basic/list.js index 72ae0be7..59136c0b 100644 --- a/test/script/basic/list.js +++ b/test/script/basic/list.js @@ -33,7 +33,7 @@ print("l.class.name=" + Java.typeName(l.class)) // Has "class" property like any l.add("foo") l.add("bar") -print("l.length=" + l.length) // doesn't work, returns undefined +print("l.length=" + l.length) // works, maps to l.size() print("l.size()=" + l.size()) // this will work print("l[0]=" + l[0]) diff --git a/test/script/basic/list.js.EXPECTED b/test/script/basic/list.js.EXPECTED index 18feade2..47f3bd4f 100644 --- a/test/script/basic/list.js.EXPECTED +++ b/test/script/basic/list.js.EXPECTED @@ -1,5 +1,5 @@ l.class.name=java.util.ArrayList -l.length=undefined +l.length=2 l.size()=2 l[0]=foo l[1]=bar diff --git a/test/script/currently-failing/gettersetter.js b/test/script/currently-failing/gettersetter.js new file mode 100644 index 00000000..88f69492 --- /dev/null +++ b/test/script/currently-failing/gettersetter.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "maputil.js"); + +function Foo() { + return { + get foo() { return 42; }, + set foo(x) {} + } +} + +var obj1 = Foo(); +var obj2 = Foo(); + +assertSameMap(obj1, obj2, "Object literals before change"); + +Object.defineProperty(obj2, "foo", { get: function() { return 'hello' } }); +assertSameMap(obj1, obj2); + +Object.defineProperty(obj2, "foo", { set: function(x) { print(x) } }); +assertSameMap(obj1, obj2); diff --git a/test/script/error/JDK-8027933.js b/test/script/error/JDK-8027933.js new file mode 100644 index 00000000..9b398fa5 --- /dev/null +++ b/test/script/error/JDK-8027933.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 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. + * + * 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-8027933: Add const.as.var option + * + * @test/compile-error + */ + +// without --const-as-var the following should fail to compile +const THE_ANSWER = 42; diff --git a/test/script/error/JDK-8027933.js.EXPECTED b/test/script/error/JDK-8027933.js.EXPECTED new file mode 100644 index 00000000..9389a5fc --- /dev/null +++ b/test/script/error/JDK-8027933.js.EXPECTED @@ -0,0 +1,3 @@ +test/script/error/JDK-8027933.js:31:0 Expected an operand but found const +const THE_ANSWER = 42; +^ diff --git a/test/script/error/JDK-8039047.js b/test/script/error/JDK-8039047.js new file mode 100644 index 00000000..8cbd51fd --- /dev/null +++ b/test/script/error/JDK-8039047.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 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. + * + * 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-8039047: Parser accepts conditional catch clauses even when --no-syntax-extensions / -nse option is passed + * + * @option --no-syntax-extensions + * @test/compile-error + */ + +try { + func() +} catch (e if e instanceof ReferenceError) { + print("Got ReferenceError " + e); +} diff --git a/test/script/error/JDK-8039047.js.EXPECTED b/test/script/error/JDK-8039047.js.EXPECTED new file mode 100644 index 00000000..b1d2f170 --- /dev/null +++ b/test/script/error/JDK-8039047.js.EXPECTED @@ -0,0 +1,6 @@ +test/script/error/JDK-8039047.js:33:11 Expected ) but found if +} catch (e if e instanceof ReferenceError) { + ^ +test/script/error/JDK-8039047.js:35:0 Expected eof but found } +} +^ diff --git a/test/script/maptests/builtins.js b/test/script/maptests/builtins.js new file mode 100644 index 00000000..4de2ec9a --- /dev/null +++ b/test/script/maptests/builtins.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "maputil.js"); + +// check that builtin objects share property map + +assertSameMap(new Boolean(true), new Boolean(false)); +assertSameMap(new Number(3), new Number(Math.PI)); +assertSameMap(new String('hello'), new String('world')); +assertSameMap(new Object(), new Object()); +assertSameMap(/hello/, /world/); +// try w/without regexp flags +assertSameMap(/hello/i, /world/g); +assertSameMap(new Date(), new Date()); +assertSameMap(new Date(2000, 1, 1), new Date(1972, 5, 6)); +assertSameMap(Function(), Function()); +assertSameMap(Function("x", "return x"), Function("x", "return x*x")); +assertSameMap(new Error(), new Error()); +assertSameMap(new Error('foo'), new Error('bar')); +assertSameMap(new EvalError(), new EvalError()); +assertSameMap(new EvalError('foo'), new EvalError('bar')); +assertSameMap(new RangeError(), new RangeError()); +assertSameMap(new RangeError('foo'), new RangeError('bar')); +assertSameMap(new ReferenceError(), new ReferenceError()); +assertSameMap(new ReferenceError('foo'), new ReferenceError('bar')); +assertSameMap(new SyntaxError(), new SyntaxError()); +assertSameMap(new SyntaxError('foo'), new SyntaxError('bar')); +assertSameMap(new TypeError(), new TypeError()); +assertSameMap(new TypeError('foo'), new TypeError('bar')); +assertSameMap(new URIError(), new URIError()); +assertSameMap(new URIError('foo'), new URIError('bar')); diff --git a/test/script/maptests/constructor.js b/test/script/maptests/constructor.js new file mode 100644 index 00000000..5722a3c1 --- /dev/null +++ b/test/script/maptests/constructor.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "point.js"); + +// use constructor defined in a different script file +// These objects should share the map +assertSameMap(new Point(2, 3), new Point(43, 23)); +assertSameMap(new Point(), new Point()); +assertSameMap(new Point(), new Point(3, 1)); diff --git a/test/script/maptests/maputil.js b/test/script/maptests/maputil.js new file mode 100644 index 00000000..aa85d7f1 --- /dev/null +++ b/test/script/maptests/maputil.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @subtest + */ + +function assertSameMap(obj1, obj2, msg) { + if (! Debug.identical(Debug.map(obj1), Debug.map(obj2))) { + fail(obj1.constructor + " instances don't share map"); + } +} + +function assertNotSameMap(obj1, obj2, msg) { + if (Debug.identical(Debug.map(obj1), Debug.map(obj2))) { + fail(obj1.constructor + " and " + obj2.constructor + " instances share map"); + } +} diff --git a/test/script/maptests/object_create.js b/test/script/maptests/object_create.js new file mode 100644 index 00000000..1b1bd604 --- /dev/null +++ b/test/script/maptests/object_create.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "maputil.js"); + +// Objects created by Object.create +var obj1 = Object.create(Object.prototype); +var obj2 = Object.create(Object.prototype); +assertSameMap(obj1, obj2); + +var proto = { foo: 233 }; +obj1 = Object.create(proto); +obj2 = Object.create(proto); +assertSameMap(obj1, obj2); diff --git a/test/script/maptests/object_literals.js b/test/script/maptests/object_literals.js new file mode 100644 index 00000000..c73d8014 --- /dev/null +++ b/test/script/maptests/object_literals.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "maputil.js"); + +// Object literals created at the same callsite +function makeObject() { + return { foo: 34 } +} +assertSameMap(makeObject(), makeObject()); + +function makeObject2() { + return { foo: 42, bar: 'hello' } +} +assertSameMap(makeObject2(), makeObject2()); + +// Object literals created at different callsites +assertSameMap({}, {}); +assertSameMap({foo: 4}, {foo: 'hello'}); +assertSameMap({foo: 34, bar: 'fdgd'}, {foo: 'world', bar: 54}); diff --git a/test/script/maptests/point.js b/test/script/maptests/point.js new file mode 100644 index 00000000..38b8fa25 --- /dev/null +++ b/test/script/maptests/point.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + + +/** + * @subtest + */ + +function Point(x, y) { + this.x =x; this.y =y; +} + +Point.prototype.toString = function() { + return "(" + this.x + "," + this.y + ")"; +} + +Point.prototype.modulus = function() { + return Math.sqrt(this.x*this.x + this.y*this.y); +} + +Point.prototype.argument = function() { + return Math.atan2(this.y, this.x); +} + +load(__DIR__ + "maputil.js"); + +assertSameMap(new Point(2, 3), new Point(43, 23)); +assertSameMap(new Point(), new Point()); +assertSameMap(new Point(), new Point(3, 1)); diff --git a/test/script/maptests/property_add.js b/test/script/maptests/property_add.js new file mode 100644 index 00000000..20264554 --- /dev/null +++ b/test/script/maptests/property_add.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "maputil.js"); + +function Foo() {} + +var obj1 = new Foo(); +var obj2 = new Foo(); + +assertSameMap(obj1, obj2); + +// property addition at same callsite +function addX(obj, val) { + obj.x = val; +} +addX(obj1, 3); +addX(obj2, 'hello'); + +assertSameMap(obj1, obj2); diff --git a/test/script/maptests/property_delete.js b/test/script/maptests/property_delete.js new file mode 100644 index 00000000..e2824dd6 --- /dev/null +++ b/test/script/maptests/property_delete.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "maputil.js"); + +function Foo() { + this.x = 33; +} + +var obj1 = new Foo(); +var obj2 = new Foo(); + +assertSameMap(obj1, obj2); + +// property deletion at same callsite +function deleteX(obj) { + delete obj.x; +} +deleteX(obj1); +deleteX(obj2); + +assertSameMap(obj1, obj2); diff --git a/test/script/maptests/proto.js b/test/script/maptests/proto.js new file mode 100644 index 00000000..afb59509 --- /dev/null +++ b/test/script/maptests/proto.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/** + * @test + * @option -Dnashorn.debug=true + * @fork + */ + +load(__DIR__ + "maputil.js"); + +// add/delete property to proto (direct/indirect) should +// not affect the property map of the objects + +var proto2 = { foo: 334 } +var proto = Object.create(proto2); +proto.bar = "hello"; + +var obj1 = Object.create(proto); +var obj2 = Object.create(proto); + +assertSameMap(obj1, obj2); + +proto.newX = 'world'; +assertSameMap(obj1, obj2); + +delete proto.newX; +assertSameMap(obj1, obj2); + +proto2.newX = "foo"; +assertSameMap(obj1, obj2); + +delete proto2.newX; +assertSameMap(obj1, obj2); + + diff --git a/test/script/sandbox/JDK-8031106.js b/test/script/sandbox/JDK-8031106.js new file mode 100644 index 00000000..d5d83f69 --- /dev/null +++ b/test/script/sandbox/JDK-8031106.js @@ -0,0 +1,39 @@ +/* + * 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. + * + * 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-8031106: Nashorn: IndexOutOfBoundsException in NashornCallSiteDescriptor.getNameToken() + * + * @test + * @run + */ + +var cl = new java.lang.Object().getClass(); +try { + cl["forName"]; + fail("Should have thrown exception!"); +} catch (e) { + if (! (e instanceof java.lang.SecurityException)) { + fail("SecurityException expected, got " + e); + } +} diff --git a/test/script/sandbox/safeprops.js b/test/script/sandbox/safeprops.js new file mode 100644 index 00000000..dc12e74f --- /dev/null +++ b/test/script/sandbox/safeprops.js @@ -0,0 +1,65 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Try to access System properties safe to read for any code. + * No security exception expected. + * + * @test + * @security + * @run + * @bug 8033924: Default permissions are not given for eval code + */ + +var propNames = [ + "java.version", + "java.vendor", + "java.vendor.url", + "java.class.version", + "os.name", + "os.version", + "os.arch", + "file.separator", + "path.separator", + "line.separator", + "java.specification.version", + "java.specification.vendor", + "java.specification.name", + "java.vm.specification.version", + "java.vm.specification.vendor", + "java.vm.specification.name", + "java.vm.version", + "java.vm.vendor", + "java.vm.name" +]; + +// no security exception expected +for (var p in propNames) { + java.lang.System.getProperty(propNames[p]); +} + +// no security exception expected +for (var p in propNames) { + var name = propNames[p]; + eval('java.lang.System.getProperty(name)'); +} diff --git a/test/script/trusted/JDK-8006529.js b/test/script/trusted/JDK-8006529.js index 8eb83909..3567c33e 100644 --- a/test/script/trusted/JDK-8006529.js +++ b/test/script/trusted/JDK-8006529.js @@ -113,7 +113,7 @@ function findFunction(node) { var getContextMethod = Context.class.getMethod("getContext") var getEnvMethod = Context.class.getMethod("getEnv") -var SourceConstructor = Source.class.getConstructor(java.lang.String.class, java.lang.String.class) +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(ScriptEnvironment.class) @@ -121,7 +121,7 @@ var CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class) // source code, returns a jdk.nashorn.internal.ir.FunctionNode object // representing it. function compile(source) { - var source = SourceConstructor.newInstance("<no name>", source); + var source = sourceForMethod.invoke(null, "<no name>", source); var env = getEnvMethod.invoke(getContextMethod.invoke(null)) diff --git a/test/script/trusted/JDK-8032060.js b/test/script/trusted/JDK-8032060.js new file mode 100644 index 00000000..8cb350cc --- /dev/null +++ b/test/script/trusted/JDK-8032060.js @@ -0,0 +1,72 @@ +/* + * Copyright (c) 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. + * + * 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-8032060: PropertyMap of Error objects is not stable + * + * @test + * @option -Dnashorn.debug=true + * @fork + * @run + */ + +function checkMap(e1, e2) { + if (! Debug.identical(Debug.map(e1), Debug.map(e2))) { + fail("e1 and e2 have different maps"); + } + + var m1, m2; + + try { + throw e1 + } catch (e) { + m1 = Debug.map(e) + } + + try { + throw e2 + } catch (e) { + m2 = Debug.map(e) + } + + if (! Debug.identical(m1, m2)) { + fail("e1 and e2 have different maps after being thrown"); + } +} + +checkMap(new Error(), new Error()); +checkMap(new EvalError(), new EvalError()); +checkMap(new RangeError(), new RangeError()); +checkMap(new ReferenceError(), new ReferenceError()); +checkMap(new SyntaxError(), new SyntaxError()); +checkMap(new TypeError(), new TypeError()); +checkMap(new URIError(), new URIError()); + +// now try with message param +checkMap(new Error("x"), new Error("y")); +checkMap(new EvalError("x"), new EvalError("y")); +checkMap(new RangeError("x"), new RangeError("y")); +checkMap(new ReferenceError("x"), new ReferenceError("y")); +checkMap(new SyntaxError("x"), new SyntaxError("y")); +checkMap(new TypeError("x"), new TypeError("y")); +checkMap(new URIError("x"), new URIError("y")); diff --git a/test/src/jdk/nashorn/api/scripting/InvocableTest.java b/test/src/jdk/nashorn/api/scripting/InvocableTest.java index fad3f372..a6722f57 100644 --- a/test/src/jdk/nashorn/api/scripting/InvocableTest.java +++ b/test/src/jdk/nashorn/api/scripting/InvocableTest.java @@ -26,6 +26,7 @@ package jdk.nashorn.api.scripting; import java.util.Objects; +import java.util.function.Function; import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; @@ -522,4 +523,16 @@ public class InvocableTest { Assert.assertEquals(itf.test1(42, "a", "b"), "i == 42, strings instanceof java.lang.String[] == true, strings == [a, b]"); Assert.assertEquals(itf.test2(44, "c", "d", "e"), "arguments[0] == 44, arguments[1] instanceof java.lang.String[] == true, arguments[1] == [c, d, e]"); } + + @Test + @SuppressWarnings("unchecked") + public void defaultMethodTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Invocable inv = (Invocable) e; + + Object obj = e.eval("({ apply: function(arg) { return arg.toUpperCase(); }})"); + Function<String, String> func = inv.getInterface(obj, Function.class); + assertEquals(func.apply("hello"), "HELLO"); + } } diff --git a/test/src/jdk/nashorn/api/scripting/ScopeTest.java b/test/src/jdk/nashorn/api/scripting/ScopeTest.java index a18055bb..dc27d826 100644 --- a/test/src/jdk/nashorn/api/scripting/ScopeTest.java +++ b/test/src/jdk/nashorn/api/scripting/ScopeTest.java @@ -245,4 +245,320 @@ public class ScopeTest { sb.put("x", "newX"); assertTrue(e.eval("x", ctx).equals("newX")); } + + /** + * Test multi-threaded access to defined global variables for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedVarTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + final String sharedScript = "foo"; + + assertEquals(e.eval("var foo = 'original context';", origContext), null); + assertEquals(e.eval("var foo = 'new context';", newCtxt), null); + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + assertEquals(e.eval("var foo = 'newer context';", newCtxt), null); + final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); + + t3.start(); + t4.start(); + t3.join(); + t4.join(); + + assertEquals(e.eval(sharedScript), "original context"); + assertEquals(e.eval(sharedScript, newCtxt), "newer context"); + } + + /** + * Test multi-threaded access to undefined global variables for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedGlobalTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + assertEquals(e.eval("foo = 'original context';", origContext), "original context"); + assertEquals(e.eval("foo = 'new context';", newCtxt), "new context"); + final String sharedScript = "foo"; + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + Object obj3 = e.eval("delete foo; foo = 'newer context';", newCtxt); + assertEquals(obj3, "newer context"); + final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); + + t3.start(); + t4.start(); + t3.join(); + t4.join(); + + Assert.assertEquals(e.eval(sharedScript), "original context"); + Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context"); + } + + /** + * Test multi-threaded access using the postfix ++ operator for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedIncTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + assertEquals(e.eval("var x = 0;", origContext), null); + assertEquals(e.eval("var x = 2;", newCtxt), null); + final String sharedScript = "x++;"; + + final Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + try { + for (int i = 0; i < 1000; i++) { + assertEquals(e.eval(sharedScript, origContext), (double)i); + } + } catch (ScriptException se) { + fail(se.toString()); + } + } + }); + final Thread t2 = new Thread(new Runnable() { + @Override + public void run() { + try { + for (int i = 2; i < 1000; i++) { + assertEquals(e.eval(sharedScript, newCtxt), (double)i); + } + } catch (ScriptException se) { + fail(se.toString()); + } + } + }); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + } + + /** + * Test multi-threaded access to primitive prototype properties for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedPrimitiveTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + Object obj1 = e.eval("String.prototype.foo = 'original context';", origContext); + Object obj2 = e.eval("String.prototype.foo = 'new context';", newCtxt); + assertEquals(obj1, "original context"); + assertEquals(obj2, "new context"); + final String sharedScript = "''.foo"; + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); + assertEquals(obj3, "newer context"); + final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); + final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); + + t3.start(); + t4.start(); + t3.join(); + t4.join(); + + Assert.assertEquals(e.eval(sharedScript), "original context"); + Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context"); + } + + /** + * Test multi-threaded scope function invocation for shared script classes with multiple globals. + */ + @Test + public static void multiThreadedFunctionTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + e.eval(new URLReader(ScopeTest.class.getResource("resources/func.js")), origContext); + assertEquals(origContext.getAttribute("scopeVar"), 1); + assertEquals(e.eval("scopeTest()"), 1); + + e.eval(new URLReader(ScopeTest.class.getResource("resources/func.js")), newCtxt); + assertEquals(newCtxt.getAttribute("scopeVar"), 1); + assertEquals(e.eval("scopeTest();", newCtxt), 1); + + assertEquals(e.eval("scopeVar = 3;", newCtxt), 3); + assertEquals(newCtxt.getAttribute("scopeVar"), 3); + + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, "scopeTest()", 1, 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, "scopeTest()", 3, 1000)); + + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + } + + /** + * Test multi-threaded access to global getters and setters for shared script classes with multiple globals. + */ + @Test + public static void getterSetterTest() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + final String sharedScript = "accessor1"; + + e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), origContext); + assertEquals(e.eval("accessor1 = 1;"), 1); + assertEquals(e.eval(sharedScript), 1); + + e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), newCtxt); + assertEquals(e.eval("accessor1 = 2;", newCtxt), 2); + assertEquals(e.eval(sharedScript, newCtxt), 2); + + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, 1, 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, 2, 1000)); + + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + assertEquals(e.eval(sharedScript), 1); + assertEquals(e.eval(sharedScript, newCtxt), 2); + assertEquals(e.eval("v"), 1); + assertEquals(e.eval("v", newCtxt), 2); + } + + /** + * Test multi-threaded access to global getters and setters for shared script classes with multiple globals. + */ + @Test + public static void getterSetter2Test() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext origContext = e.getContext(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + final String sharedScript = "accessor2"; + + e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), origContext); + assertEquals(e.eval("accessor2 = 1;"), 1); + assertEquals(e.eval(sharedScript), 1); + + e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), newCtxt); + assertEquals(e.eval("accessor2 = 2;", newCtxt), 2); + assertEquals(e.eval(sharedScript, newCtxt), 2); + + + final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, 1, 1000)); + final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, 2, 1000)); + + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + assertEquals(e.eval(sharedScript), 1); + assertEquals(e.eval(sharedScript, newCtxt), 2); + assertEquals(e.eval("x"), 1); + assertEquals(e.eval("x", newCtxt), 2); + } + + /** + * Test "slow" scopes involving {@code with} and {@code eval} statements for shared script classes with multiple globals. + */ + @Test + public static void testSlowScope() throws ScriptException, InterruptedException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + + for (int i = 0; i < 100; i++) { + final Bindings b = e.createBindings(); + final ScriptContext ctxt = new SimpleScriptContext(); + ctxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + e.eval(new URLReader(ScopeTest.class.getResource("resources/witheval.js")), ctxt); + assertEquals(e.eval("a", ctxt), 1); + assertEquals(b.get("a"), 1); + assertEquals(e.eval("b", ctxt), 3); + assertEquals(b.get("b"), 3); + assertEquals(e.eval("c", ctxt), 10); + assertEquals(b.get("c"), 10); + } + } + + private static class ScriptRunner implements Runnable { + + final ScriptEngine engine; + final ScriptContext context; + final String source; + final Object expected; + final int iterations; + + ScriptRunner(final ScriptEngine engine, final ScriptContext context, final String source, final Object expected, final int iterations) { + this.engine = engine; + this.context = context; + this.source = source; + this.expected = expected; + this.iterations = iterations; + } + + @Override + public void run() { + try { + for (int i = 0; i < iterations; i++) { + assertEquals(engine.eval(source, context), expected); + } + } catch (ScriptException se) { + throw new RuntimeException(se); + } + } + } + } diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java index df8696d8..52199145 100644 --- a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java +++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java @@ -560,6 +560,47 @@ public class ScriptEngineTest { assertTrue(reached[0]); } + // properties that can be read by any code + private static String[] propNames = { + "java.version", + "java.vendor", + "java.vendor.url", + "java.class.version", + "os.name", + "os.version", + "os.arch", + "file.separator", + "path.separator", + "line.separator", + "java.specification.version", + "java.specification.vendor", + "java.specification.name", + "java.vm.specification.version", + "java.vm.specification.vendor", + "java.vm.specification.name", + "java.vm.version", + "java.vm.vendor", + "java.vm.name" + }; + + // @bug 8033924: Default permissions are not given for eval code + @Test + public void checkPropertyReadPermissions() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + + for (final String name : propNames) { + checkProperty(e, name); + } + } + + private static void checkProperty(final ScriptEngine e, final String name) + throws ScriptException { + String value = System.getProperty(name); + e.put("name", name); + assertEquals(value, e.eval("java.lang.System.getProperty(name)")); + } + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); // Returns String that would be the result of calling PrintWriter.println diff --git a/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java b/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java index 544f4ea7..241f22c3 100644 --- a/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java +++ b/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java @@ -25,6 +25,7 @@ package jdk.nashorn.api.scripting; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -230,6 +231,29 @@ public class ScriptObjectMirrorTest { } @Test + public void indexPropertiesExternalBufferTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final ScriptObjectMirror obj = (ScriptObjectMirror)e.eval("var obj = {}; obj"); + final ByteBuffer buf = ByteBuffer.allocate(5); + int i; + for (i = 0; i < 5; i++) { + buf.put(i, (byte)(i+10)); + } + obj.setIndexedPropertiesToExternalArrayData(buf); + + for (i = 0; i < 5; i++) { + assertEquals((byte)(i+10), ((Number)e.eval("obj[" + i + "]")).byteValue()); + } + + e.eval("for (i = 0; i < 5; i++) obj[i] = 0"); + for (i = 0; i < 5; i++) { + assertEquals((byte)0, ((Number)e.eval("obj[" + i + "]")).byteValue()); + assertEquals((byte)0, buf.get(i)); + } + } + + @Test public void conversionTest() throws ScriptException { final ScriptEngineManager m = new ScriptEngineManager(); final ScriptEngine e = m.getEngineByName("nashorn"); diff --git a/test/src/jdk/nashorn/api/scripting/resources/func.js b/test/src/jdk/nashorn/api/scripting/resources/func.js new file mode 100644 index 00000000..477bd1a6 --- /dev/null +++ b/test/src/jdk/nashorn/api/scripting/resources/func.js @@ -0,0 +1,42 @@ +/* + * 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. + */ + +// This script is loaded from jdk.nashorn.api.scripting.ScopeTest to test script class sharing and reuse. + +var scopeVar = 1; +var global = this; +undefGlobal = this; + +function scopeTest() { + if (this !== global) { + throw new Error("this !== global"); + } + if (this !== undefGlobal) { + throw new Error("this !== undefinedGlobal") + } + return scopeVar; +} + +scopeTest(); diff --git a/test/src/jdk/nashorn/api/scripting/resources/gettersetter.js b/test/src/jdk/nashorn/api/scripting/resources/gettersetter.js new file mode 100644 index 00000000..51e24727 --- /dev/null +++ b/test/src/jdk/nashorn/api/scripting/resources/gettersetter.js @@ -0,0 +1,38 @@ +/* + * 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. + */ + +// This script is loaded from jdk.nashorn.api.scripting.ScopeTest to test script class sharing and reuse. + +var v; + +Object.defineProperty(this, "accessor1", { + get: function() { return v; }, + set: function(n) { v = n; } +}); + +Object.defineProperty(this, "accessor2", { + get: function() { return x; }, + set: function(n) { x = n; } +}); diff --git a/test/src/jdk/nashorn/api/scripting/resources/witheval.js b/test/src/jdk/nashorn/api/scripting/resources/witheval.js new file mode 100644 index 00000000..6041d5b4 --- /dev/null +++ b/test/src/jdk/nashorn/api/scripting/resources/witheval.js @@ -0,0 +1,60 @@ +/* + * 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. + */ + +// This script is loaded from jdk.nashorn.api.scripting.ScopeTest to test script class sharing and reuse. + +var a; + +function outer(p, e) { + eval(e); + with(p) { + function inner() { + a = 1; + c = 10; + if (a !== 1) { + throw new Error("a !== 1"); + } + if (b !== 3) { + throw new Error("b !== 3"); + } + if (c !== 10) { + throw new Error("c !== 10"); + } + } + inner(); + } +} + +outer({}, "b = 3;"); + +if (a !== 1) { + throw new Error("a !== 1"); +} +if (b !== 3) { + throw new Error("b !== 3"); +} +if (c !== 10) { + throw new Error("c !== 10"); +} diff --git a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java index 00d79acb..1ebb97a2 100644 --- a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java +++ b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java @@ -25,13 +25,16 @@ package jdk.nashorn.internal.codegen; +import static jdk.nashorn.internal.runtime.Source.sourceFor; +import static jdk.nashorn.internal.runtime.Source.readFully; + import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.options.Options; import org.testng.Assert; @@ -58,7 +61,7 @@ public class CompilerTest { } private Context context; - private ScriptObject global; + private Global global; @BeforeClass public void setupTest() { @@ -68,6 +71,7 @@ public class CompilerTest { options.set("print.ast", true); options.set("print.parse", true); options.set("scripting", true); + options.set("const.as.var", true); final ErrorManager errors = new ErrorManager() { @Override @@ -146,11 +150,11 @@ public class CompilerTest { log("Begin compiling " + file.getAbsolutePath()); } - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { - final char[] buffer = Source.readFully(file); + final char[] buffer = readFully(file); boolean excluded = false; if (filter != null) { @@ -169,7 +173,7 @@ public class CompilerTest { if (globalChanged) { Context.setGlobal(global); } - final Source source = new Source(file.getAbsolutePath(), buffer); + final Source source = sourceFor(file.getAbsolutePath(), buffer); final ScriptFunction script = context.compileScript(source, global); if (script == null || context.getErrorManager().getNumberOfErrors() > 0) { log("Compile failed: " + file.getAbsolutePath()); diff --git a/test/src/jdk/nashorn/internal/parser/ParserTest.java b/test/src/jdk/nashorn/internal/parser/ParserTest.java index 3e10a89e..8a68bb11 100644 --- a/test/src/jdk/nashorn/internal/parser/ParserTest.java +++ b/test/src/jdk/nashorn/internal/parser/ParserTest.java @@ -25,6 +25,9 @@ package jdk.nashorn.internal.parser; +import static jdk.nashorn.internal.runtime.Source.sourceFor; +import static jdk.nashorn.internal.runtime.Source.readFully; + import java.io.File; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; @@ -62,6 +65,7 @@ public class ParserTest { options.set("anon.functions", true); options.set("parse.only", true); options.set("scripting", true); + options.set("const.as.var", true); ErrorManager errors = new ErrorManager(); this.context = new Context(options, errors, Thread.currentThread().getContextClassLoader()); @@ -131,7 +135,7 @@ public class ParserTest { } try { - final char[] buffer = Source.readFully(file); + final char[] buffer = readFully(file); boolean excluded = false; if (filter != null) { final String content = new String(buffer); @@ -153,7 +157,7 @@ public class ParserTest { } }; errors.setLimit(0); - final Source source = new Source(file.getAbsolutePath(), buffer); + final Source source = sourceFor(file.getAbsolutePath(), buffer); new Parser(context.getEnv(), source, errors).parse(); if (errors.getNumberOfErrors() > 0) { log("Parse failed: " + file.getAbsolutePath()); diff --git a/test/src/jdk/nashorn/internal/performance/PerformanceWrapper.java b/test/src/jdk/nashorn/internal/performance/PerformanceWrapper.java index 999f6fa4..ce969210 100644 --- a/test/src/jdk/nashorn/internal/performance/PerformanceWrapper.java +++ b/test/src/jdk/nashorn/internal/performance/PerformanceWrapper.java @@ -31,9 +31,9 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; /** @@ -89,7 +89,7 @@ public class PerformanceWrapper extends jdk.nashorn.tools.Shell { @Override protected Object apply(final ScriptFunction target, final Object self) { if (_runsPerIteration == 0 && _numberOfIterations == 0) { - final ScriptObject global = jdk.nashorn.internal.runtime.Context.getGlobal(); + final Global global = jdk.nashorn.internal.runtime.Context.getGlobal(); final ScriptFunction _target = target; final Object _self = self; diff --git a/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java b/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java new file mode 100644 index 00000000..a2d72ebf --- /dev/null +++ b/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 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.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.DirectoryStream; +import java.nio.file.Path; +import java.nio.file.FileSystems; +import javax.script.ScriptException; +import org.testng.annotations.Test; +import javax.script.ScriptEngine; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertEquals; + +/** + * @test + * @bug 8039185 8039403 + * @summary Test for persistent code cache and path handling + * @run testng jdk.nashorn.internal.runtime.CodeStoreAndPathTest + */ + +public class CodeStoreAndPathTest { + + final String code1 = "var code1; var x = 'Hello Script'; var x1 = 'Hello Script'; " + + "var x2 = 'Hello Script'; var x3 = 'Hello Script'; " + + "var x4 = 'Hello Script'; var x5 = 'Hello Script';" + + "var x6 = 'Hello Script'; var x7 = 'Hello Script'; " + + "var x8 = 'Hello Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';" + + "function f() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}" + + "function g() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}" + + "function h() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}" + + "function i() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}"; + final String code2 = "var code2; var x = 'Hello Script'; var x1 = 'Hello Script'; " + + "var x2 = 'Hello Script'; var x3 = 'Hello Script'; " + + "var x4 = 'Hello Script'; var x5 = 'Hello Script';" + + "var x6 = 'Hello Script'; var x7 = 'Hello Script'; " + + "var x8 = 'Hello Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';" + + "function f() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}" + + "function g() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}" + + "function h() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}" + + "function i() {x ='Bye Script'; x1 ='Bye Script'; x2='Bye Script';" + + "x3='Bye Script'; x4='Bye Script'; x5='Bye Script'; x6='Bye Script';" + + "x7='Bye Script'; x8='Bye Script'; var x9 = 'Hello Script'; " + + "var x10 = 'Hello Script';}"; + // Script size < Default minimum size for storing a compiled script class + final String code3 = "var code3; var x = 'Hello Script'; var x1 = 'Hello Script'; "; + final String codeCache = "build/nashorn_code_cache"; + final String oldUserDir = System.getProperty("user.dir"); + + public void checkCompiledScripts(DirectoryStream<Path> stream, int numberOfScripts) throws IOException { + for (Path file : stream) { + numberOfScripts--; + } + stream.close(); + assertEquals(numberOfScripts,0); + } + + @Test + public void pathHandlingTest() throws ScriptException, IOException { + System.setProperty("nashorn.persistent.code.cache", codeCache); + String[] options = new String[]{"--persistent-code-cache"}; + NashornScriptEngineFactory fac = new NashornScriptEngineFactory(); + ScriptEngine e = fac.getScriptEngine(options); + Path expectedCodeCachePath = FileSystems.getDefault().getPath(oldUserDir + File.separator + codeCache); + Path actualCodeCachePath = FileSystems.getDefault().getPath(System.getProperty( + "nashorn.persistent.code.cache")).toAbsolutePath(); + // Check that nashorn code cache is created in current working directory + assertEquals(actualCodeCachePath, expectedCodeCachePath); + // Check that code cache dir exists and it's not empty + File file = new File(actualCodeCachePath.toUri()); + assertFalse(!file.isDirectory(), "No code cache directory was created!"); + assertFalse(file.list().length == 0, "Code cache directory is empty!"); + } + + @Test + public void changeUserDirTest() throws ScriptException, IOException { + System.setProperty("nashorn.persistent.code.cache", codeCache); + String[] options = new String[]{"--persistent-code-cache"}; + NashornScriptEngineFactory fac = new NashornScriptEngineFactory(); + ScriptEngine e = fac.getScriptEngine(options); + Path codeCachePath = FileSystems.getDefault().getPath(System.getProperty( + "nashorn.persistent.code.cache")).toAbsolutePath(); + String newUserDir = "build/newUserDir"; + // Now changing current working directory + System.setProperty("user.dir", System.getProperty("user.dir") + File.separator + newUserDir); + // Check that a new compiled script is stored in exisitng code cache + e.eval(code1); + DirectoryStream<Path> stream = Files.newDirectoryStream(codeCachePath); + // Already one compiled script has been stored in the cache during initialization + checkCompiledScripts(stream, 2); + // Setting to default current working dir + System.setProperty("user.dir", oldUserDir); + } + + @Test + public void codeCacheTest() throws ScriptException, IOException { + System.setProperty("nashorn.persistent.code.cache", codeCache); + String[] options = new String[]{"--persistent-code-cache"}; + NashornScriptEngineFactory fac = new NashornScriptEngineFactory(); + ScriptEngine e = fac.getScriptEngine(options); + Path codeCachePath = FileSystems.getDefault().getPath(System.getProperty( + "nashorn.persistent.code.cache")).toAbsolutePath(); + e.eval(code1); + e.eval(code2); + e.eval(code3);// less than minimum size for storing + // Already one compiled script has been stored in the cache during initialization + // adding code1 and code2. + DirectoryStream<Path> stream = Files.newDirectoryStream(codeCachePath); + checkCompiledScripts(stream, 3); + } +} diff --git a/test/src/jdk/nashorn/internal/runtime/ContextTest.java b/test/src/jdk/nashorn/internal/runtime/ContextTest.java index 1b21c23f..4c4a8c65 100644 --- a/test/src/jdk/nashorn/internal/runtime/ContextTest.java +++ b/test/src/jdk/nashorn/internal/runtime/ContextTest.java @@ -25,10 +25,12 @@ package jdk.nashorn.internal.runtime; +import static jdk.nashorn.internal.runtime.Source.sourceFor; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import java.util.Map; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.options.Options; import org.testng.annotations.Test; @@ -45,7 +47,7 @@ public class ContextTest { final Options options = new Options(""); final ErrorManager errors = new ErrorManager(); final Context cx = new Context(options, errors, Thread.currentThread().getContextClassLoader()); - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); Context.setGlobal(cx.createGlobal()); try { String code = "22 + 10"; @@ -65,7 +67,7 @@ public class ContextTest { final ErrorManager errors = new ErrorManager(); final Context cx = new Context(options, errors, Thread.currentThread().getContextClassLoader()); final boolean strict = cx.getEnv()._strict; - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); Context.setGlobal(cx.createGlobal()); try { @@ -106,7 +108,7 @@ public class ContextTest { } private Object eval(final Context cx, final String name, final String code) { - final Source source = new Source(name, code); + final Source source = sourceFor(name, code); final ScriptObject global = Context.getGlobal(); final ScriptFunction func = cx.compileScript(source, global); return func != null ? ScriptRuntime.apply(func, global) : null; diff --git a/test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java b/test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java new file mode 100644 index 00000000..7b84f5a7 --- /dev/null +++ b/test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 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.PrintStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import static org.testng.Assert.fail; +import org.testng.annotations.Test; + +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; +import javax.script.SimpleScriptContext; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; + +/** + * @test + * @bug 8037378 + * @summary Sanity tests for no persistence caching + * @run testng/othervm jdk.nashorn.internal.runtime.NoPersistenceCachingTest + */ +public class NoPersistenceCachingTest { + + private ScriptEngine engine; + private ScriptContext context1, context2, context3; + private ByteArrayOutputStream stderr; + private PrintStream prevStderr; + + @BeforeTest + public void setupTest() { + stderr = new ByteArrayOutputStream(); + prevStderr = System.err; + System.setErr(new PrintStream(stderr)); + NashornScriptEngineFactory nashornFactory = null; + ScriptEngineManager sm = new ScriptEngineManager(); + for (ScriptEngineFactory fac : sm.getEngineFactories()) { + if (fac instanceof NashornScriptEngineFactory) { + nashornFactory = (NashornScriptEngineFactory) fac; + break; + } + } + if (nashornFactory == null) { + fail("Cannot find nashorn factory!"); + } + String[] options = new String[]{"--log=compiler:finest"}; + engine = nashornFactory.getScriptEngine(options); + context1 = engine.getContext(); + context2 = new SimpleScriptContext(); + context2.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + context3 = new SimpleScriptContext(); + context3.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); + } + + @AfterTest + public void setErrTest() { + System.setErr(prevStderr); + } + + public void runTest(int numberOfContext, String expectedOutputPattern, + int expectedPatternOccurrence) { + + try { + switch (numberOfContext) { + case 2: + String scriptTwoContexts = "print('HelloTwoContexts')"; + engine.eval(scriptTwoContexts, context1); + engine.eval(scriptTwoContexts, context2); + break; + case 3: + String scriptThreeContexts = "print('HelloThreeContexts')"; + engine.eval(scriptThreeContexts, context1); + engine.eval(scriptThreeContexts, context2); + engine.eval(scriptThreeContexts, context3); + break; + } + } catch (final Exception se) { + se.printStackTrace(); + fail(se.getMessage()); + } + Pattern deoptimizing = Pattern.compile(expectedOutputPattern); + Matcher matcher = deoptimizing.matcher(stderr.toString()); + int matches = 0; + while (matcher.find()) { + matches++; + } + if (matches != expectedPatternOccurrence) { + fail("Number of cache hit is not correct, expected: " + + expectedPatternOccurrence + " and found: " + matches + "\n" + + stderr); + } + stderr.reset(); + } + + private static String getCodeCachePattern() { + return ("\\[compiler\\]\\sCode\\scache\\shit\\sfor\\s<eval>\\savoiding\\srecompile."); + } + + @Test + public void twoContextTest() { + runTest(2, getCodeCachePattern(), 1); + + } + + @Test + public void threeContextTest() { + runTest(3, getCodeCachePattern(), 2); + } +} diff --git a/test/src/jdk/nashorn/internal/runtime/SourceTest.java b/test/src/jdk/nashorn/internal/runtime/SourceTest.java new file mode 100644 index 00000000..34635856 --- /dev/null +++ b/test/src/jdk/nashorn/internal/runtime/SourceTest.java @@ -0,0 +1,128 @@ +/* + * 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 jdk.nashorn.api.scripting.URLReader; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.Arrays; + +import static jdk.nashorn.internal.runtime.Source.sourceFor; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/** + * Tests different Source representations. + */ +public class SourceTest { + + final private static String SOURCE_NAME = "source.js"; + final private static String SOURCE_STRING = "var x = 1;"; + final private static char[] SOURCE_CHARS = SOURCE_STRING.toCharArray(); + final private static String RESOURCE_PATH = "resources/load_test.js"; + final private static File SOURCE_FILE = new File("build/test/classes/jdk/nashorn/internal/runtime/" + RESOURCE_PATH); + final private static URL SOURCE_URL = SourceTest.class.getResource(RESOURCE_PATH); + + + @Test + public void testStringSource() { + testSources(sourceFor(SOURCE_NAME, SOURCE_STRING), sourceFor(SOURCE_NAME, SOURCE_STRING)); + testSources(sourceFor(SOURCE_NAME, SOURCE_STRING), sourceFor(SOURCE_NAME, SOURCE_CHARS)); + } + + @Test + public void testCharArraySource() { + testSources(sourceFor(SOURCE_NAME, SOURCE_CHARS), sourceFor(SOURCE_NAME, SOURCE_CHARS)); + testSources(sourceFor(SOURCE_NAME, SOURCE_CHARS), sourceFor(SOURCE_NAME, SOURCE_STRING)); + } + + @Test + public void testURLSource() { + try { + testSources(sourceFor(SOURCE_NAME, SOURCE_URL), sourceFor(SOURCE_NAME, SOURCE_URL)); + testSources(sourceFor(SOURCE_NAME, SOURCE_URL), sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL))); + + } catch (final IOException e) { + fail(e.toString()); + } + } + + @Test + public void testURLReaderSource() { + try { + System.err.println(SourceTest.class.getResource("")); + testSources(sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)), sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL))); + testSources(sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)), sourceFor(SOURCE_NAME, SOURCE_URL)); + } catch (final IOException e) { + fail(e.toString()); + } + } + + @Test + public void testReaderSource() { + try { + testSources(sourceFor(SOURCE_NAME, getReader(RESOURCE_PATH)), sourceFor(SOURCE_NAME, getReader(RESOURCE_PATH))); + } catch (final IOException e) { + fail(e.toString()); + } + } + + @Test + public void testFileSource() { + try { + testSources(sourceFor(SOURCE_NAME, SOURCE_FILE), sourceFor(SOURCE_NAME, SOURCE_FILE)); + } catch (final IOException e) { + fail(e.toString()); + } + } + + private Reader getReader(final String path) { + return new InputStreamReader(SourceTest.class.getResourceAsStream(path)); + } + + private void testSources(final Source source1, final Source source2) { + final char[] chars1 = source1.getContent(); + final char[] chars2 = source2.getContent(); + final String str1 = source1.getString(); + final String str2 = source2.getString(); + assertTrue(Arrays.equals(chars1, chars2)); + assertEquals(str1, str2); + assertEquals(source1.hashCode(), source2.hashCode()); + assertTrue(source1.equals(source2)); + // Test for immutability + Arrays.fill(source1.getContent(), (char)0); + Arrays.fill(source2.getContent(), (char)1); + assertTrue(Arrays.equals(source1.getContent(), str1.toCharArray())); + assertTrue(Arrays.equals(source1.getContent(), chars1)); + assertTrue(Arrays.equals(source1.getContent(), source2.getContent())); + } +} diff --git a/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java b/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java index 469d890e..f6da6f82 100644 --- a/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java +++ b/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java @@ -220,4 +220,19 @@ public class TrustedScriptEngineTest { // bar should be visible in default context assertTrue(e.eval("typeof bar").equals("function")); } + + + @Test public void nashornSwallowsConstKeyword() throws Exception { + final NashornScriptEngineFactory f = new NashornScriptEngineFactory(); + final String[] args = new String[] { "--const-as-var" }; + final ScriptEngine engine = f.getScriptEngine(args); + + final Object ret = engine.eval("" + + "(function() {\n" + + " const x = 10;\n" + + " return x;\n" + + "})();" + ); + assertEquals(ret, 10, "Parsed and executed OK"); + } } diff --git a/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java b/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java index ac2dc172..47ea7b32 100644 --- a/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java +++ b/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java @@ -25,6 +25,7 @@ package jdk.nashorn.internal.test.framework; +import static jdk.nashorn.internal.runtime.Source.sourceFor; import static jdk.nashorn.tools.Shell.COMPILATION_ERROR; import static jdk.nashorn.tools.Shell.RUNTIME_ERROR; import static jdk.nashorn.tools.Shell.SUCCESS; @@ -34,12 +35,11 @@ import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import jdk.nashorn.api.scripting.NashornException; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; -import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.options.Options; /** @@ -110,12 +110,12 @@ public final class SharedContextEvaluator implements ScriptEvaluator { @Override public int run(final OutputStream out, final OutputStream err, final String[] args) throws IOException { - final ScriptObject oldGlobal = Context.getGlobal(); + final Global oldGlobal = Context.getGlobal(); try { ctxOut.setDelegatee(out); ctxErr.setDelegatee(err); final ErrorManager errors = context.getErrorManager(); - final ScriptObject global = context.createGlobal(); + final Global global = context.createGlobal(); Context.setGlobal(global); // For each file on the command line. @@ -125,7 +125,7 @@ public final class SharedContextEvaluator implements ScriptEvaluator { continue; } final File file = new File(fileName); - ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global); + ScriptFunction script = context.compileScript(sourceFor(fileName, file.toURI().toURL()), global); if (script == null || errors.getNumberOfErrors() != 0) { return COMPILATION_ERROR; diff --git a/test/src/jdk/nashorn/test/models/IntFloatOverloadSelection.java b/test/src/jdk/nashorn/test/models/IntFloatOverloadSelection.java new file mode 100644 index 00000000..947896b3 --- /dev/null +++ b/test/src/jdk/nashorn/test/models/IntFloatOverloadSelection.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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.test.models; + +public class IntFloatOverloadSelection { + + public static String overloadedMethod(int i) { + return "int"; + } + + public static String overloadedMethod(float f) { + return "float"; + } +} diff --git a/test/src/jdk/nashorn/test/models/SourceHelper.java b/test/src/jdk/nashorn/test/models/SourceHelper.java index 46b1e488..7e90f304 100644 --- a/test/src/jdk/nashorn/test/models/SourceHelper.java +++ b/test/src/jdk/nashorn/test/models/SourceHelper.java @@ -46,7 +46,7 @@ public final class SourceHelper { } public static String readFully(final URL url) throws IOException { - return new Source(url.toString(), url).getString(); + return Source.sourceFor(url.toString(), url).getString(); } public static String readFully(final Reader reader) throws IOException { |