diff --git a/rhino/src/main/java/org/mozilla/javascript/ArrowFunction.java b/rhino/src/main/java/org/mozilla/javascript/ArrowFunction.java index 45044f8856..c29c881f41 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ArrowFunction.java +++ b/rhino/src/main/java/org/mozilla/javascript/ArrowFunction.java @@ -15,11 +15,17 @@ public class ArrowFunction extends BaseFunction { private final Callable targetFunction; private final Scriptable boundThis; + private final Scriptable boundHomeObject; public ArrowFunction( - Context cx, Scriptable scope, Callable targetFunction, Scriptable boundThis) { + Context cx, + Scriptable scope, + Callable targetFunction, + Scriptable boundThis, + Scriptable boundHomeObject) { this.targetFunction = targetFunction; this.boundThis = boundThis; + this.boundHomeObject = boundHomeObject; ScriptRuntime.setFunctionProtoAndParent(this, cx, scope, false); @@ -80,6 +86,10 @@ Scriptable getCallThis(Context cx) { return boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx); } + Scriptable getBoundHomeObject() { + return this.boundHomeObject; + } + Callable getTargetFunction() { return targetFunction; } diff --git a/rhino/src/main/java/org/mozilla/javascript/BaseFunction.java b/rhino/src/main/java/org/mozilla/javascript/BaseFunction.java index 5229ac4d7e..a270b32507 100644 --- a/rhino/src/main/java/org/mozilla/javascript/BaseFunction.java +++ b/rhino/src/main/java/org/mozilla/javascript/BaseFunction.java @@ -656,6 +656,14 @@ protected int findPrototypeId(String s) { return id; } + public void setHomeObject(Scriptable homeObject) { + this.homeObject = homeObject; + } + + public Scriptable getHomeObject() { + return homeObject; + } + private static final int Id_constructor = 1, Id_toString = 2, Id_toSource = 3, @@ -668,6 +676,7 @@ protected int findPrototypeId(String s) { private Object argumentsObj = NOT_FOUND; private String nameValue = null; private boolean isGeneratorFunction = false; + private Scriptable homeObject = null; // For function object instances, attributes are // {configurable:false, enumerable:false}; diff --git a/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java b/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java index e9b70da565..cc6e9907ea 100644 --- a/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java +++ b/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java @@ -566,6 +566,9 @@ private void visitExpression(Node node, int contextFlags) { throw Kit.codeBug(); } addIndexOp(Icode_CLOSURE_EXPR, fnIndex); + if (fn.isMethodDefinition()) { + addIcode(ICode_FN_STORE_HOME_OBJECT); + } stackChange(1); } break; @@ -626,6 +629,8 @@ private void visitExpression(Node node, int contextFlags) { addUint8(callType); addUint8(type == Token.NEW ? 1 : 0); addUint16(lineNumber & 0xFFFF); + } else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + addIndexOp(Icode_CALL_ON_SUPER, argCount); } else { // Only use the tail call optimization if we're not in a try // or we're not generating debug info (since the @@ -718,6 +723,10 @@ private void visitExpression(Node node, int contextFlags) { addIcode(Icode_POP); addStringOp(Token.NAME, "undefined"); resolveForwardGoto(afterLabel); + } else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + addStringOp( + type == Token.GETPROP ? Token.GETPROP_SUPER : Token.GETPROPNOWARN_SUPER, + child.getString()); } else { addStringOp(type, child.getString()); } @@ -757,6 +766,10 @@ private void visitExpression(Node node, int contextFlags) { addIcode(Icode_POP); addStringOp(Token.NAME, "undefined"); resolveForwardGoto(afterLabel); + } else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + visitExpression(child, 0); + addToken(Token.GETELEM_SUPER); + stackChange(-1); } else { finishGetElemGeneration(child); } @@ -843,7 +856,11 @@ private void visitExpression(Node node, int contextFlags) { stackChange(-1); } visitExpression(child, 0); - addStringOp(Token.SETPROP, property); + addStringOp( + node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1 + ? Token.SETPROP_SUPER + : Token.SETPROP, + property); stackChange(-1); } break; @@ -863,7 +880,10 @@ private void visitExpression(Node node, int contextFlags) { stackChange(-1); } visitExpression(child, 0); - addToken(Token.SETELEM); + addToken( + node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1 + ? Token.SETELEM_SUPER + : Token.SETELEM); stackChange(-2); break; @@ -996,6 +1016,7 @@ private void visitExpression(Node node, int contextFlags) { case Token.NULL: case Token.THIS: + case Token.SUPER: case Token.THISFN: case Token.FALSE: case Token.TRUE: diff --git a/rhino/src/main/java/org/mozilla/javascript/Context.java b/rhino/src/main/java/org/mozilla/javascript/Context.java index ca9e460a92..b91f202719 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Context.java +++ b/rhino/src/main/java/org/mozilla/javascript/Context.java @@ -2524,7 +2524,8 @@ private ScriptNode parse( } } - IRFactory irf = new IRFactory(compilerEnv, sourceString, compilationErrorReporter); + IRFactory irf = + new IRFactory(compilerEnv, sourceName, sourceString, compilationErrorReporter); ScriptNode tree = irf.transformTree(ast); if (compilerEnv.isGeneratingSource()) { diff --git a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java index 3902f95bf3..d358350245 100644 --- a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java +++ b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java @@ -95,13 +95,24 @@ public final class IRFactory { private AstNodePosition astNodePos; public IRFactory(CompilerEnvirons env, String sourceString) { - this(env, sourceString, env.getErrorReporter()); + this(env, null, sourceString, env.getErrorReporter()); } + /** Use {@link #IRFactory(CompilerEnvirons, String, String, ErrorReporter)} */ + @Deprecated public IRFactory(CompilerEnvirons env, String sourceString, ErrorReporter errorReporter) { + this(env, null, sourceString, errorReporter); + } + + public IRFactory( + CompilerEnvirons env, + String sourceName, + String sourceString, + ErrorReporter errorReporter) { parser = new Parser(env, errorReporter); astNodePos = new AstNodePosition(sourceString); parser.currentPos = astNodePos; + parser.setSourceURI(sourceName); } /** Transforms the tree into a lower-level IR suitable for codegen. */ @@ -180,7 +191,9 @@ private Node transform(AstNode node) { case Token.NULL: case Token.DEBUGGER: return transformLiteral(node); - + case Token.SUPER: + parser.setRequiresActivation(); + return transformLiteral(node); case Token.NAME: return transformName((Name) node); case Token.NUMBER: @@ -533,6 +546,9 @@ private Node transformElementGet(ElementGet node) { if (node.type == Token.QUESTION_DOT) { getElem.putIntProp(Node.OPTIONAL_CHAINING, 1); } + if (target.getType() == Token.SUPER) { + getElem.putIntProp(Node.SUPER_PROPERTY_ACCESS, 1); + } return getElem; } @@ -660,17 +676,26 @@ private Node transformFunction(FunctionNode fn) { } private Node transformFunctionCall(FunctionCall node) { - Node call = createCallOrNew(Token.CALL, transform(node.getTarget())); - call.setLineColumnNumber(node.getLineno(), node.getColumn()); - List args = node.getArguments(); - for (int i = 0; i < args.size(); i++) { - AstNode arg = args.get(i); - call.addChildToBack(transform(arg)); - } - if (node.isOptionalCall()) { - call.putIntProp(Node.OPTIONAL_CHAINING, 1); + astNodePos.push(node); + try { + Node transformedTarget = transform(node.getTarget()); + Node call = createCallOrNew(Token.CALL, transformedTarget); + call.setLineColumnNumber(node.getLineno(), node.getColumn()); + List args = node.getArguments(); + for (int i = 0; i < args.size(); i++) { + AstNode arg = args.get(i); + call.addChildToBack(transform(arg)); + } + if (node.isOptionalCall()) { + call.putIntProp(Node.OPTIONAL_CHAINING, 1); + } + if (transformedTarget.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + call.putIntProp(Node.SUPER_PROPERTY_ACCESS, 1); + } + return call; + } finally { + astNodePos.pop(); } - return call; } private Node transformGenExpr(GeneratorExpression node) { @@ -863,6 +888,11 @@ private Node transformLetNode(LetNode node) { } private Node transformLiteral(AstNode node) { + // Trying to call super as a function. See 15.4.2 Static Semantics: HasDirectSuper + if (node.getParent() instanceof FunctionCall + && node.getType() == Token.SUPER + && parser.currentScriptOrFn.isMethodDefinition()) + parser.reportError("msg.super.shorthand.function"); return node; } @@ -969,8 +999,12 @@ private Node transformTemplateLiteral(TemplateLiteral node) { } private Node transformTemplateLiteralCall(TaggedTemplateLiteral node) { - Node call = createCallOrNew(Token.CALL, transform(node.getTarget())); + Node transformedTarget = transform(node.getTarget()); + Node call = createCallOrNew(Token.CALL, transformedTarget); call.setLineColumnNumber(node.getLineno(), node.getColumn()); + if (transformedTarget.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + call.putIntProp(Node.SUPER_PROPERTY_ACCESS, 1); + } TemplateLiteral templateLiteral = (TemplateLiteral) node.getTemplateLiteral(); List elems = templateLiteral.getElements(); call.addChildToBack(templateLiteral); @@ -1937,6 +1971,9 @@ private Node createPropertyGet( ref.putIntProp(Node.OPTIONAL_CHAINING, 1); getRef.putIntProp(Node.OPTIONAL_CHAINING, 1); } + if (target.getType() == Token.SUPER) { + getRef.putIntProp(Node.SUPER_PROPERTY_ACCESS, 1); + } return getRef; } @@ -1944,6 +1981,9 @@ private Node createPropertyGet( if (type == Token.QUESTION_DOT) { node.putIntProp(Node.OPTIONAL_CHAINING, 1); } + if (target.getType() == Token.SUPER) { + node.putIntProp(Node.SUPER_PROPERTY_ACCESS, 1); + } return node; } Node elem = Node.newString(name); @@ -2147,7 +2187,9 @@ private Node createAssignment(int assignType, Node left, Node right) { int assignOp; switch (assignType) { case Token.ASSIGN: - return parser.simpleAssignment(left, right); + { + return propagateSuperFromLhs(parser.simpleAssignment(left, right), left); + } case Token.ASSIGN_BITOR: assignOp = Token.BITOR; break; @@ -2203,7 +2245,7 @@ private Node createAssignment(int assignType, Node left, Node right) { { Node op = new Node(assignOp, left, right); Node lvalueLeft = Node.newString(Token.BINDNAME, left.getString()); - return new Node(Token.SETNAME, lvalueLeft, op); + return propagateSuperFromLhs(new Node(Token.SETNAME, lvalueLeft, op), left); } case Token.GETPROP: case Token.GETELEM: @@ -2215,7 +2257,7 @@ private Node createAssignment(int assignType, Node left, Node right) { Node opLeft = new Node(Token.USE_STACK); Node op = new Node(assignOp, opLeft, right); - return new Node(type, obj, id, op); + return propagateSuperFromLhs(new Node(type, obj, id, op), left); } case Token.GET_REF: { @@ -2223,13 +2265,20 @@ private Node createAssignment(int assignType, Node left, Node right) { parser.checkMutableReference(ref); Node opLeft = new Node(Token.USE_STACK); Node op = new Node(assignOp, opLeft, right); - return new Node(Token.SET_REF_OP, ref, op); + return propagateSuperFromLhs(new Node(Token.SET_REF_OP, ref, op), left); } } throw Kit.codeBug(); } + private Node propagateSuperFromLhs(Node result, Node left) { + if (left.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + result.putIntProp(Node.SUPER_PROPERTY_ACCESS, 1); + } + return result; + } + private static Node createUseLocal(Node localBlock) { if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug(); Node result = new Node(Token.LOCAL_LOAD); diff --git a/rhino/src/main/java/org/mozilla/javascript/Icode.java b/rhino/src/main/java/org/mozilla/javascript/Icode.java index f835ae2197..8e87db775f 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Icode.java +++ b/rhino/src/main/java/org/mozilla/javascript/Icode.java @@ -78,9 +78,10 @@ abstract class Icode { Icode_LITERAL_NEW_OBJECT = Icode_INTNUMBER - 1, Icode_LITERAL_NEW_ARRAY = Icode_LITERAL_NEW_OBJECT - 1, Icode_LITERAL_SET = Icode_LITERAL_NEW_ARRAY - 1, + ICode_FN_STORE_HOME_OBJECT = Icode_LITERAL_SET - 1, // Array literal with skipped index like [1,,2] - Icode_SPARE_ARRAYLIT = Icode_LITERAL_SET - 1, + Icode_SPARE_ARRAYLIT = ICode_FN_STORE_HOME_OBJECT - 1, // Load index register to prepare for the following index operation Icode_REG_IND_C0 = Icode_SPARE_ARRAYLIT - 1, @@ -151,8 +152,12 @@ abstract class Icode { // Jump if stack head is null or undefined Icode_IF_NULL_UNDEF = Icode_LITERAL_KEY_SET - 1, Icode_IF_NOT_NULL_UNDEF = Icode_IF_NULL_UNDEF - 1, + + // Call a method on the super object, i.e. super.foo() + Icode_CALL_ON_SUPER = Icode_IF_NOT_NULL_UNDEF - 1, + // Last icode - MIN_ICODE = Icode_IF_NOT_NULL_UNDEF; + MIN_ICODE = Icode_CALL_ON_SUPER; static String bytecodeName(int bytecode) { if (!validBytecode(bytecode)) { @@ -240,6 +245,8 @@ static String bytecodeName(int bytecode) { return "LITERAL_NEW_ARRAY"; case Icode_LITERAL_SET: return "LITERAL_SET"; + case ICode_FN_STORE_HOME_OBJECT: + return "FN_STORE_HOME_OBJECT"; case Icode_SPARE_ARRAYLIT: return "SPARE_ARRAYLIT"; case Icode_REG_IND_C0: @@ -334,6 +341,8 @@ static String bytecodeName(int bytecode) { return "IF_NULL_UNDEF"; case Icode_IF_NOT_NULL_UNDEF: return "IF_NOT_NULL_UNDEF"; + case Icode_CALL_ON_SUPER: + return "CALL_ON_SUPER"; } // icode without name diff --git a/rhino/src/main/java/org/mozilla/javascript/Interpreter.java b/rhino/src/main/java/org/mozilla/javascript/Interpreter.java index ff9a9efc41..25ac7db4d8 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Interpreter.java +++ b/rhino/src/main/java/org/mozilla/javascript/Interpreter.java @@ -121,7 +121,8 @@ void initializeArgs( Object[] args, double[] argsDbl, int argShift, - int argCount) { + int argCount, + Scriptable homeObject) { if (useActivation) { // Copy args to new array to pass to enterActivationFunction // or debuggerFrame.onEnter @@ -144,7 +145,8 @@ void initializeArgs( scope, args, idata.isStrict, - idata.argsHasRest); + idata.argsHasRest, + homeObject); } else { scope = ScriptRuntime.createFunctionActivation( @@ -153,7 +155,8 @@ void initializeArgs( scope, args, idata.isStrict, - idata.argsHasRest); + idata.argsHasRest, + homeObject); } } } else { @@ -609,6 +612,7 @@ static void dumpICode(InterpreterData idata) { out.println(tname + " " + idata.itsNestedFunctions[indexReg]); break; case Token.CALL: + case Icode_CALL_ON_SUPER: case Icode_TAIL_CALL: case Token.REF_CALL: case Token.NEW: @@ -1132,7 +1136,18 @@ static Object interpret( } } - CallFrame frame = initFrame(cx, scope, thisObj, args, null, 0, args.length, ifun, null); + CallFrame frame = + initFrame( + cx, + scope, + thisObj, + ifun.getHomeObject(), + args, + null, + 0, + args.length, + ifun, + null); frame.isContinuationsTopFrame = cx.isContinuationsTopCall; cx.isContinuationsTopCall = false; @@ -1648,6 +1663,21 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl lhs, stringReg, cx, frame.scope); continue Loop; } + case Token.GETPROP_SUPER: + case Token.GETPROPNOWARN_SUPER: + { + Object superObject = stack[stackTop]; + if (superObject == DBL_MRK) Kit.codeBug(); + stack[stackTop] = + ScriptRuntime.getSuperProp( + superObject, + stringReg, + cx, + frame.scope, + frame.thisObj, + op == Token.GETPROPNOWARN_SUPER); + continue Loop; + } case Token.SETPROP: { Object rhs = stack[stackTop]; @@ -1662,6 +1692,24 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl lhs, stringReg, rhs, cx, frame.scope); continue Loop; } + case Token.SETPROP_SUPER: + { + Object rhs = stack[stackTop]; + if (rhs == DBL_MRK) + rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object superObject = stack[stackTop]; + if (superObject == DBL_MRK) Kit.codeBug(); + stack[stackTop] = + ScriptRuntime.setSuperProp( + superObject, + stringReg, + rhs, + cx, + frame.scope, + frame.thisObj); + continue Loop; + } case Icode_PROP_INC_DEC: { Object lhs = stack[stackTop]; @@ -1682,11 +1730,21 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl stackTop = doGetElem(cx, frame, stack, sDbl, stackTop); continue Loop; } + case Token.GETELEM_SUPER: + { + stackTop = doGetElemSuper(cx, frame, stack, sDbl, stackTop); + continue Loop; + } case Token.SETELEM: { stackTop = doSetElem(cx, frame, stack, sDbl, stackTop); continue Loop; } + case Token.SETELEM_SUPER: + { + stackTop = doSetElemSuper(cx, frame, stack, sDbl, stackTop); + continue Loop; + } case Icode_ELEM_INC_DEC: { stackTop = @@ -1853,6 +1911,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl continue Loop; } case Token.CALL: + case Icode_CALL_ON_SUPER: case Icode_TAIL_CALL: case Token.REF_CALL: { @@ -1867,6 +1926,18 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl // are already Scriptable and Callable objects respectively Callable fun = (Callable) stack[stackTop]; Scriptable funThisObj = (Scriptable) stack[stackTop + 1]; + Scriptable funHomeObj = + (fun instanceof BaseFunction) + ? ((BaseFunction) fun).getHomeObject() + : null; + if (op == Icode_CALL_ON_SUPER) { + // funThisObj would have been the "super" object, which we + // used to lookup the function. Now that that's done, we + // discard it and invoke the function with the current + // "this". + funThisObj = frame.thisObj; + } + if (op == Token.REF_CALL) { Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, indexReg); @@ -1893,6 +1964,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl ArrowFunction afun = (ArrowFunction) fun; fun = afun.getTargetFunction(); funThisObj = afun.getCallThis(cx); + funHomeObj = afun.getBoundHomeObject(); } else if (fun instanceof LambdaConstructor) { break; } else if (fun instanceof LambdaFunction) { @@ -2046,6 +2118,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl cx, calleeScope, funThisObj, + funHomeObj, stack, sDbl, stackTop + 2, @@ -2125,6 +2198,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl cx, frame.scope, newInstance, + newInstance, stack, sDbl, stackTop + 1, @@ -2273,6 +2347,28 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl case Token.THIS: stack[++stackTop] = frame.thisObj; continue Loop; + case Token.SUPER: + { + // See 9.1.1.3.5 GetSuperBase + + // If we are referring to "super", then we always have an + // activation + // (this is done in IrFactory). The home object is stored as + // part of the + // activation frame to propagate it correctly for nested + // functions. + Scriptable homeObject = getCurrentFrameHomeObject(frame); + if (homeObject == null) { + // This if is specified in the spec, but I cannot imagine + // how the home object will ever be null since `super` is + // legal _only_ in method definitions, where we do have a + // home object! + stack[++stackTop] = Undefined.instance; + } else { + stack[++stackTop] = homeObject.getPrototype(); + } + continue Loop; + } case Token.THISFN: stack[++stackTop] = frame.fnOrScript; continue Loop; @@ -2411,12 +2507,23 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl InterpretedFunction.createFunction( cx, frame.scope, frame.fnOrScript, indexReg); if (fn.idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) { + Scriptable homeObject = getCurrentFrameHomeObject(frame); stack[++stackTop] = - new ArrowFunction(cx, frame.scope, fn, frame.thisObj); + new ArrowFunction( + cx, frame.scope, fn, frame.thisObj, homeObject); } else { stack[++stackTop] = fn; } continue Loop; + case ICode_FN_STORE_HOME_OBJECT: + { + // Stack contains: [object, keysArray, flagsArray, valuesArray, + // function] + InterpretedFunction fun = (InterpretedFunction) stack[stackTop]; + Scriptable homeObject = (Scriptable) stack[stackTop - 4]; + fun.setHomeObject(homeObject); + continue Loop; + } case Icode_CLOSURE_STMT: initFunction(cx, frame.scope, frame.fnOrScript, indexReg); continue Loop; @@ -2866,6 +2973,14 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl : ScriptRuntime.wrapNumber(interpreterResultDbl); } + private static Scriptable getCurrentFrameHomeObject(CallFrame frame) { + if (frame.scope instanceof NativeCall) { + return ((NativeCall) frame.scope).getHomeObject(); + } else { + return null; + } + } + private static int doInOrInstanceof( Context cx, int op, Object[] stack, double[] sDbl, int stackTop) { Object rhs = stack[stackTop]; @@ -2988,6 +3103,23 @@ private static int doGetElem( return stackTop; } + private static int doGetElemSuper( + Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop) { + --stackTop; + Object superObject = stack[stackTop]; + if (superObject == DOUBLE_MARK) Kit.codeBug(); + Object value; + Object id = stack[stackTop + 1]; + if (id != DOUBLE_MARK) { + value = ScriptRuntime.getSuperElem(superObject, id, cx, frame.scope, frame.thisObj); + } else { + double d = sDbl[stackTop + 1]; + value = ScriptRuntime.getSuperIndex(superObject, d, cx, frame.scope, frame.thisObj); + } + stack[stackTop] = value; + return stackTop; + } + private static int doSetElem( Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop) { stackTop -= 2; @@ -3011,6 +3143,31 @@ private static int doSetElem( return stackTop; } + private static int doSetElemSuper( + Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop) { + stackTop -= 2; + Object rhs = stack[stackTop + 2]; + if (rhs == DOUBLE_MARK) { + rhs = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]); + } + Object superObject = stack[stackTop]; + if (superObject == DOUBLE_MARK) Kit.codeBug(); + Object value; + Object id = stack[stackTop + 1]; + if (id != DOUBLE_MARK) { + value = + ScriptRuntime.setSuperElem( + superObject, id, rhs, cx, frame.scope, frame.thisObj); + } else { + double d = sDbl[stackTop + 1]; + value = + ScriptRuntime.setSuperIndex( + superObject, d, rhs, cx, frame.scope, frame.thisObj); + } + stack[stackTop] = value; + return stackTop; + } + private static int doElemIncDec( Context cx, CallFrame frame, @@ -3487,6 +3644,7 @@ private static CallFrame initFrame( Context cx, Scriptable callerScope, Scriptable thisObj, + Scriptable homeObj, Object[] args, double[] argsDbl, int argShift, @@ -3494,7 +3652,7 @@ private static CallFrame initFrame( InterpretedFunction fnOrScript, CallFrame parentFrame) { CallFrame frame = new CallFrame(cx, thisObj, fnOrScript, parentFrame); - frame.initializeArgs(cx, callerScope, args, argsDbl, argShift, argCount); + frame.initializeArgs(cx, callerScope, args, argsDbl, argShift, argCount, homeObj); enterFrame(cx, frame, args, false); return frame; } @@ -3582,7 +3740,7 @@ private static void exitFrame(Context cx, CallFrame frame, Object throwable) { } private static void setCallResult(CallFrame frame, Object callResult, double callResultDbl) { - if (frame.savedCallOp == Token.CALL) { + if (frame.savedCallOp == Token.CALL || frame.savedCallOp == Icode_CALL_ON_SUPER) { frame.stack[frame.savedStackTop] = callResult; frame.sDbl[frame.savedStackTop] = callResultDbl; } else if (frame.savedCallOp == Token.NEW) { @@ -3621,7 +3779,7 @@ private static NativeContinuation captureContinuation( x.stack[i] = null; x.stackAttributes[i] = ScriptableObject.EMPTY; } - if (x.savedCallOp == Token.CALL) { + if (x.savedCallOp == Token.CALL || x.savedCallOp == Icode_CALL_ON_SUPER) { // the call will always overwrite the stack top with the result x.stack[x.savedStackTop] = null; } else { diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeCall.java b/rhino/src/main/java/org/mozilla/javascript/NativeCall.java index d8474761c3..2b1f0f1a06 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeCall.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeCall.java @@ -33,8 +33,11 @@ static void init(Scriptable scope, boolean sealed) { Object[] args, boolean isArrow, boolean isStrict, - boolean argsHasRest) { + boolean argsHasRest, + Scriptable homeObject) { this.function = function; + this.homeObject = homeObject; + this.isArrow = isArrow; setParentScope(scope); // leave prototype null @@ -143,12 +146,18 @@ public void defineAttributesForArguments() { } } + public Scriptable getHomeObject() { + return homeObject; + } + private static final int Id_constructor = 1, MAX_PROTOTYPE_ID = 1; NativeFunction function; Object[] originalArgs; boolean isStrict; private Arguments arguments; + boolean isArrow; + private Scriptable homeObject; transient NativeCall parentActivationCall; } diff --git a/rhino/src/main/java/org/mozilla/javascript/Node.java b/rhino/src/main/java/org/mozilla/javascript/Node.java index 928426fef5..0b3f7ca4fc 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Node.java +++ b/rhino/src/main/java/org/mozilla/javascript/Node.java @@ -67,7 +67,8 @@ public class Node implements Iterable { TRAILING_COMMA = 28, OBJECT_LITERAL_DESTRUCTURING = 29, OPTIONAL_CHAINING = 30, - LAST_PROP = OPTIONAL_CHAINING; + SUPER_PROPERTY_ACCESS = 31, + LAST_PROP = SUPER_PROPERTY_ACCESS; // values of ISNUMBER_PROP to specify // which of the children are Number types @@ -438,6 +439,8 @@ private static final String propToString(int propType) { return "trailing comma"; case OPTIONAL_CHAINING: return "optional_chaining"; + case SUPER_PROPERTY_ACCESS: + return "super_property_access"; default: Kit.codeBug(); diff --git a/rhino/src/main/java/org/mozilla/javascript/Parser.java b/rhino/src/main/java/org/mozilla/javascript/Parser.java index 2a1fe44277..b10f529b78 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Parser.java +++ b/rhino/src/main/java/org/mozilla/javascript/Parser.java @@ -140,6 +140,7 @@ public class Parser { // The following are per function variables and should be saved/restored // during function parsing. See PerFunctionVariables class below. ScriptNode currentScriptOrFn; + private boolean insideMethod; Scope currentScope; private int endFlags; private boolean inForInit; // bound temporarily during forStatement() @@ -919,6 +920,10 @@ private void parseFunctionParams(FunctionNode fnNode) throws IOException { } private FunctionNode function(int type) throws IOException { + return function(type, false); + } + + private FunctionNode function(int type, boolean isMethodDefiniton) throws IOException { boolean isGenerator = false; int syntheticType = type; int baseLineno = lineNumber(); // line number where source starts @@ -976,6 +981,7 @@ private FunctionNode function(int type) throws IOException { } FunctionNode fnNode = new FunctionNode(functionSourceStart, name); + fnNode.setMethodDefinition(isMethodDefiniton); fnNode.setFunctionType(type); if (isGenerator) { fnNode.setIsES6Generator(); @@ -985,6 +991,8 @@ private FunctionNode function(int type) throws IOException { fnNode.setJsDocNode(getAndResetJsDoc()); PerFunctionVariables savedVars = new PerFunctionVariables(fnNode); + boolean wasInsideMethod = insideMethod; + insideMethod = isMethodDefiniton; try { parseFunctionParams(fnNode); AstNode body = parseFunctionBody(type, fnNode); @@ -1002,6 +1010,7 @@ private FunctionNode function(int type) throws IOException { } } finally { savedVars.restore(); + insideMethod = wasInsideMethod; } if (memberExprNode != null) { @@ -1050,6 +1059,8 @@ private AstNode arrowFunction(AstNode params, int startLine, int startColumn) Set paramNames = new HashSet<>(); PerFunctionVariables savedVars = new PerFunctionVariables(fnNode); + // Intentionally not overwriting "insideMethod" - we want to propagate this from the parent + // function or scope try { if (params instanceof ParenthesizedExpression) { fnNode.setParens(0, params.getLength()); @@ -3050,6 +3061,11 @@ private AstNode taggedTemplateLiteral(AstNode pn) throws IOException { */ private AstNode propertyAccess(int tt, AstNode pn, boolean isOptionalChain) throws IOException { if (pn == null) codeBug(); + if (pn.getType() == Token.SUPER && isOptionalChain) { + reportError("msg.optional.super"); + return makeErrorNode(); + } + int memberTypeFlags = 0, lineno = lineNumber(), dotPos = ts.tokenBeg, @@ -3364,12 +3380,27 @@ private AstNode primaryExpr() throws IOException { case Token.THIS: case Token.FALSE: case Token.TRUE: - consumeToken(); - pos = ts.tokenBeg; - end = ts.tokenEnd; - KeywordLiteral keywordLiteral = new KeywordLiteral(pos, end - pos, tt); - keywordLiteral.setLineColumnNumber(lineNumber(), columnNumber()); - return keywordLiteral; + { + consumeToken(); + pos = ts.tokenBeg; + end = ts.tokenEnd; + KeywordLiteral keywordLiteral = new KeywordLiteral(pos, end - pos, tt); + keywordLiteral.setLineColumnNumber(lineNumber(), columnNumber()); + return keywordLiteral; + } + + case Token.SUPER: + if (insideFunction() && insideMethod) { + consumeToken(); + pos = ts.tokenBeg; + end = ts.tokenEnd; + KeywordLiteral keywordLiteral = new KeywordLiteral(pos, end - pos, tt); + keywordLiteral.setLineColumnNumber(lineNumber(), columnNumber()); + return keywordLiteral; + } else { + reportError("msg.super.shorthand.function"); + } + break; case Token.TEMPLATE_LITERAL: consumeToken(); @@ -3795,6 +3826,7 @@ private ObjectLiteral objectLiteral() throws IOException { propertyName = null; } else { propertyName = ts.getString(); + // short-hand method definition ObjectProperty objectProp = methodDefinition(ppos, pname, entryKind); pname.setJsDocNode(jsdocNode); elems.add(objectProp); @@ -3944,7 +3976,7 @@ private ObjectProperty plainProperty(AstNode property, int ptt) throws IOExcepti private ObjectProperty methodDefinition(int pos, AstNode propName, int entryKind) throws IOException { - FunctionNode fn = function(FunctionNode.FUNCTION_EXPRESSION); + FunctionNode fn = function(FunctionNode.FUNCTION_EXPRESSION, true); // We've already parsed the function name, so fn should be anonymous. Name name = fn.getFunctionName(); if (name != null && name.length() != 0) { @@ -4759,6 +4791,10 @@ public void reportErrorsIfExists(int baseLineno) { } } + public void setSourceURI(String sourceURI) { + this.sourceURI = sourceURI; + } + public interface CurrentPositionReporter { public int getPosition(); diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 2fe205e15c..0c15dbb405 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -1722,15 +1722,11 @@ public static Object getObjectElem(Object obj, Object elem, Context cx) { /** Call obj.[[Get]](id) */ public static Object getObjectElem(Object obj, Object elem, Context cx, Scriptable scope) { - Scriptable sobj = toObjectOrNull(cx, obj, scope); - if (sobj == null) { - throw undefReadError(obj, elem); - } + Scriptable sobj = asScriptableOrThrowUndefReadError(cx, scope, obj, elem); return getObjectElem(sobj, elem, cx); } public static Object getObjectElem(Scriptable obj, Object elem, Context cx) { - Object result; if (obj instanceof XMLObject) { @@ -1750,7 +1746,40 @@ public static Object getObjectElem(Scriptable obj, Object elem, Context cx) { if (result == Scriptable.NOT_FOUND) { result = Undefined.instance; } + return result; + } + + public static Object getSuperElem( + Object superObject, Object elem, Context cx, Scriptable scope, Object thisObject) { + Scriptable superScriptable = + asScriptableOrThrowUndefReadError(cx, scope, superObject, elem); + Scriptable thisScriptable = asScriptableOrThrowUndefReadError(cx, scope, thisObject, elem); + return getSuperElem(elem, superScriptable, thisScriptable); + } + + public static Object getSuperElem( + Object elem, Scriptable superScriptable, Scriptable thisScriptable) { + Object result; + // No XML support for super + if (isSymbol(elem)) { + result = + ScriptableObject.getSuperProperty( + superScriptable, thisScriptable, (Symbol) elem); + } else { + StringIdOrIndex s = toStringIdOrIndex(elem); + if (s.stringId == null) { + int index = s.index; + result = ScriptableObject.getSuperProperty(superScriptable, thisScriptable, index); + } else { + result = + ScriptableObject.getSuperProperty( + superScriptable, thisScriptable, s.stringId); + } + } + if (result == Scriptable.NOT_FOUND) { + result = Undefined.instance; + } return result; } @@ -1770,15 +1799,11 @@ public static Object getObjectProp(Object obj, String property, Context cx) { * @param scope the scope that should be used to resolve primitive prototype */ public static Object getObjectProp(Object obj, String property, Context cx, Scriptable scope) { - Scriptable sobj = toObjectOrNull(cx, obj, scope); - if (sobj == null) { - throw undefReadError(obj, property); - } + Scriptable sobj = asScriptableOrThrowUndefReadError(cx, scope, obj, property); return getObjectProp(sobj, property, cx); } public static Object getObjectProp(Scriptable obj, String property, Context cx) { - Object result = ScriptableObject.getProperty(obj, property); if (result == Scriptable.NOT_FOUND) { if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) { @@ -1801,10 +1826,7 @@ public static Object getObjectPropNoWarn(Object obj, String property, Context cx public static Object getObjectPropNoWarn( Object obj, String property, Context cx, Scriptable scope) { - Scriptable sobj = toObjectOrNull(cx, obj, scope); - if (sobj == null) { - throw undefReadError(obj, property); - } + Scriptable sobj = asScriptableOrThrowUndefReadError(cx, scope, obj, property); Object result = ScriptableObject.getProperty(sobj, property); if (result == Scriptable.NOT_FOUND) { return Undefined.instance; @@ -1812,6 +1834,42 @@ public static Object getObjectPropNoWarn( return result; } + public static Object getSuperProp( + Object superObject, + String property, + Context cx, + Scriptable scope, + Object thisObject, + boolean noWarn) { + Scriptable superScriptable = + asScriptableOrThrowUndefReadError(cx, scope, superObject, property); + Scriptable thisScriptable = + asScriptableOrThrowUndefReadError(cx, scope, thisObject, property); + return getSuperProp(superScriptable, thisScriptable, property, cx, noWarn); + } + + private static Object getSuperProp( + Scriptable superScriptable, + Scriptable thisScriptable, + String property, + Context cx, + boolean noWarn) { + Object result = + ScriptableObject.getSuperProperty(superScriptable, thisScriptable, property); + + if (result == Scriptable.NOT_FOUND) { + if (noWarn) { + return Undefined.instance; + } + if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) { + Context.reportWarning( + ScriptRuntime.getMessageById("msg.ref.undefined.prop", property)); + } + return Undefined.instance; + } + return result; + } + /** * A cheaper and less general version of the above for well-known argument types. * @@ -1824,10 +1882,7 @@ public static Object getObjectIndex(Object obj, double dblIndex, Context cx) { /** A cheaper and less general version of the above for well-known argument types. */ public static Object getObjectIndex(Object obj, double dblIndex, Context cx, Scriptable scope) { - Scriptable sobj = toObjectOrNull(cx, obj, scope); - if (sobj == null) { - throw undefReadError(obj, toString(dblIndex)); - } + Scriptable sobj = asScriptableOrThrowUndefReadError(cx, scope, obj, dblIndex); int index = (int) dblIndex; if (index == dblIndex && index >= 0) { @@ -1842,7 +1897,31 @@ public static Object getObjectIndex(Scriptable obj, int index, Context cx) { if (result == Scriptable.NOT_FOUND) { result = Undefined.instance; } + return result; + } + + public static Object getSuperIndex( + Object superObject, double dblIndex, Context cx, Scriptable scope, Object thisObject) { + Scriptable superScriptable = + asScriptableOrThrowUndefReadError(cx, scope, superObject, dblIndex); + Scriptable thisScriptable = + asScriptableOrThrowUndefReadError(cx, scope, thisObject, dblIndex); + int index = (int) dblIndex; + if (index == dblIndex && index >= 0) { + return getSuperIndex(superScriptable, thisScriptable, index); + } + + String s = toString(dblIndex); + return getSuperProp(superScriptable, thisScriptable, s, cx, false); + } + + private static Object getSuperIndex( + Scriptable superScriptable, Scriptable thisScriptable, int index) { + Object result = ScriptableObject.getSuperProperty(superScriptable, thisScriptable, index); + if (result == Scriptable.NOT_FOUND) { + return Undefined.instance; + } return result; } @@ -1859,10 +1938,7 @@ public static Object setObjectElem(Object obj, Object elem, Object value, Contex /** Call obj.[[Put]](id, value) */ public static Object setObjectElem( Object obj, Object elem, Object value, Context cx, Scriptable scope) { - Scriptable sobj = toObjectOrNull(cx, obj, scope); - if (sobj == null) { - throw undefWriteError(obj, elem, value); - } + Scriptable sobj = asScriptableOrThrowUndefWriteError(cx, scope, obj, elem, value); return setObjectElem(sobj, elem, value, cx); } @@ -1883,6 +1959,43 @@ public static Object setObjectElem(Scriptable obj, Object elem, Object value, Co return value; } + /** Call super.[[Put]](id, value) */ + public static Object setSuperElem( + Object superObject, + Object elem, + Object value, + Context cx, + Scriptable scope, + Object thisObject) { + Scriptable superScriptable = + asScriptableOrThrowUndefWriteError(cx, scope, superObject, elem, value); + Scriptable thisScriptable = + asScriptableOrThrowUndefWriteError(cx, scope, thisObject, elem, value); + return setSuperElem(superScriptable, thisScriptable, elem, value, cx); + } + + public static Object setSuperElem( + Scriptable superScriptable, + Scriptable thisScriptable, + Object elem, + Object value, + Context cx) { + // No XML support for super + if (isSymbol(elem)) { + ScriptableObject.putSuperProperty( + superScriptable, thisScriptable, (Symbol) elem, value); + } else { + StringIdOrIndex s = toStringIdOrIndex(elem); + if (s.stringId == null) { + ScriptableObject.putSuperProperty(superScriptable, thisScriptable, s.index, value); + } else { + ScriptableObject.putSuperProperty( + superScriptable, thisScriptable, s.stringId, value); + } + } + return value; + } + /** * Version of setObjectElem when elem is a valid JS identifier name. * @@ -1896,17 +2009,8 @@ public static Object setObjectProp(Object obj, String property, Object value, Co /** Version of setObjectElem when elem is a valid JS identifier name. */ public static Object setObjectProp( Object obj, String property, Object value, Context cx, Scriptable scope) { - if (!(obj instanceof Scriptable) - && cx.isStrictMode() - && cx.getLanguageVersion() >= Context.VERSION_1_8) { - throw undefWriteError(obj, property, value); - } - - Scriptable sobj = toObjectOrNull(cx, obj, scope); - if (sobj == null) { - throw undefWriteError(obj, property, value); - } - + verifyIsScriptableOrComplainWriteErrorInEs5Strict(obj, property, value, cx); + Scriptable sobj = asScriptableOrThrowUndefWriteError(cx, scope, obj, property, value); return setObjectProp(sobj, property, value, cx); } @@ -1915,6 +2019,35 @@ public static Object setObjectProp(Scriptable obj, String property, Object value return value; } + /** Version of setSuperElem when elem is a valid JS identifier name. */ + public static Object setSuperProp( + Object superObject, + String property, + Object value, + Context cx, + Scriptable scope, + Object thisObject) { + verifyIsScriptableOrComplainWriteErrorInEs5Strict(superObject, property, value, cx); + verifyIsScriptableOrComplainWriteErrorInEs5Strict(thisObject, property, value, cx); + + Scriptable superScriptable = + asScriptableOrThrowUndefWriteError(cx, scope, superObject, property, value); + Scriptable thisScriptable = + asScriptableOrThrowUndefWriteError(cx, scope, thisObject, property, value); + + return setSuperProp(superScriptable, thisScriptable, property, value, cx); + } + + public static Object setSuperProp( + Scriptable superScriptable, + Scriptable thisScriptable, + String property, + Object value, + Context cx) { + ScriptableObject.putSuperProperty(superScriptable, thisScriptable, property, value); + return value; + } + /** * A cheaper and less general version of the above for well-known argument types. * @@ -1928,11 +2061,7 @@ public static Object setObjectIndex(Object obj, double dblIndex, Object value, C /** A cheaper and less general version of the above for well-known argument types. */ public static Object setObjectIndex( Object obj, double dblIndex, Object value, Context cx, Scriptable scope) { - Scriptable sobj = toObjectOrNull(cx, obj, scope); - if (sobj == null) { - throw undefWriteError(obj, String.valueOf(dblIndex), value); - } - + Scriptable sobj = asScriptableOrThrowUndefWriteError(cx, scope, obj, dblIndex, value); int index = (int) dblIndex; if (index == dblIndex && index >= 0) { return setObjectIndex(sobj, index, value, cx); @@ -1946,6 +2075,37 @@ public static Object setObjectIndex(Scriptable obj, int index, Object value, Con return value; } + /** A cheaper and less general version of the above for well-known argument types. */ + public static Object setSuperIndex( + Object superObject, + double dblIndex, + Object value, + Context cx, + Scriptable scope, + Object thisObject) { + Scriptable superScriptable = + asScriptableOrThrowUndefWriteError(cx, scope, superObject, dblIndex, value); + Scriptable thisScriptable = + asScriptableOrThrowUndefWriteError(cx, scope, thisObject, dblIndex, value); + + int index = (int) dblIndex; + if (index == dblIndex && index >= 0) { + return setSuperIndex(superScriptable, thisScriptable, index, value, cx); + } + String s = toString(dblIndex); + return setSuperProp(superScriptable, thisScriptable, s, value, cx); + } + + public static Object setSuperIndex( + Scriptable superScriptable, + Scriptable thisScriptable, + int index, + Object value, + Context cx) { + ScriptableObject.putSuperProperty(superScriptable, thisScriptable, index, value); + return value; + } + public static boolean deleteObjectElem(Scriptable target, Object elem, Context cx) { if (isSymbol(elem)) { SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(target); @@ -3456,10 +3616,7 @@ public static Object propIncrDecr(Object obj, String id, Context cx, int incrDec public static Object propIncrDecr( Object obj, String id, Context cx, Scriptable scope, int incrDecrMask) { - Scriptable start = toObjectOrNull(cx, obj, scope); - if (start == null) { - throw undefReadError(obj, id); - } + Scriptable start = asScriptableOrThrowUndefReadError(cx, scope, obj, id); Scriptable target = start; Object value; @@ -4363,24 +4520,24 @@ public static void initScript( /** * @deprecated Use {@link #createFunctionActivation(NativeFunction, Context, Scriptable, - * Object[], boolean, boolean)} instead + * Object[], boolean, boolean, Scriptable)} instead */ @Deprecated public static Scriptable createFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args) { return createFunctionActivation( - funObj, Context.getCurrentContext(), scope, args, false, false); + funObj, Context.getCurrentContext(), scope, args, false, false, null); } /** * @deprecated Use {@link #createFunctionActivation(NativeFunction, Context, Scriptable, - * Object[], boolean, boolean)} instead + * Object[], boolean, boolean, Scriptable)} instead */ @Deprecated public static Scriptable createFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { return new NativeCall( - funObj, Context.getCurrentContext(), scope, args, false, isStrict, false); + funObj, Context.getCurrentContext(), scope, args, false, isStrict, false, null); } public static Scriptable createFunctionActivation( @@ -4389,19 +4546,20 @@ public static Scriptable createFunctionActivation( Scriptable scope, Object[] args, boolean isStrict, - boolean argsHasRest) { - return new NativeCall(funObj, cx, scope, args, false, isStrict, argsHasRest); + boolean argsHasRest, + Scriptable homeObject) { + return new NativeCall(funObj, cx, scope, args, false, isStrict, argsHasRest, homeObject); } /** * @deprecated Use {@link #createArrowFunctionActivation(NativeFunction, Context, Scriptable, - * Object[], boolean, boolean)} instead + * Object[], boolean, boolean, Scriptable)} instead */ @Deprecated public static Scriptable createArrowFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { return new NativeCall( - funObj, Context.getCurrentContext(), scope, args, true, isStrict, false); + funObj, Context.getCurrentContext(), scope, args, true, isStrict, false, null); } public static Scriptable createArrowFunctionActivation( @@ -4410,8 +4568,9 @@ public static Scriptable createArrowFunctionActivation( Scriptable scope, Object[] args, boolean isStrict, - boolean argsHasRest) { - return new NativeCall(funObj, cx, scope, args, true, isStrict, argsHasRest); + boolean argsHasRest, + Scriptable homeObject) { + return new NativeCall(funObj, cx, scope, args, true, isStrict, argsHasRest, homeObject); } public static void enterActivationFunction(Context cx, Scriptable scope) { @@ -5112,6 +5271,33 @@ public static EcmaError typeError3(String messageId, String arg1, String arg2, S return typeError(msg); } + private static Scriptable asScriptableOrThrowUndefReadError( + Context cx, Scriptable scope, Object obj, Object elem) { + Scriptable scriptable = toObjectOrNull(cx, obj, scope); + if (scriptable == null) { + throw undefReadError(obj, elem); + } + return scriptable; + } + + private static Scriptable asScriptableOrThrowUndefWriteError( + Context cx, Scriptable scope, Object obj, Object elem, Object value) { + Scriptable scriptable = toObjectOrNull(cx, obj, scope); + if (scriptable == null) { + throw undefWriteError(obj, elem, value); + } + return scriptable; + } + + private static void verifyIsScriptableOrComplainWriteErrorInEs5Strict( + Object obj, String property, Object value, Context cx) { + if (!(obj instanceof Scriptable) + && cx.isStrictMode() + && cx.getLanguageVersion() >= Context.VERSION_1_8) { + throw undefWriteError(obj, property, value); + } + } + public static RuntimeException undefReadError(Object object, Object id) { return typeErrorById("msg.undef.prop.read", toString(object), toString(id)); } @@ -5333,6 +5519,11 @@ public static Scriptable lastStoredScriptable(Context cx) { return result; } + public static void discardLastStoredScriptable(Context cx) { + if (cx.scratchScriptable == null) throw new IllegalStateException(); + cx.scratchScriptable = null; + } + static String makeUrlForGeneratedScript( boolean isEval, String masterScriptUrl, int masterScriptLine) { if (isEval) { diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptableObject.java b/rhino/src/main/java/org/mozilla/javascript/ScriptableObject.java index 8fc9cd5eca..3b630dd273 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptableObject.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptableObject.java @@ -2126,7 +2126,19 @@ private void checkNotSealed(Object key, int index) { * @since 1.5R2 */ public static Object getProperty(Scriptable obj, String name) { - Scriptable start = obj; + return getPropWalkingPrototypeChain(obj, name, obj); + } + + /** + * Gets a named property from super, walking the super's prototype chain, but passing the + * correct "this" to getter slots. + */ + public static Object getSuperProperty(Scriptable superObj, Scriptable thisObj, String name) { + return getPropWalkingPrototypeChain(superObj, name, thisObj); + } + + private static Object getPropWalkingPrototypeChain( + Scriptable obj, String name, Scriptable start) { Object result; do { result = obj.get(name, start); @@ -2138,7 +2150,16 @@ public static Object getProperty(Scriptable obj, String name) { /** This is a version of getProperty that works with Symbols. */ public static Object getProperty(Scriptable obj, Symbol key) { - Scriptable start = obj; + return getPropWalkingPrototypeChain(obj, obj, key); + } + + /** This is a version of getSuperProperty that works with Symbols. */ + public static Object getSuperProperty(Scriptable superObj, Scriptable thisObj, Symbol key) { + return getPropWalkingPrototypeChain(superObj, thisObj, key); + } + + private static Object getPropWalkingPrototypeChain( + Scriptable obj, Scriptable start, Symbol key) { Object result; do { result = ensureSymbolScriptable(obj).get(key, start); @@ -2190,7 +2211,19 @@ public static T getTypedProperty(Scriptable s, int index, Class type) { * @since 1.5R2 */ public static Object getProperty(Scriptable obj, int index) { - Scriptable start = obj; + return getPropWalkingPrototypeChain(obj, index, obj); + } + + /** + * Gets an indexed property from super, walking the super's prototype chain, but passing the + * correct "this" to getter slots. + */ + public static Object getSuperProperty(Scriptable superObj, Scriptable thisObj, int index) { + return getPropWalkingPrototypeChain(superObj, index, thisObj); + } + + private static Object getPropWalkingPrototypeChain( + Scriptable obj, int index, Scriptable start) { Object result; do { result = obj.get(index, start); @@ -2303,6 +2336,12 @@ public static void putProperty(Scriptable obj, String name, Object value) { base.put(name, obj, value); } + /** Variant of putProperty to handle super.name = value */ + public static void putSuperProperty( + Scriptable superObj, Scriptable thisObj, String name, Object value) { + superObj.put(name, thisObj, value); + } + /** This is a version of putProperty for Symbol keys. */ public static void putProperty(Scriptable obj, Symbol key, Object value) { Scriptable base = getBase(obj, key); @@ -2310,6 +2349,12 @@ public static void putProperty(Scriptable obj, Symbol key, Object value) { ensureSymbolScriptable(base).put(key, obj, value); } + /** Variant of putProperty to handle super[key] = value where key is a symbol */ + public static void putSuperProperty( + Scriptable superObj, Scriptable thisObj, Symbol key, Object value) { + ensureSymbolScriptable(superObj).put(key, thisObj, value); + } + /** * Puts a named property in an object or in an object in its prototype chain. * @@ -2352,6 +2397,12 @@ public static void putProperty(Scriptable obj, int index, Object value) { base.put(index, obj, value); } + /** Variant of putProperty to handle super[index] = value where index is integer */ + public static void putSuperProperty( + Scriptable superObj, Scriptable thisObj, int index, Object value) { + superObj.put(index, thisObj, value); + } + /** * Removes the property from an object or its prototype chain. * diff --git a/rhino/src/main/java/org/mozilla/javascript/Token.java b/rhino/src/main/java/org/mozilla/javascript/Token.java index 80bbc58384..2fac729e63 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Token.java +++ b/rhino/src/main/java/org/mozilla/javascript/Token.java @@ -71,10 +71,15 @@ public static enum CommentType { TYPEOF = DELPROP + 1, GETPROP = TYPEOF + 1, GETPROPNOWARN = GETPROP + 1, - SETPROP = GETPROPNOWARN + 1, - GETELEM = SETPROP + 1, - SETELEM = GETELEM + 1, - CALL = SETELEM + 1, + GETPROP_SUPER = GETPROPNOWARN + 1, + GETPROPNOWARN_SUPER = GETPROP_SUPER + 1, + SETPROP = GETPROPNOWARN_SUPER + 1, + SETPROP_SUPER = SETPROP + 1, + GETELEM = SETPROP_SUPER + 1, + GETELEM_SUPER = GETELEM + 1, + SETELEM = GETELEM_SUPER + 1, + SETELEM_SUPER = SETELEM + 1, + CALL = SETELEM_SUPER + 1, NAME = CALL + 1, NUMBER = NAME + 1, STRING = NUMBER + 1, @@ -110,7 +115,8 @@ public static enum CommentType { REF_CALL = DEL_REF + 1, // f(args) = something or f(args)++ REF_SPECIAL = REF_CALL + 1, // reference for special properties like __proto YIELD = REF_SPECIAL + 1, // JS 1.7 yield pseudo keyword - STRICT_SETNAME = YIELD + 1, + SUPER = YIELD + 1, // ES6 super keyword + STRICT_SETNAME = SUPER + 1, EXP = STRICT_SETNAME + 1, // Exponentiation Operator // For XML support: @@ -331,12 +337,22 @@ public static String typeToName(int token) { return "GETPROP"; case GETPROPNOWARN: return "GETPROPNOWARN"; + case GETPROP_SUPER: + return "GETPROP_SUPER"; + case GETPROPNOWARN_SUPER: + return "GETPROPNOWARN_SUPER"; case SETPROP: return "SETPROP"; + case SETPROP_SUPER: + return "SETPROP_SUPER"; case GETELEM: return "GETELEM"; + case GETELEM_SUPER: + return "GETELEM_SUPER"; case SETELEM: return "SETELEM"; + case SETELEM_SUPER: + return "SETELEM_SUPER"; case CALL: return "CALL"; case NAME: @@ -581,6 +597,8 @@ public static String typeToName(int token) { return "LET"; case YIELD: return "YIELD"; + case SUPER: + return "SUPER"; case EXP: return "EXP"; case CONST: @@ -686,6 +704,8 @@ public static String keywordToName(int token) { return "with"; case Token.YIELD: return "yield"; + case Token.SUPER: + return "super"; case Token.CATCH: return "catch"; case Token.CONST: diff --git a/rhino/src/main/java/org/mozilla/javascript/TokenStream.java b/rhino/src/main/java/org/mozilla/javascript/TokenStream.java index f133ac8f31..58d3bc359a 100644 --- a/rhino/src/main/java/org/mozilla/javascript/TokenStream.java +++ b/rhino/src/main/java/org/mozilla/javascript/TokenStream.java @@ -379,7 +379,7 @@ private static int stringToKeywordForES(String name, boolean isStrict) { Id_instanceof = Token.INSTANCEOF, Id_new = Token.NEW, Id_return = Token.RETURN, - Id_super = Token.RESERVED, + Id_super = Token.SUPER, Id_switch = Token.SWITCH, Id_this = Token.THIS, Id_throw = Token.THROW, diff --git a/rhino/src/main/java/org/mozilla/javascript/ast/KeywordLiteral.java b/rhino/src/main/java/org/mozilla/javascript/ast/KeywordLiteral.java index e760f2cbc6..975a3d0aba 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ast/KeywordLiteral.java +++ b/rhino/src/main/java/org/mozilla/javascript/ast/KeywordLiteral.java @@ -9,9 +9,10 @@ import org.mozilla.javascript.Token; /** - * AST node for keyword literals: currently, {@code this}, {@code null}, {@code true}, {@code - * false}, and {@code debugger}. Node type is one of {@link Token#THIS}, {@link Token#NULL}, {@link - * Token#TRUE}, {@link Token#FALSE}, or {@link Token#DEBUGGER}. + * AST node for keyword literals: currently, {@code this}, {@code super}, {@code null}, {@code + * true}, {@code false}, and {@code debugger}. Node type is one of {@link Token#THIS}, {@link + * Token#SUPER}, {@link Token#NULL}, {@link Token#TRUE}, {@link Token#FALSE}, or {@link + * Token#DEBUGGER}. */ public class KeywordLiteral extends AstNode { @@ -43,6 +44,7 @@ public KeywordLiteral(int pos, int len, int nodeType) { @Override public KeywordLiteral setType(int nodeType) { if (!(nodeType == Token.THIS + || nodeType == Token.SUPER || nodeType == Token.NULL || nodeType == Token.TRUE || nodeType == Token.FALSE @@ -65,6 +67,9 @@ public String toSource(int depth) { case Token.THIS: sb.append("this"); break; + case Token.SUPER: + sb.append("super"); + break; case Token.NULL: sb.append("null"); break; diff --git a/rhino/src/main/java/org/mozilla/javascript/ast/ScriptNode.java b/rhino/src/main/java/org/mozilla/javascript/ast/ScriptNode.java index bd23c869dc..bf18a32544 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ast/ScriptNode.java +++ b/rhino/src/main/java/org/mozilla/javascript/ast/ScriptNode.java @@ -38,6 +38,7 @@ public class ScriptNode extends Scope { private Object compilerData; private int tempNumber = 0; private boolean inStrictMode; + private boolean isMethodDefinition; { // during parsing, a ScriptNode or FunctionNode's top scope is itself @@ -329,6 +330,14 @@ public boolean isInStrictMode() { return inStrictMode; } + public boolean isMethodDefinition() { + return isMethodDefinition; + } + + public void setMethodDefinition(boolean methodDefinition) { + isMethodDefinition = methodDefinition; + } + @Override public void visit(NodeVisitor v) { if (v.visit(this)) { diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java index b872eca764..cb40e79865 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java @@ -120,6 +120,12 @@ private void generateGenerator() { cfw.addALoad(argsLocal); cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addPush(scriptOrFn.hasRestParameter()); + cfw.addLoadThis(); + cfw.addInvoke( + ByteCode.INVOKEVIRTUAL, + "org/mozilla/javascript/BaseFunction", + "getHomeObject", + "()Lorg/mozilla/javascript/Scriptable;"); addScriptRuntimeInvoke( "createFunctionActivation", "(Lorg/mozilla/javascript/NativeFunction;" @@ -128,6 +134,7 @@ private void generateGenerator() { + "[Ljava/lang/Object;" + "Z" + "Z" + + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.addAStore(variableObjectLocal); @@ -207,6 +214,7 @@ private void initBodyGeneration() { epilogueLabel = -1; enterAreaStartLabel = -1; generatorStateLocal = -1; + savedHomeObjectLocal = -1; } /** Generate the prologue for a function or script. */ @@ -424,6 +432,39 @@ private void generatePrologue() { cfw.addALoad(argsLocal); cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addPush(scriptOrFn.hasRestParameter()); + + if (!isArrow) { + // Just pass the home object of the function + cfw.addLoadThis(); + cfw.addInvoke( + ByteCode.INVOKEVIRTUAL, + "org/mozilla/javascript/BaseFunction", + "getHomeObject", + "()Lorg/mozilla/javascript/Scriptable;"); + } else { + // Propagate the home object from the activation scope (i.e. the NativeCall) + int putNull = cfw.acquireLabel(); + int after = cfw.acquireLabel(); + + cfw.addALoad(variableObjectLocal); + cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/NativeCall"); + cfw.add(ByteCode.IFEQ, putNull); + + cfw.addALoad(variableObjectLocal); + cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/NativeCall"); + cfw.addInvoke( + ByteCode.INVOKEVIRTUAL, + "org/mozilla/javascript/NativeCall", + "getHomeObject", + "()Lorg/mozilla/javascript/Scriptable;"); + cfw.add(ByteCode.GOTO, after); + + cfw.markLabel(putNull); + cfw.add(ByteCode.ACONST_NULL); + + cfw.markLabel(after); + } + String methodName = isArrow ? "createArrowFunctionActivation" : "createFunctionActivation"; addScriptRuntimeInvoke( @@ -434,6 +475,7 @@ private void generatePrologue() { + "[Ljava/lang/Object;" + "Z" + "Z" + + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.addAStore(variableObjectLocal); cfw.addALoad(contextLocal); @@ -1064,6 +1106,46 @@ private void generateExpression(Node node, Node parent) { cfw.addALoad(thisObjLocal); break; + case Token.SUPER: + { + // See 9.1.1.3.5 GetSuperBase + + // Read home object from activation, which we know we'll always have because we + // set "requiresActivation" to any function that mentions "super" in IRFactory + cfw.addALoad(variableObjectLocal); + cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/NativeCall"); + cfw.addInvoke( + ByteCode.INVOKEVIRTUAL, + "org/mozilla/javascript/NativeCall", + "getHomeObject", + "()Lorg/mozilla/javascript/Scriptable;"); + + // If null, then put undefined + int after = cfw.acquireLabel(); + int putPrototype = cfw.acquireLabel(); + cfw.add(ByteCode.DUP); + cfw.add(ByteCode.IFNONNULL, putPrototype); + + cfw.add(ByteCode.POP); + cfw.add( + ByteCode.GETSTATIC, + "org/mozilla/javascript/Undefined", + "instance", + "Ljava/lang/Object;"); + cfw.add(ByteCode.GOTO, after); + + // Otherwise put the prototype + cfw.markLabel(putPrototype); + cfw.addInvoke( + ByteCode.INVOKEINTERFACE, + "org/mozilla/javascript/Scriptable", + "getPrototype", + "()Lorg/mozilla/javascript/Scriptable;"); + + cfw.markLabel(after); + break; + } + case Token.THISFN: cfw.add(ByteCode.ALOAD_0); break; @@ -1675,10 +1757,20 @@ private void finishGetElemGeneration(Node node, Node child) { generateExpression(child.getNext(), node); // id cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); - if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) { - addDynamicInvoke("PROP:GETINDEX", Signatures.PROP_GET_INDEX); + + if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + cfw.addALoad(thisObjLocal); + if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) { + addDynamicInvoke("PROP:GETINDEXSUPER", Signatures.PROP_GET_INDEX_SUPER); + } else { + addDynamicInvoke("PROP:GETELEMENTSUPER", Signatures.PROP_GET_ELEMENT_SUPER); + } } else { - addDynamicInvoke("PROP:GETELEMENT", Signatures.PROP_GET_ELEMENT); + if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) { + addDynamicInvoke("PROP:GETINDEX", Signatures.PROP_GET_INDEX); + } else { + addDynamicInvoke("PROP:GETELEMENT", Signatures.PROP_GET_ELEMENT); + } } } @@ -1956,6 +2048,16 @@ private void visitFunction(OptFunctionNode ofn, int functionType) { + ")Lorg/mozilla/javascript/Function;"); } + if (ofn.fnode.isMethodDefinition()) { + cfw.add(ByteCode.DUP); + cfw.addALoad(savedHomeObjectLocal); + cfw.addInvoke( + ByteCode.INVOKEVIRTUAL, + "org/mozilla/javascript/BaseFunction", + "setHomeObject", + "(Lorg/mozilla/javascript/Scriptable;)V"); + } + if (functionType == FunctionNode.FUNCTION_EXPRESSION || functionType == FunctionNode.ARROW_FUNCTION) { // Leave closure object on stack and do not pass it to @@ -2054,10 +2156,7 @@ private void generateObjectLiteralFactory(Node node, int count) { } private void visitArrayLiteral(Node node, Node child, boolean topLevel) { - int count = 0; - for (Node cursor = child; cursor != null; cursor = cursor.getNext()) { - ++count; - } + int count = countArguments(child); // If code budget is tight swap out literals into separate method if (!topLevel @@ -2274,6 +2373,9 @@ private void visitObjectLiteral(Node node, Node child, boolean topLevel) { "newObject", "(Lorg/mozilla/javascript/Scriptable;)Lorg/mozilla/javascript/Scriptable;"); cfw.add(ByteCode.DUP); + savedHomeObjectLocal = getNewWordLocal(); + cfw.addAStore(savedHomeObjectLocal); + cfw.add(ByteCode.DUP); addLoadProperty(node, child, properties, count); @@ -2393,7 +2495,43 @@ private void visitStandardCall(Node node, Node child) { String signature; Integer afterLabel = null; - if (firstArgChild == null) { + if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + // Just one general case, non optimized depending on the number of arguments + + int argCount = countArguments(firstArgChild); + generateFunctionAndThisObj(child, node); + + // stack: ... functionObj, superObj is stored on scratch last scriptable + // We discard the last scriptable, and then push the thisObj. So we have resolved the + // function on the super object, but we invoke it with the current this. + cfw.addALoad(contextLocal); + cfw.addInvoke( + ByteCode.INVOKESTATIC, + "org/mozilla/javascript/ScriptRuntime", + "discardLastStoredScriptable", + "(Lorg/mozilla/javascript/Context;)V"); + cfw.addALoad(thisObjLocal); + + if (argCount == 0) { + methodName = "call0"; + signature = SIGNATURE_CALL0; + } else if (argCount == 1) { + generateExpression(firstArgChild, node); + methodName = "call1"; + signature = SIGNATURE_CALL1; + } else { + if (argCount == 2) { + generateExpression(firstArgChild, node); + generateExpression(firstArgChild.getNext(), node); + methodName = "call2"; + signature = SIGNATURE_CALL2; + } else { + generateCallArgArray(node, firstArgChild, false); + methodName = "callN"; + signature = SIGNATURE_CALLN; + } + } + } else if (firstArgChild == null) { if (childType == Token.NAME) { // name() call String name = child.getString(); @@ -2424,12 +2562,7 @@ private void visitStandardCall(Node node, Node child) { generateFunctionAndThisObj(child, node); pushThisFromLastScriptable(); methodName = isOptionalChainingCall ? "call0Optional" : "call0"; - signature = - "(Lorg/mozilla/javascript/Callable;" - + "Lorg/mozilla/javascript/Scriptable;" - + "Lorg/mozilla/javascript/Context;" - + "Lorg/mozilla/javascript/Scriptable;" - + ")Ljava/lang/Object;"; + signature = SIGNATURE_CALL0; } } else if (childType == Token.NAME) { @@ -2466,13 +2599,7 @@ private void visitStandardCall(Node node, Node child) { pushThisFromLastScriptable(); methodName = "callN"; generateCallArgArray(node, firstArgChild, false); - signature = - "(Lorg/mozilla/javascript/Callable;" - + "Lorg/mozilla/javascript/Scriptable;" - + "[Ljava/lang/Object;" - + "Lorg/mozilla/javascript/Context;" - + "Lorg/mozilla/javascript/Scriptable;" - + ")Ljava/lang/Object;"; + signature = SIGNATURE_CALLN; } else { generateCallArgArray(node, firstArgChild, false); cfw.addPush(name); @@ -2485,12 +2612,9 @@ private void visitStandardCall(Node node, Node child) { + ")Ljava/lang/Object;"; } } else { - int argCount = 0; - for (Node arg = firstArgChild; arg != null; arg = arg.getNext()) { - ++argCount; - } + int argCount = countArguments(firstArgChild); generateFunctionAndThisObj(child, node); - // stack: ... functionObj thisObj + // stack: ... functionObj, thisObj is stored on scratch last scriptable if (isOptionalChainingCall) { // jump to afterLabel is name is not null and not undefined @@ -2517,36 +2641,17 @@ private void visitStandardCall(Node node, Node child) { if (argCount == 1) { generateExpression(firstArgChild, node); methodName = "call1"; - signature = - "(Lorg/mozilla/javascript/Callable;" - + "Lorg/mozilla/javascript/Scriptable;" - + "Ljava/lang/Object;" - + "Lorg/mozilla/javascript/Context;" - + "Lorg/mozilla/javascript/Scriptable;" - + ")Ljava/lang/Object;"; + signature = SIGNATURE_CALL1; } else { if (argCount == 2) { generateExpression(firstArgChild, node); generateExpression(firstArgChild.getNext(), node); methodName = "call2"; - signature = - "(Lorg/mozilla/javascript/Callable;" - + "Lorg/mozilla/javascript/Scriptable;" - + "Ljava/lang/Object;" - + "Ljava/lang/Object;" - + "Lorg/mozilla/javascript/Context;" - + "Lorg/mozilla/javascript/Scriptable;" - + ")Ljava/lang/Object;"; + signature = SIGNATURE_CALL2; } else { generateCallArgArray(node, firstArgChild, false); methodName = "callN"; - signature = - "(Lorg/mozilla/javascript/Callable;" - + "Lorg/mozilla/javascript/Scriptable;" - + "[Ljava/lang/Object;" - + "Lorg/mozilla/javascript/Context;" - + "Lorg/mozilla/javascript/Scriptable;" - + ")Ljava/lang/Object;"; + signature = SIGNATURE_CALLN; } } } @@ -2559,6 +2664,14 @@ private void visitStandardCall(Node node, Node child) { } } + private static int countArguments(Node firstArgChild) { + int argCount = 0; + for (Node arg = firstArgChild; arg != null; arg = arg.getNext()) { + ++argCount; + } + return argCount; + } + private void pushThisFromLastScriptable() { // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis cfw.addALoad(contextLocal); @@ -2588,6 +2701,10 @@ private void visitStandardNew(Node node, Node child) { } private void visitOptimizedCall(Node node, OptFunctionNode target, int type, Node child) { + // Only calls to top-level functions (i.e. non member functions) can be optimized, so they + // cannot be a super.foo() call + if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) Kit.codeBug(); + Node firstArgChild = child.getNext(); String className = codegen.mainClassName; @@ -2701,10 +2818,7 @@ private void visitOptimizedCall(Node node, OptFunctionNode target, int type, Nod } private void generateCallArgArray(Node node, Node argChild, boolean directCall) { - int argCount = 0; - for (Node child = argChild; child != null; child = child.getNext()) { - ++argCount; - } + int argCount = countArguments(argChild); // load array object to set arguments if (argCount == 1 && itsOneArgArray >= 0) { cfw.addALoad(itsOneArgArray); @@ -4176,19 +4290,22 @@ private void visitGetProp(Node node, Node child) { cfw.add(ByteCode.GOTO, after); cfw.markLabel(getExpr); - finishGetPropGeneration(node, child); + finishGetPropGeneration(node, child.getNext()); cfw.markLabel(after); } else { - finishGetPropGeneration(node, child); + finishGetPropGeneration(node, child.getNext()); } } - private void finishGetPropGeneration(Node node, Node child) { - Node nameChild = child.getNext(); + private void finishGetPropGeneration(Node node, Node nameChild) { cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); - if (node.getType() == Token.GETPROPNOWARN) { + if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + cfw.addALoad(thisObjLocal); + cfw.addLoadConstant(node.getType() == Token.GETPROPNOWARN ? 1 : 0); + addDynamicInvoke("PROP:GETSUPER:" + nameChild.getString(), Signatures.PROP_GET_SUPER); + } else if (node.getType() == Token.GETPROPNOWARN) { addDynamicInvoke("PROP:GETNOWARN:" + nameChild.getString(), Signatures.PROP_GET_NOWARN); } else { addDynamicInvoke("PROP:GET:" + nameChild.getString(), Signatures.PROP_GET); @@ -4201,15 +4318,18 @@ private void visitSetProp(int type, Node node, Node child) { Node nameChild = child; if (type == Token.SETPROP_OP) { cfw.add(ByteCode.DUP); - cfw.addALoad(contextLocal); - cfw.addALoad(variableObjectLocal); - addDynamicInvoke("PROP:GET:" + nameChild.getString(), Signatures.PROP_GET); + finishGetPropGeneration(node, nameChild); } child = child.getNext(); generateExpression(child, node); cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); - addDynamicInvoke("PROP:SET:" + nameChild.getString(), Signatures.PROP_SET); + if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) { + cfw.addALoad(thisObjLocal); + addDynamicInvoke("PROP:SETSUPER:" + nameChild.getString(), Signatures.PROP_SET_SUPER); + } else { + addDynamicInvoke("PROP:SET:" + nameChild.getString(), Signatures.PROP_SET); + } } private void visitSetElem(int type, Node node, Node child) { @@ -4221,6 +4341,7 @@ private void visitSetElem(int type, Node node, Node child) { generateExpression(child, node); child = child.getNext(); boolean indexIsNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1); + boolean isSuper = node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1; if (type == Token.SETELEM_OP) { if (indexIsNumber) { // stack: ... object object number @@ -4228,23 +4349,42 @@ private void visitSetElem(int type, Node node, Node child) { cfw.add(ByteCode.DUP2_X1); cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); - addDynamicInvoke("PROP:GETINDEX", Signatures.PROP_GET_INDEX); + if (isSuper) { + cfw.addALoad(thisObjLocal); + addDynamicInvoke("PROP:GETINDEXSUPER", Signatures.PROP_GET_INDEX_SUPER); + } else { + addDynamicInvoke("PROP:GETINDEX", Signatures.PROP_GET_INDEX); + } } else { // stack: ... object object indexObject // -> ... object indexObject object indexObject cfw.add(ByteCode.DUP_X1); cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); - addDynamicInvoke("PROP:GETELEMENT", Signatures.PROP_GET_ELEMENT); + if (isSuper) { + cfw.addALoad(thisObjLocal); + addDynamicInvoke("PROP:GETELEMENTSUPER", Signatures.PROP_GET_ELEMENT_SUPER); + } else { + addDynamicInvoke("PROP:GETELEMENT", Signatures.PROP_GET_ELEMENT); + } } } generateExpression(child, node); cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); - if (indexIsNumber) { - addDynamicInvoke("PROP:SETINDEX", Signatures.PROP_SET_INDEX); + if (isSuper) { + cfw.addALoad(thisObjLocal); + if (indexIsNumber) { + addDynamicInvoke("PROP:SETINDEXSUPER", Signatures.PROP_SET_INDEX_SUPER); + } else { + addDynamicInvoke("PROP:SETELEMENTSUPER", Signatures.PROP_SET_ELEMENT_SUPER); + } } else { - addDynamicInvoke("PROP:SETELEMENT", Signatures.PROP_SET_ELEMENT); + if (indexIsNumber) { + addDynamicInvoke("PROP:SETINDEX", Signatures.PROP_SET_INDEX); + } else { + addDynamicInvoke("PROP:SETELEMENT", Signatures.PROP_SET_ELEMENT); + } } } @@ -4460,6 +4600,35 @@ private void releaseWordLocal(int local) { locals[local] = 0; } + private static final String SIGNATURE_CALL1 = + "(Lorg/mozilla/javascript/Callable;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + ")Ljava/lang/Object;"; + private static final String SIGNATURE_CALL2 = + "(Lorg/mozilla/javascript/Callable;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + ")Ljava/lang/Object;"; + private static final String SIGNATURE_CALLN = + "(Lorg/mozilla/javascript/Callable;" + + "Lorg/mozilla/javascript/Scriptable;" + + "[Ljava/lang/Object;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + ")Ljava/lang/Object;"; + private static final String SIGNATURE_CALL0 = + "(Lorg/mozilla/javascript/Callable;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + ")Ljava/lang/Object;"; + static final int GENERATOR_TERMINATE = -1; static final int GENERATOR_START = 0; static final int GENERATOR_YIELD_START = 1; @@ -4500,6 +4669,7 @@ private void releaseWordLocal(int local) { private int itsZeroArgArray; private int itsOneArgArray; private int generatorStateLocal; + private int savedHomeObjectLocal; private boolean isGenerator; private int generatorSwitch; diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/Bootstrapper.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/Bootstrapper.java index 4d0fdfc7b5..f34dc4d996 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/Bootstrapper.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/Bootstrapper.java @@ -72,6 +72,11 @@ private static Operation parseOperation(String name) throws NoSuchMethodExceptio return RhinoOperation.GETNOWARN .withNamespace(StandardNamespace.PROPERTY) .named(getNameSegment(tokens, name, 2)); + case "GETSUPER": + // Get an object property from super with a constant name + return RhinoOperation.GETSUPER + .withNamespace(StandardNamespace.PROPERTY) + .named(getNameSegment(tokens, name, 2)); case "GETWITHTHIS": // Same but also return "this" so that it is found by "lastStoredScriptable" return RhinoOperation.GETWITHTHIS @@ -86,20 +91,38 @@ private static Operation parseOperation(String name) throws NoSuchMethodExceptio // Get the value of an element from a property that is on the stack,\ // as if using "[]" notation. Could be a String, number, or Symbol return RhinoOperation.GETELEMENT.withNamespace(StandardNamespace.PROPERTY); + case "GETELEMENTSUPER": + // Get the value of an element from a property that is on the stack,\ + // as if using "[]" notation. Could be a String, number, or Symbol + return RhinoOperation.GETELEMENTSUPER.withNamespace(StandardNamespace.PROPERTY); case "GETINDEX": // Same but the value is definitely a numeric index return RhinoOperation.GETINDEX.withNamespace(StandardNamespace.PROPERTY); + case "GETINDEXSUPER": + // Same but the value is definitely a numeric index + return RhinoOperation.GETINDEXSUPER.withNamespace(StandardNamespace.PROPERTY); case "SET": // Set an object property with a constant name return StandardOperation.SET .withNamespace(StandardNamespace.PROPERTY) .named(getNameSegment(tokens, name, 2)); + case "SETSUPER": + // Set an object property in super with a constant name + return RhinoOperation.SETSUPER + .withNamespace(StandardNamespace.PROPERTY) + .named(getNameSegment(tokens, name, 2)); case "SETELEMENT": // Set an object property as if by "[]", with a property on the stack return RhinoOperation.SETELEMENT.withNamespace(StandardNamespace.PROPERTY); + case "SETELEMENTSUPER": + // Set an object property in super as if by "[]", with a property on the stack + return RhinoOperation.SETELEMENTSUPER.withNamespace(StandardNamespace.PROPERTY); case "SETINDEX": // Same but the property name is definitely a number return RhinoOperation.SETINDEX.withNamespace(StandardNamespace.PROPERTY); + case "SETINDEXSUPER": + // Same but the property name is definitely a number + return RhinoOperation.SETINDEXSUPER.withNamespace(StandardNamespace.PROPERTY); } } else if ("NAME".equals(namespaceName)) { switch (opName) { diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/DefaultLinker.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/DefaultLinker.java index 7b49bbcbf0..cd331757d7 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/DefaultLinker.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/DefaultLinker.java @@ -89,6 +89,10 @@ private GuardedInvocation getPropertyInvocation( "getObjectPropNoWarn", 1, op.getName()); + } else if (op.isOperation(RhinoOperation.GETSUPER)) { + mh = + bindStringParameter( + lookup, mType, ScriptRuntime.class, "getSuperProp", 1, op.getName()); } else if (op.isOperation(RhinoOperation.GETWITHTHIS)) { mh = bindStringParameter( @@ -111,14 +115,26 @@ private GuardedInvocation getPropertyInvocation( mh = bindStringParameter( lookup, mType, ScriptRuntime.class, "setObjectProp", 1, op.getName()); + } else if (op.isOperation(RhinoOperation.SETSUPER)) { + mh = + bindStringParameter( + lookup, mType, ScriptRuntime.class, "setSuperProp", 1, op.getName()); } else if (op.isOperation(RhinoOperation.GETELEMENT)) { mh = lookup.findStatic(ScriptRuntime.class, "getObjectElem", mType); + } else if (op.isOperation(RhinoOperation.GETELEMENTSUPER)) { + mh = lookup.findStatic(ScriptRuntime.class, "getSuperElem", mType); } else if (op.isOperation(RhinoOperation.GETINDEX)) { mh = lookup.findStatic(ScriptRuntime.class, "getObjectIndex", mType); + } else if (op.isOperation(RhinoOperation.GETINDEXSUPER)) { + mh = lookup.findStatic(ScriptRuntime.class, "getSuperIndex", mType); } else if (op.isOperation(RhinoOperation.SETELEMENT)) { mh = lookup.findStatic(ScriptRuntime.class, "setObjectElem", mType); + } else if (op.isOperation(RhinoOperation.SETELEMENTSUPER)) { + mh = lookup.findStatic(ScriptRuntime.class, "setSuperElem", mType); } else if (op.isOperation(RhinoOperation.SETINDEX)) { mh = lookup.findStatic(ScriptRuntime.class, "setObjectIndex", mType); + } else if (op.isOperation(RhinoOperation.SETINDEXSUPER)) { + mh = lookup.findStatic(ScriptRuntime.class, "setSuperIndex", mType); } if (mh != null) { diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java index 4a818d3d7c..311359e20d 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java @@ -146,7 +146,7 @@ public static void initFunction( public static Function bindThis( NativeFunction fn, Context cx, Scriptable scope, Scriptable thisObj) { - return new ArrowFunction(cx, scope, fn, thisObj); + return new ArrowFunction(cx, scope, fn, thisObj, null); } public static Object callSpecial( diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/RhinoOperation.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/RhinoOperation.java index f55aed4e24..9bcd8335ac 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/RhinoOperation.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/RhinoOperation.java @@ -10,14 +10,20 @@ public enum RhinoOperation implements Operation { BIND, GETNOWARN, + GETSUPER, GETWITHTHIS, GETWITHTHISOPTIONAL, GETELEMENT, + GETELEMENTSUPER, GETINDEX, + GETINDEXSUPER, SETSTRICT, SETCONST, + SETSUPER, SETELEMENT, + SETELEMENTSUPER, SETINDEX, + SETINDEXSUPER, ADD, EQ, SHALLOWEQ, diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/Signatures.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/Signatures.java index ffa1310759..ef1f64790a 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/Signatures.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/Signatures.java @@ -28,6 +28,18 @@ interface Signatures { "(Ljava/lang/Object;Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;)Ljava/lang/Object;"; + /** + * PROP:GET_SUPER:{name}: Looks up the super property named "name". Falls back to + * ScriptRuntime.getSuperProp. + */ + String PROP_GET_SUPER = + "(Ljava/lang/Object;" // superObj + + "Lorg/mozilla/javascript/Context;" // cx + + "Lorg/mozilla/javascript/Scriptable;" // scope + + "Ljava/lang/Object;" // thisObj + + "Z" // noWarn + + ")Ljava/lang/Object;"; + /** * PROP:GETWITHTHIS:{name}: Looks up an object property like PROP:GET, and sets "this" in the * "last stored scriptable." Falls back to ScriptRuntime.getPropFunctionAndThis. @@ -43,9 +55,23 @@ interface Signatures { * ScriptRuntime.getObjectIndex. */ String PROP_GET_INDEX = - "(Ljava/lang/Object;D" + "(Ljava/lang/Object;" + + "D" + "Lorg/mozilla/javascript/Context;" - + "Lorg/mozilla/javascript/Scriptable;)Ljava/lang/Object;"; + + "Lorg/mozilla/javascript/Scriptable;" + + ")Ljava/lang/Object;"; + + /** + * PROP:GETINDEXSUPER: Get a property from super based on a numeric index. Falls back to + * ScriptRuntime.getSuperIndex. + */ + String PROP_GET_INDEX_SUPER = + "(Ljava/lang/Object;" + + "D" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + + ")Ljava/lang/Object;"; /** * PROP:GETELEMENT: Get a property from an object based on an element ID, which could be a @@ -58,6 +84,18 @@ interface Signatures { + "Lorg/mozilla/javascript/Scriptable;" + ")Ljava/lang/Object;"; + /** + * PROP:GETELEMENTSUPER: Get a property from super based on an element ID, which could be a + * string, number, or symbol. Falls back to ScriptRuntime.getSuperElem. + */ + String PROP_GET_ELEMENT_SUPER = + "(Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + + ")Ljava/lang/Object;"; + /** * PROP:SET:{name}: Sets the named property on an object. Falls back to * ScriptRuntime.setObjectProp. @@ -66,6 +104,19 @@ interface Signatures { "(Ljava/lang/Object;Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;Lorg/mozilla/javascript/Scriptable;)Ljava/lang/Object;"; + /** + * PROP:SETSUPER:{name}: Sets the named property on super. Falls back to + * ScriptRuntime.setSuperProp. + */ + String PROP_SET_SUPER = + "(" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + + ")Ljava/lang/Object;"; + /** * PROP:SETINDEX: Set a property on an object based on a numeric index. Falls back to * ScriptRuntime.setObjectIndex. @@ -78,6 +129,19 @@ interface Signatures { + "Lorg/mozilla/javascript/Scriptable;" + ")Ljava/lang/Object;"; + /** + * PROP:SETINDEXSUPER: Set a property on super based on a numeric index. Falls back to + * ScriptRuntime.setSuperIndex. + */ + String PROP_SET_INDEX_SUPER = + "(Ljava/lang/Object;" + + "D" + + "Ljava/lang/Object;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + + ")Ljava/lang/Object;"; + /** * PROP:SETELEMENT: Set a property on an object based on an identifier. Falls back to * ScriptRuntime.setObjectElem. @@ -90,6 +154,19 @@ interface Signatures { + "Lorg/mozilla/javascript/Scriptable;" + ")Ljava/lang/Object;"; + /** + * PROP:SETELEMENT_SUPER: Set a property on super based on an identifier. Falls back to + * ScriptRuntime.setSuperElem. + */ + String PROP_SET_ELEMENT_SUPER = + "(Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + + ")Ljava/lang/Object;"; + /** * NAME:GET:{name}: Looks up a the named value from the scope. Falls back to ScriptRuntime.name. * Compared to that function, this version of the signature puts the "scope" first in the diff --git a/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties b/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties index 07d3d50c9c..b829843ce8 100644 --- a/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties +++ b/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties @@ -453,6 +453,12 @@ msg.no.paren =\ msg.reserved.id =\ identifier is a reserved word: {0} +msg.super.shorthand.function =\ + super should be inside a shorthand function + +msg.optional.super =\ + super is not allowed in an optional chaining expression + msg.no.paren.catch =\ missing ( before catch-block condition diff --git a/rhino/src/test/java/org/mozilla/javascript/SuperTest.java b/rhino/src/test/java/org/mozilla/javascript/SuperTest.java new file mode 100644 index 0000000000..407f0d9cf2 --- /dev/null +++ b/rhino/src/test/java/org/mozilla/javascript/SuperTest.java @@ -0,0 +1,945 @@ +package org.mozilla.javascript; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mozilla.javascript.tests.Utils; + +class SuperTest { + @Nested + class LexerParser { + @Test + void superIsNotAKeywordUntilES6() { + try (Context cx = Context.enter()) { + cx.setLanguageVersion(Context.VERSION_1_8); + Script test = cx.compileString("var super = 42;", "test", 1, null); + assertNotNull(test); + } + } + + @Test + void superIsAKeywordInES6AndCannotBeUsedAsVariableName() { + assertIsSyntaxErrorES6("var super = 42;", "missing variable name (test#1)"); + } + + @Test + void isSyntaxErrorIfHasDirectSuperOfMethodDefinitionIsTrue() { + assertIsSyntaxErrorES6( + "({ method() { super(); }});", + "super should be inside a shorthand function (test#1)"); + } + + @Test + void superCannotBeUsedInAPropertyValue() { + assertIsSyntaxErrorES6( + "var o = { a: super.b }", + "super should be inside a shorthand function (test#1)"); + } + + @Test + void superCannotBeUsedInAComputedPropertyName() { + assertIsSyntaxErrorES6( + "var o = { [super.x]: 42 }", + "super should be inside a shorthand function (test#1)"); + } + + @Test + void superCannotBeUsedInANonShorthandMethod() { + assertIsSyntaxErrorES6( + "var o = { f: function() { super.x } }", + "super should be inside a shorthand function (test#1)"); + } + + @Test + void superCannotHaveOptionalPropertyAccess() { + assertIsSyntaxErrorES6( + "var o = { f() { super?.x } }", + "super is not allowed in an optional chaining expression (test#1)"); + } + + private void assertIsSyntaxErrorES6(String source, String expected) { + try (Context cx = Context.enter()) { + cx.setLanguageVersion(Context.VERSION_ES6); + EvaluatorException err = + assertThrows( + EvaluatorException.class, + () -> cx.compileString(source, "test", 1, null)); + assertEquals(expected, err.getMessage()); + } + } + } + + @Nested + class PropertyRead { + @Test + void byName() { + String script = + "" + + "const a = { x: 1 };\n" + + "const b = {\n" + + " x: 2,\n" + + " f() {\n" + + " return super.x;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void byIndex() { + String script = + "" + + "const a = { [42]: 1 };\n" + + "const b = {\n" + + " f() {\n" + + " return super[42];\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void byElementString() { + String script = + "" + + "const a = { x: 1 };\n" + + "const b = {\n" + + " x: 2,\n" + + " f() {\n" + + " return super['x'];\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void byElementIndex() { + String script = + "" + + "const a = { [42]: 1 };\n" + + "const b = {\n" + + " [42]: 2,\n" + + " f() {\n" + + " return super['42'];\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void byElementSymbol() { + String script = + "" + + "const s = Symbol();\n" + + "const a = { [s]: 1 };\n" + + "const b = {\n" + + " [s]: 2,\n" + + " f() {\n" + + " return super[s];\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void getter() { + String script = + "" + + "const a = { x: 1 };\n" + + "const b = {\n" + + " get f() {\n" + + " return super.x;\n" + + " }\n" + + "};\n" + + "\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f;"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void getterWithThis() { + String script = + "" + + "const a = { x: 'a', get y() { return this.x + 'x'; } };\n" + + "const b = { x: 'b', get y() { return super['y'] + 'y'; } };\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.y;"; + Utils.assertWithAllOptimizationLevelsES6("bxy", script); + } + + @Test + void usesHomeObjectAndIgnoresThis1() { + String script = + "" + + "const a = { x: 1 };\n" + + "const b = {\n" + + " f() {\n" + + " return super.x;\n" + + " }\n" + + "};\n" + + "\n" + + "Object.setPrototypeOf(b, a);\n" + + "var fn = b.f;\n" + + "fn()\n"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void usesHomeObjectAndIgnoresThis2() { + String script = + "" + + "const a = { x: 1 };\n" + + "const b = {\n" + + " f() {\n" + + " return super.x;\n" + + " }\n" + + "};\n" + + "\n" + + "Object.setPrototypeOf(b, a);\n" + + "var fn = b.f;\n" + + "var c = {x: 42};\n" + + "var d = { fn };\n" + + "Object.setPrototypeOf(d, c)\n" + + "d.fn()\n"; + Utils.assertWithAllOptimizationLevelsES6(1, script); + } + + @Test + void nestedObjects() { + String script = + "" + + "const protoX = { x: 'x' };\n" + + "const protoY = { y: 'y' };\n" + + "\n" + + "const obj = {\n" + + " f() {\n" + + " var nested = { g() { return super.x; } };\n" + + " Object.setPrototypeOf(nested, protoX);\n" + + " return nested.g() + super.y;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(obj, protoY);\n" + + "obj.f();"; + Utils.assertWithAllOptimizationLevelsES6("xy", script); + } + + @Test + void nestedObjectsLambda() { + String script = + "" + + "const protoX = { x: 'x' };\n" + + "const protoY = { y: 'y' };\n" + + "\n" + + "const obj = {\n" + + " f() {\n" + + " var nested = { g() { return () => super.x; } };\n" + + " Object.setPrototypeOf(nested, protoX);\n" + + " return nested.g()() + super.y;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(obj, protoY);\n" + + "obj.f();"; + Utils.assertWithAllOptimizationLevelsES6("xy", script); + } + + @Test + void getPropNoWarnPropertyFound() { + String script = + "" + + "const a = { x: 1 };\n" + + "const b = {\n" + + " x: 'a string',\n" + + " f() {\n" + + " return typeof super.x;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6("number", script); + } + + @Test + void getPropNoWarnPropertyMissing() { + String script = + "" + + "const a = {};\n" + + "const b = {\n" + + " x: 'a string',\n" + + " f() {\n" + + " return typeof super.x;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6("undefined", script); + } + + @Test + void getPropMissingWithContextFeatureStrictMode() { + ContextFactory factory = + new ContextFactory() { + @Override + protected boolean hasFeature(Context cx, int featureIndex) { + if (featureIndex == Context.FEATURE_STRICT_MODE) { + return true; + } + return super.hasFeature(cx, featureIndex); + } + }; + + Utils.runWithAllOptimizationLevels( + factory, + cx -> { + AtomicBoolean warningReported = new AtomicBoolean(false); + cx.setErrorReporter( + new ErrorReporter() { + @Override + public void warning( + String message, + String sourceName, + int line, + String lineSource, + int lineOffset) { + assertEquals( + "Reference to undefined property \"x\"", message); + warningReported.set(true); + } + + @Override + public void error( + String message, + String sourceName, + int line, + String lineSource, + int lineOffset) { + fail("should not have been called"); + } + + @Override + public EvaluatorException runtimeError( + String message, + String sourceName, + int line, + String lineSource, + int lineOffset) { + fail("should not have been called"); + return null; + } + }); + + cx.setLanguageVersion(Context.VERSION_ES6); + String script = + "" + + "const a = {};\n" + + "const b = {\n" + + " x: 'a string',\n" + + " f() {\n" + + " return super.x;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Object result = + cx.evaluateString( + cx.initStandardObjects(), script, "test", 1, null); + assertEquals(Undefined.instance, result); + + assertTrue(warningReported.get()); + return null; + }); + } + + @Test + void propertyNotFoundInSuper() { + // super is implicitly Object.prototype here + String script = + "" + + "const o = {\n" + + " f() {\n" + + " return super.x;\n" + + " }," + + " x: 42\n" + + "};\n" + + "o.f();"; + Utils.assertWithAllOptimizationLevelsES6(Undefined.instance, script); + } + + @Test + void prototypeIsNull() { + String script = + "" + + "var obj = {\n" + + " method() {\n" + + " super.x;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(obj, null);\n" + + "obj.method();"; + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + EcmaError error = + assertThrows( + EcmaError.class, + () -> + cx.evaluateString( + cx.initStandardObjects(), + script, + "test", + 1, + null)); + assertEquals( + "TypeError: Cannot read property \"x\" from null (test#3)", + error.getMessage()); + return null; + }); + } + } + + @Nested + class PropertyWrite { + @Test + void byName() { + String script = + "" + + "var proto = { x: 'proto' };" + + "var object = {\n" + + " x: 'obj',\n" + + " f() { super.x = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f();" + + "object.x + ':' + proto.x"; + Utils.assertWithAllOptimizationLevelsES6("new:proto", script); + } + + @Test + void byIndex() { + String script = + "" + + "var proto = { [42]: 'proto' };" + + "var object = {\n" + + " [42]: 'obj',\n" + + " f() { super[42] = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f();" + + "object[42] + ':' + proto[42]"; + Utils.assertWithAllOptimizationLevelsES6("new:proto", script); + } + + @Test + void byElementString() { + String script = + "" + + "var proto = { x: 'proto' };" + + "var object = {\n" + + " x: 'obj',\n" + + " f() { super['x'] = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f();" + + "object.x+ ':' + proto.x"; + Utils.assertWithAllOptimizationLevelsES6("new:proto", script); + } + + @Test + void byElementIndex() { + String script = + "" + + "var proto = { [42]: 'proto' };" + + "var object = {\n" + + " [42]: 'obj',\n" + + " f() { super['42'] = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f();" + + "object[42] + ':' + proto[42]"; + Utils.assertWithAllOptimizationLevelsES6("new:proto", script); + } + + @Test + void byElementSymbol() { + String script = + "" + + "const s = Symbol();" + + "var proto = { [s]: 'proto' };" + + "var object = {\n" + + " [s]: 'obj',\n" + + " f() { super[s] = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f();" + + "object[s] + ':' + proto[s]"; + Utils.assertWithAllOptimizationLevelsES6("new:proto", script); + } + + @Test + void setter() { + String script = + "" + + "var proto = {\n" + + " x: 0\n" + + "};\n" + + "var object = {\n" + + " set f(v) {\n" + + " super.x = v;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f = 1;\n" + + "object.x + ':' + proto.x"; + Utils.assertWithAllOptimizationLevelsES6("1:0", script); + } + + @Test + void setterWithThis() { + String script = + "" + + "var proto = {\n" + + " _x: 0,\n" + + " set x(v) {\n" + + " return this._x = v;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " set x(v) {\n" + + " super.x = v;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.x = 1;\n" + + "object._x + ':' + proto._x"; + Utils.assertWithAllOptimizationLevelsES6("1:0", script); + } + + @Test + void propertyNotFoundInSuper() { + // super is implicitly Object.prototype here + String script = + "" + + "const o = {\n" + + " f() {\n" + + " super.x = 1;\n" + + " }," + + " x: 42\n" + + "};\n" + + "o.f();\n" + + "o.x + ':' + Object.prototype.x"; + Utils.assertWithAllOptimizationLevelsES6("1:undefined", script); + } + + @Test + void superPropertyNotWritableIgnoredSilentlyInNonStrictMode() { + String script = + "\n" + + "var proto = { x: 'proto' };\n" + + "var object = {\n" + + " x: 'obj',\n" + + " f() { super.x = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "Object.defineProperty(proto, 'x', {writable: false});\n" + + "object.f();\n" + + "object.x + ':' + proto.x"; + ; + Utils.assertWithAllOptimizationLevelsES6("obj:proto", script); + } + + @Test + void thisPropertyNotWritableIgnoredSilentlyInNonStrictMode() { + String script = + "\n" + + "var proto = { x: 'proto' };\n" + + "var object = {\n" + + " x: 'obj',\n" + + " f() { super.x = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "Object.defineProperty(object, 'x', {writable: false});\n" + + "object.f();\n" + + "object.x + ':' + proto.x"; + ; + Utils.assertWithAllOptimizationLevelsES6("obj:proto", script); + } + + @Test + void superPropertyNotWritableInStrictIsError() { + String script = + "\n" + + "'use strict';\n" + + "var proto = { x: 'proto' };\n" + + "var object = {\n" + + " x: 'obj',\n" + + " f() { super.x = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "Object.defineProperty(proto, 'x', {writable: false});\n" + + "object.f();\n" + + "object.x + ':' + proto.x"; + ; + assertThrowsTypeErrorCannotWriteProperty(script); + } + + @Test + void thisPropertyNotWritableInStrictIsError() { + String script = + "\n" + + "'use strict';\n" + + "var proto = { x: 'proto' };\n" + + "var object = {\n" + + " x: 'obj',\n" + + " f() { super.x = 'new'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "Object.defineProperty(object, 'x', {writable: false});\n" + + "object.f();\n" + + "object.x + ':' + proto.x"; + ; + assertThrowsTypeErrorCannotWriteProperty(script); + } + + private void assertThrowsTypeErrorCannotWriteProperty(String script) { + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + EcmaError error = + assertThrows( + EcmaError.class, + () -> + cx.evaluateString( + cx.initStandardObjects(), + script, + "test", + 1, + null)); + assertEquals( + "TypeError: Cannot modify readonly property: x. (test#6)", + error.getMessage()); + return null; + }); + } + + @Test + void prototypeIsNull() { + String script = + "" + + "var obj = {\n" + + " method() {\n" + + " super.x = 42;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(obj, null);\n" + + "obj.method();"; + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + EcmaError error = + assertThrows( + EcmaError.class, + () -> + cx.evaluateString( + cx.initStandardObjects(), + script, + "test", + 1, + null)); + assertEquals( + "TypeError: Cannot set property \"x\" of null to \"42\" (test#3)", + error.getMessage()); + return null; + }); + } + + @Test + void missingPropertyPrototypeSealedCreatesItOnTheThisObject() { + String script = + "\n" + + "var proto = {};\n" + + "var object = {\n" + + " f() { super.x = 1; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "Object.seal(proto);\n" + + "object.f();\n" + + "object.x + ':' + proto.x"; + ; + Utils.assertWithAllOptimizationLevelsES6("1:undefined", script); + } + + @Test + void missingPropertyThisSealedIsIgnoredSilentlyInNonStrictMode() { + String script = + "\n" + + "var proto = {};\n" + + "var object = {\n" + + " f() { super.x = 1; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "Object.seal(object);\n" + + "object.f();\n" + + "object.x + ':' + proto.x"; + ; + Utils.assertWithAllOptimizationLevelsES6("undefined:undefined", script); + } + + @Test + void missingPropertyThisSealedIsErrorInStrictMode() { + String script = + "\n" + + "'use strict';\n" + + "var proto = {};\n" + + "var object = {\n" + + " f() { super.x = 1; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "Object.seal(object);\n" + + "object.f();\n" + + "object.x + ':' + proto.x"; + ; + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + EcmaError error = + assertThrows( + EcmaError.class, + () -> + cx.evaluateString( + cx.initStandardObjects(), + script, + "test", + 1, + null)); + assertEquals( + "TypeError: Cannot add properties to this object because extensible is false. (test#5)", + error.getMessage()); + return null; + }); + } + + @Test + void modifyOperatorByName() { + String script = + "" + + "var proto = { x: 'proto' };" + + "var object = {\n" + + " x: 'obj',\n" + + " f() { super.x += '1'; }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f();" + + "object.x + ':' + proto.x"; + Utils.assertWithAllOptimizationLevelsES6("proto1:proto", script); + } + } + + @Nested + class MethodCall { + @Test + void methodsAreResolvedOnSuperObject() { + final String script = + "" + + "const proto = {\n" + + " f(x) {\n" + + " return 'prototype' + x;\n" + + " }\n" + + "};\n" + + "const obj = {\n" + + " f() {\n" + + " return super.f(1);\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(obj, proto);\n" + + "obj.f();"; + Utils.assertWithAllOptimizationLevelsES6("prototype1", script); + } + + // All the n-arguments variants are necessary because we have optimized code paths in + // compiled classes + // for 0, 1, 2, and N arguments + + @Test + void thisIsSetCorrectly0Args() { + String script = + "" + + "var proto = {\n" + + " x: 'proto',\n" + + " f() {\n" + + " return this.x;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " x: 'object',\n" + + " g() {\n" + + " return super.f();\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.g();"; + Utils.assertWithAllOptimizationLevelsES6("object", script); + } + + @Test + void thisIsSetCorrectly1Arg() { + String script = + "" + + "var proto = {\n" + + " x: 'proto',\n" + + " f(a) {\n" + + " return this.x + ':' + a;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " x: 'object',\n" + + " g() {\n" + + " return super.f('a');\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.g();"; + Utils.assertWithAllOptimizationLevelsES6("object:a", script); + } + + @Test + void thisIsSetCorrectly2Args() { + String script = + "" + + "var proto = {\n" + + " x: 'proto',\n" + + " f(a, b) {\n" + + " return this.x + ':' + a + ':' + b;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " x: 'object',\n" + + " g() {\n" + + " return super.f('a', 'b');\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.g();"; + Utils.assertWithAllOptimizationLevelsES6("object:a:b", script); + } + + @Test + void thisIsSetCorrectlyNArgs() { + String script = + "" + + "var proto = {\n" + + " x: 'proto',\n" + + " f(a, b, c) {\n" + + " return this.x + ':' + a + ':' + b + ':' + c;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " x: 'object',\n" + + " g() {\n" + + " return super.f('a', 'b', 'c');\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.g();"; + Utils.assertWithAllOptimizationLevelsES6("object:a:b:c", script); + } + + @Test + void lastScratchScriptableIsCleanedUpProperly() { + String script = + "" + + "function f1() { return 'f1'; }\n" + + "var proto = {\n" + + " f2() {\n" + + " return 'f2';\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " f3() {\n" + + " return super.f2() + f1();\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f3();"; + Utils.assertWithAllOptimizationLevelsES6("f2f1", script); + } + + @Test + void thisIsSetCorrectlyForTemplateLiteralCall() { + String script = + "" + + "var proto = {\n" + + " x: 'proto',\n" + + " f() {\n" + + " return this.x;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " x: 'object',\n" + + " f() {\n" + + " return super.f`some ignored string`;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f();"; + Utils.assertWithAllOptimizationLevelsES6("object", script); + } + + @Test + void nestedLambdaCaptureSuper() { + String script = + "" + + "var proto = {\n" + + " x: 'proto',\n" + + " f() {\n" + + " return this.x;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " x: 'object',\n" + + " f() {\n" + + " return () => { return super.f(); } ;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f()();"; + Utils.assertWithAllOptimizationLevelsES6("object", script); + } + + @Test + void doublyNestedLambdaCaptureSuper() { + String script = + "" + + "var proto = {\n" + + " x: 'proto',\n" + + " f() {\n" + + " return this.x;\n" + + " }\n" + + "};\n" + + "var object = {\n" + + " x: 'object',\n" + + " f() {\n" + + " return () => { return () => { return super.f(); } } ;\n" + + " }\n" + + "};\n" + + "Object.setPrototypeOf(object, proto);\n" + + "object.f()()();"; + Utils.assertWithAllOptimizationLevelsES6("object", script); + } + } +} diff --git a/tests/src/test/java/org/mozilla/javascript/tests/ComputedPropertiesTest.java b/tests/src/test/java/org/mozilla/javascript/tests/ComputedPropertiesTest.java index 91d0c435ca..16d77b10d4 100644 --- a/tests/src/test/java/org/mozilla/javascript/tests/ComputedPropertiesTest.java +++ b/tests/src/test/java/org/mozilla/javascript/tests/ComputedPropertiesTest.java @@ -265,7 +265,7 @@ public void unsupportedInDestructuringInFunctionArguments() { public void unsupportedInDestructuringInVariableDeclaration() { String script = "var { [a]: b } = {};"; assertComputedPropertiesAreUnsupportedInDestructuring( - script, "Unsupported computed property in destructuring."); + script, "Unsupported computed property in destructuring. (test#1)"); } private void assertComputedPropertiesAreUnsupportedInDestructuring( diff --git a/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java b/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java index 8273bc1926..92304b01ad 100644 --- a/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java +++ b/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java @@ -111,7 +111,6 @@ public class Test262SuiteTest { "regexp-named-groups", "regexp-unicode-property-escapes", "resizable-arraybuffer", - "super", "tail-call-optimization", "u180e")); diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index a5b400211e..98ce45ff85 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -3512,16 +3512,13 @@ language/comments 9/52 (17.31%) multi-line-asi-line-separator.js multi-line-asi-paragraph-separator.js -language/computed-property-names 37/48 (77.08%) +language/computed-property-names 34/48 (70.83%) class/accessor 4/4 (100.0%) class/method 11/11 (100.0%) class/static 14/14 (100.0%) object/accessor/getter.js - object/accessor/getter-super.js object/accessor/setter.js - object/accessor/setter-super.js object/method/generator.js - object/method/super.js to-name-side-effects/class.js to-name-side-effects/numbers-class.js @@ -3555,7 +3552,7 @@ language/directive-prologue 18/62 (29.03%) 14.1-9-s.js {non-strict: [-1]} func-decl-inside-func-decl-parse.js non-strict -language/eval-code 253/347 (72.91%) +language/eval-code 242/347 (69.74%) direct/arrow-fn-a-following-parameter-is-named-arguments-arrow-func-declare-arguments-assign.js non-strict direct/arrow-fn-a-following-parameter-is-named-arguments-arrow-func-declare-arguments-assign-incl-def-param-arrow-arguments.js non-strict direct/arrow-fn-a-preceding-parameter-is-named-arguments-arrow-func-declare-arguments-assign.js non-strict @@ -3759,16 +3756,7 @@ language/eval-code 253/347 (72.91%) direct/new.target-arrow.js {unsupported: [new.target]} direct/new.target-fn.js {unsupported: [new.target]} direct/non-definable-global-var.js non-strict - direct/super-call.js {unsupported: [super]} - direct/super-call-arrow.js {unsupported: [super]} - direct/super-call-fn.js {unsupported: [super]} - direct/super-call-method.js {unsupported: [super]} - direct/super-prop.js {unsupported: [super]} - direct/super-prop-arrow.js {unsupported: [super]} - direct/super-prop-dot-no-home.js {unsupported: [super]} - direct/super-prop-expr-no-home.js {unsupported: [super]} - direct/super-prop-expr-no-home-no-eval.js {unsupported: [super]} - direct/super-prop-method.js {unsupported: [super]} + direct/super-prop-method.js direct/switch-case-decl-eval-source-is-strict-nostrict.js non-strict direct/switch-case-decl-eval-source-is-strict-onlystrict.js strict direct/switch-case-decl-onlystrict.js strict @@ -3801,8 +3789,6 @@ language/eval-code 253/347 (72.91%) indirect/non-definable-function-with-variable.js indirect/non-definable-global-var.js non-strict indirect/realm.js - indirect/super-call.js {unsupported: [super]} - indirect/super-prop.js {unsupported: [super]} indirect/switch-case-decl-strict.js indirect/switch-dflt-decl-strict.js indirect/var-env-func-init-global-update-configurable.js @@ -4967,7 +4953,7 @@ language/expressions/new 41/59 (69.49%) ~language/expressions/new.target -language/expressions/object 789/1169 (67.49%) +language/expressions/object 785/1169 (67.15%) dstr/async-gen-meth-ary-init-iter-close.js {unsupported: [async-iteration, async]} dstr/async-gen-meth-ary-init-iter-get-err.js {unsupported: [async-iteration]} dstr/async-gen-meth-ary-init-iter-get-err-array-prototype.js {unsupported: [async-iteration]} @@ -5633,7 +5619,7 @@ language/expressions/object 789/1169 (67.49%) method-definition/generator-prototype-prop.js method-definition/generator-return.js method-definition/generator-super-prop-body.js - method-definition/generator-super-prop-param.js {unsupported: [super]} + method-definition/generator-super-prop-param.js method-definition/meth-array-destructuring-param-strict-body.js method-definition/meth-dflt-params-duplicates.js non-strict method-definition/meth-dflt-params-ref-later.js @@ -5654,8 +5640,7 @@ language/expressions/object 789/1169 (67.49%) method-definition/name-prop-name-yield-expr.js non-strict method-definition/name-prop-name-yield-id.js non-strict method-definition/name-prototype-prop.js - method-definition/name-super-prop-body.js {unsupported: [super]} - method-definition/name-super-prop-param.js {unsupported: [super]} + method-definition/name-super-prop-param.js method-definition/object-method-returns-promise.js {unsupported: [async-functions]} method-definition/params-dflt-gen-meth-args-unmapped.js method-definition/params-dflt-gen-meth-ref-arguments.js @@ -5714,14 +5699,12 @@ language/expressions/object 789/1169 (67.49%) getter-body-strict-inside.js non-strict getter-body-strict-outside.js strict getter-param-dflt.js - getter-super-prop.js ident-name-prop-name-literal-await-static-init.js identifier-shorthand-await-strict-mode.js non-strict identifier-shorthand-static-init-await-valid.js let-non-strict-access.js non-strict let-non-strict-syntax.js non-strict literal-property-name-bigint.js {unsupported: [class]} - method.js object-spread-proxy-get-not-called-on-dontenum-keys.js {unsupported: [Proxy]} object-spread-proxy-no-excluded-keys.js {unsupported: [Proxy]} object-spread-proxy-ownkeys-returned-keys-order.js {unsupported: [Proxy]} @@ -5754,7 +5737,6 @@ language/expressions/object 789/1169 (67.49%) setter-length-dflt.js setter-param-arguments-strict-inside.js non-strict setter-param-eval-strict-inside.js non-strict - setter-super-prop.js yield-non-strict-access.js non-strict yield-non-strict-syntax.js non-strict @@ -5838,7 +5820,77 @@ language/expressions/strict-equals 0/30 (0.0%) language/expressions/subtraction 1/38 (2.63%) order-of-evaluation.js -~language/expressions/super +language/expressions/super 70/86 (81.4%) + call-arg-evaluation-err.js {unsupported: [class]} + call-bind-this-value.js {unsupported: [class]} + call-bind-this-value-twice.js {unsupported: [class]} + call-construct-error.js {unsupported: [class]} + call-construct-invocation.js {unsupported: [Reflect.construct, Reflect, new.target, class]} + call-expr-value.js {unsupported: [class]} + call-poisoned-underscore-proto.js {unsupported: [class]} + call-proto-not-ctor.js {unsupported: [class]} + call-spread-err-mult-err-expr-throws.js + call-spread-err-mult-err-iter-get-value.js + call-spread-err-mult-err-itr-get-call.js + call-spread-err-mult-err-itr-get-get.js + call-spread-err-mult-err-itr-step.js + call-spread-err-mult-err-itr-value.js + call-spread-err-mult-err-obj-unresolvable.js + call-spread-err-mult-err-unresolvable.js + call-spread-err-sngl-err-expr-throws.js + call-spread-err-sngl-err-itr-get-call.js + call-spread-err-sngl-err-itr-get-get.js + call-spread-err-sngl-err-itr-get-value.js + call-spread-err-sngl-err-itr-step.js + call-spread-err-sngl-err-itr-value.js + call-spread-err-sngl-err-obj-unresolvable.js + call-spread-err-sngl-err-unresolvable.js + call-spread-mult-empty.js + call-spread-mult-expr.js + call-spread-mult-iter.js + call-spread-mult-literal.js + call-spread-mult-obj-ident.js + call-spread-mult-obj-null.js + call-spread-mult-obj-undefined.js + call-spread-obj-getter-descriptor.js + call-spread-obj-getter-init.js + call-spread-obj-manipulate-outter-obj-in-getter.js + call-spread-obj-mult-spread.js + call-spread-obj-mult-spread-getter.js + call-spread-obj-null.js + call-spread-obj-override-immutable.js + call-spread-obj-overrides-prev-properties.js + call-spread-obj-skip-non-enumerable.js + call-spread-obj-spread-order.js + call-spread-obj-symbol-property.js + call-spread-obj-undefined.js + call-spread-obj-with-overrides.js + call-spread-sngl-empty.js + call-spread-sngl-expr.js + call-spread-sngl-iter.js + call-spread-sngl-literal.js + call-spread-sngl-obj-ident.js + prop-dot-cls-null-proto.js {unsupported: [class]} + prop-dot-cls-ref-strict.js {unsupported: [class]} + prop-dot-cls-ref-this.js + prop-dot-cls-this-uninit.js {unsupported: [class]} + prop-dot-cls-val.js {unsupported: [class]} + prop-dot-cls-val-from-arrow.js {unsupported: [class]} + prop-dot-cls-val-from-eval.js {unsupported: [class]} + prop-dot-obj-val-from-eval.js + prop-expr-cls-err.js {unsupported: [class]} + prop-expr-cls-key-err.js {unsupported: [class]} + prop-expr-cls-null-proto.js {unsupported: [class]} + prop-expr-cls-ref-strict.js {unsupported: [class]} + prop-expr-cls-ref-this.js + prop-expr-cls-this-uninit.js {unsupported: [class]} + prop-expr-cls-unresolvable.js {unsupported: [class]} + prop-expr-cls-val.js {unsupported: [class]} + prop-expr-cls-val-from-arrow.js {unsupported: [class]} + prop-expr-cls-val-from-eval.js {unsupported: [class]} + prop-expr-obj-val-from-eval.js + realm.js {unsupported: [Reflect]} + super-reference-resolution.js {unsupported: [class]} language/expressions/tagged-template 3/27 (11.11%) call-expression-context-strict.js strict @@ -6004,7 +6056,7 @@ language/future-reserved-words 7/55 (12.73%) public.js non-strict static.js non-strict -language/global-code 30/42 (71.43%) +language/global-code 26/42 (61.9%) block-decl-strict.js strict decl-lex.js decl-lex-configurable-global.js @@ -6028,10 +6080,6 @@ language/global-code 30/42 (71.43%) script-decl-var.js script-decl-var-collision.js script-decl-var-err.js non-strict - super-call.js {unsupported: [super]} - super-call-arrow.js {unsupported: [super]} - super-prop.js {unsupported: [super]} - super-prop-arrow.js {unsupported: [super]} switch-case-decl-strict.js strict switch-dflt-decl-strict.js strict yield-non-strict.js non-strict