Changeset 200102 in webkit


Ignore:
Timestamp:
Apr 26, 2016 11:40:41 AM (8 years ago)
Author:
gskachkov@gmail.com
Message:

calling super() a second time in a constructor should throw
https://bugs.webkit.org/show_bug.cgi?id=151113

Reviewed by Saam Barati and Keith Miller.

Source/JavaScriptCore:

Currently, our implementation checks if 'super()' was called in a constructor more
than once and raises a RuntimeError before the second call. According to the spec
we need to raise an error just after the second super() is finished and before
the new 'this' is assigned https://esdiscuss.org/topic/duplicate-super-call-behaviour.
To implement this behavior this patch adds a new op code, op_is_empty, that is used
to check if 'this' is empty.

  • bytecode/BytecodeList.json:
  • bytecode/BytecodeUseDef.h:

(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dumpBytecode):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitIsEmpty):

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::FunctionCallValueNode::emitBytecode):

  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNodeType.h:
  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileIsEmpty):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

  • jit/JIT.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_is_empty):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_is_empty):

  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • tests/stress/class-syntax-double-constructor.js: Added.

LayoutTests:

  • js/class-syntax-super-expected.txt:
  • js/script-tests/class-syntax-super.js:
Location:
trunk
Files:
1 added
29 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r200084 r200102  
     12016-04-26  Skachkov Oleksandr  <gskachkov@gmail.com>
     2
     3        calling super() a second time in a constructor should throw
     4        https://bugs.webkit.org/show_bug.cgi?id=151113
     5
     6        Reviewed by Saam Barati and Keith Miller.
     7
     8        * js/class-syntax-super-expected.txt:
     9        * js/script-tests/class-syntax-super.js:
     10
    1112016-04-26  Commit Queue  <commit-queue@webkit.org>
    212
  • trunk/LayoutTests/js/class-syntax-super-expected.txt

    r200084 r200102  
    5050PASS new (class { constructor() { (function () { eval("super()");})(); } }):::SyntaxError: super is not valid in this context.
    5151PASS (new (class { method() { (function () { eval("super.method()");})(); }})).method():::SyntaxError: super is not valid in this context.
     52PASS new (class extends Base { constructor() { super(); super();}}):::ReferenceError: 'super()' can't be called more than once in a constructor.
    5253PASS successfullyParsed
    5354
  • trunk/LayoutTests/js/script-tests/class-syntax-super.js

    r200084 r200102  
    136136shouldThrow('(new (class { method() { (function () { eval("super.method()");})(); }})).method()', '"SyntaxError: super is not valid in this context."');
    137137
     138shouldThrow('new (class extends Base { constructor() { super(); super();}})', '"ReferenceError: \'super()\' can\'t be called more than once in a constructor."');
    138139var successfullyParsed = true;
  • trunk/Source/JavaScriptCore/ChangeLog

    r200101 r200102  
     12016-04-26  Skachkov Oleksandr  <gskachkov@gmail.com>
     2
     3        calling super() a second time in a constructor should throw
     4        https://bugs.webkit.org/show_bug.cgi?id=151113
     5
     6        Reviewed by Saam Barati.
     7
     8        Currently, our implementation checks if 'super()' was called in a constructor more
     9        than once and raises a RuntimeError before the second call. According to the spec
     10        we need to raise an error just after the second super() is finished and before
     11        the new 'this' is assigned https://esdiscuss.org/topic/duplicate-super-call-behaviour.
     12        To implement this behavior this patch adds a new op code, op_is_empty, that is used
     13        to check if 'this' is empty.
     14
     15        * bytecode/BytecodeList.json:
     16        * bytecode/BytecodeUseDef.h:
     17        (JSC::computeUsesForBytecodeOffset):
     18        (JSC::computeDefsForBytecodeOffset):
     19        * bytecode/CodeBlock.cpp:
     20        (JSC::CodeBlock::dumpBytecode):
     21        * bytecompiler/BytecodeGenerator.cpp:
     22        (JSC::BytecodeGenerator::emitIsEmpty):
     23        * bytecompiler/BytecodeGenerator.h:
     24        * bytecompiler/NodesCodegen.cpp:
     25        (JSC::FunctionCallValueNode::emitBytecode):
     26        * dfg/DFGAbstractInterpreterInlines.h:
     27        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
     28        * dfg/DFGByteCodeParser.cpp:
     29        (JSC::DFG::ByteCodeParser::parseBlock):
     30        * dfg/DFGCapabilities.cpp:
     31        (JSC::DFG::capabilityLevel):
     32        * dfg/DFGClobberize.h:
     33        (JSC::DFG::clobberize):
     34        * dfg/DFGDoesGC.cpp:
     35        (JSC::DFG::doesGC):
     36        * dfg/DFGFixupPhase.cpp:
     37        (JSC::DFG::FixupPhase::fixupNode):
     38        * dfg/DFGNodeType.h:
     39        * dfg/DFGPredictionPropagationPhase.cpp:
     40        * dfg/DFGSafeToExecute.h:
     41        (JSC::DFG::safeToExecute):
     42        * dfg/DFGSpeculativeJIT32_64.cpp:
     43        (JSC::DFG::SpeculativeJIT::compile):
     44        * dfg/DFGSpeculativeJIT64.cpp:
     45        (JSC::DFG::SpeculativeJIT::compile):
     46        * ftl/FTLCapabilities.cpp:
     47        (JSC::FTL::canCompile):
     48        * ftl/FTLLowerDFGToB3.cpp:
     49        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
     50        (JSC::FTL::DFG::LowerDFGToB3::compileIsEmpty):
     51        * jit/JIT.cpp:
     52        (JSC::JIT::privateCompileMainPass):
     53        * jit/JIT.h:
     54        * jit/JITOpcodes.cpp:
     55        (JSC::JIT::emit_op_is_empty):
     56        * jit/JITOpcodes32_64.cpp:
     57        (JSC::JIT::emit_op_is_empty):
     58        * llint/LowLevelInterpreter32_64.asm:
     59        * llint/LowLevelInterpreter64.asm:
     60        * tests/stress/class-syntax-double-constructor.js: Added.
     61
    1622016-04-26  Mark Lam  <mark.lam@apple.com>
    263
  • trunk/Source/JavaScriptCore/bytecode/BytecodeList.json

    r200084 r200102  
    5050            { "name" : "op_instanceof_custom", "length" : 5 },
    5151            { "name" : "op_typeof", "length" : 3 },
     52            { "name" : "op_is_empty", "length" : 3 },
    5253            { "name" : "op_is_undefined", "length" : 3 },
    5354            { "name" : "op_is_boolean", "length" : 3 },
  • trunk/Source/JavaScriptCore/bytecode/BytecodeUseDef.h

    r200084 r200102  
    148148    case op_get_array_length:
    149149    case op_typeof:
     150    case op_is_empty:
    150151    case op_is_undefined:
    151152    case op_is_boolean:
     
    377378    case op_get_by_val:
    378379    case op_typeof:
     380    case op_is_empty:
    379381    case op_is_undefined:
    380382    case op_is_boolean:
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r200084 r200102  
    10651065        case op_typeof: {
    10661066            printUnaryOp(out, exec, location, it, "typeof");
     1067            break;
     1068        }
     1069        case op_is_empty: {
     1070            printUnaryOp(out, exec, location, it, "is_empty");
    10671071            break;
    10681072        }
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r200084 r200102  
    41194119}
    41204120
     4121RegisterID* BytecodeGenerator::emitIsEmpty(RegisterID* dst, RegisterID* src)
     4122{
     4123    emitOpcode(op_is_empty);
     4124    instructions().append(dst->index());
     4125    instructions().append(src->index());
     4126    return dst;
     4127}
     4128   
    41214129RegisterID* BytecodeGenerator::emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node)
    41224130{
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r200084 r200102  
    627627        RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
    628628        RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
     629        RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src);
    629630        void emitRequireObjectCoercible(RegisterID* value, const String& error);
    630631
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r200084 r200102  
    764764        generator.emitMove(callArguments.thisRegister(), generator.newTarget());
    765765        RegisterID* ret = generator.emitConstruct(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
     766
     767        bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Derived;
     768        bool doWeUseArrowFunctionInConstructor = isConstructorKindDerived && generator.needsToUpdateArrowFunctionContext();
     769
     770        if (generator.isDerivedConstructorContext() || (doWeUseArrowFunctionInConstructor && generator.isSuperCallUsedInInnerArrowFunction()))
     771            generator.emitLoadThisFromArrowFunctionLexicalEnvironment();
     772       
     773        RefPtr<Label> thisIsEmptyLabel = generator.newLabel();
     774        generator.emitJumpIfTrue(generator.emitIsEmpty(generator.newTemporary(), generator.thisRegister()), thisIsEmptyLabel.get());
     775        generator.emitThrowReferenceError(ASCIILiteral("'super()' can't be called more than once in a constructor."));
     776        generator.emitLabel(thisIsEmptyLabel.get());
     777
    766778        generator.emitMove(generator.thisRegister(), ret);
    767779       
    768         bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Derived;
    769         if (generator.isDerivedConstructorContext() || (isConstructorKindDerived && generator.needsToUpdateArrowFunctionContext()))
     780        if (generator.isDerivedConstructorContext() || doWeUseArrowFunctionInConstructor)
    770781            generator.emitPutThisToArrowFunctionContextScope();
    771782       
  • trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h

    r200096 r200102  
    997997    }
    998998
     999    case IsEmpty:
    9991000    case IsArrayObject:
    10001001    case IsJSArray:
     
    10851086                setConstant(node, jsBoolean(child.value().isObject() && child.value().getObject()->type() == RegExpObjectType));
    10861087                break;
     1088            case IsEmpty:
     1089                setConstant(node, jsBoolean(child.value().isEmpty()));
     1090                break;
    10871091            default:
    10881092                constantWasSet = false;
     
    11151119
    11161120            break;
     1121        case IsEmpty: {
     1122            if (child.m_type && !(child.m_type & SpecEmpty)) {
     1123                setConstant(node, jsBoolean(false));
     1124                constantWasSet = true;
     1125                break;
     1126            }
     1127
     1128            if (child.m_type && !(child.m_type & ~SpecEmpty)) {
     1129                setConstant(node, jsBoolean(true));
     1130                constantWasSet = true;
     1131                break;
     1132            }
     1133
     1134            break;
     1135        }
    11171136        case IsUndefined:
    11181137            // FIXME: Use the masquerades-as-undefined watchpoint thingy.
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r200084 r200102  
    37223722            NEXT_OPCODE(op_instanceof_custom);
    37233723        }
    3724 
     3724        case op_is_empty: {
     3725            Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
     3726            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsEmpty, value));
     3727            NEXT_OPCODE(op_is_empty);
     3728        }
    37253729        case op_is_undefined: {
    37263730            Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
  • trunk/Source/JavaScriptCore/dfg/DFGCapabilities.cpp

    r200084 r200102  
    133133    case op_instanceof:
    134134    case op_instanceof_custom:
     135    case op_is_empty:
    135136    case op_is_undefined:
    136137    case op_is_boolean:
  • trunk/Source/JavaScriptCore/dfg/DFGClobberize.h

    r200084 r200102  
    156156    case IsJSArray:
    157157    case IsArrayConstructor:
     158    case IsEmpty:
    158159    case IsUndefined:
    159160    case IsBoolean:
  • trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp

    r200084 r200102  
    157157    case IsJSArray:
    158158    case IsArrayConstructor:
     159    case IsEmpty:
    159160    case IsUndefined:
    160161    case IsBoolean:
  • trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

    r200084 r200102  
    15311531        case IsJSArray:
    15321532        case IsArrayConstructor:
     1533        case IsEmpty:
    15331534        case IsUndefined:
    15341535        case IsBoolean:
  • trunk/Source/JavaScriptCore/dfg/DFGNodeType.h

    r200084 r200102  
    308308    macro(IsJSArray, NodeResultBoolean) \
    309309    macro(IsArrayConstructor, NodeResultBoolean) \
     310    macro(IsEmpty, NodeResultBoolean) \
    310311    macro(IsUndefined, NodeResultBoolean) \
    311312    macro(IsBoolean, NodeResultBoolean) \
  • trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp

    r200034 r200102  
    765765        case IsJSArray:
    766766        case IsArrayConstructor:
     767        case IsEmpty:
    767768        case IsUndefined:
    768769        case IsBoolean:
  • trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h

    r200084 r200102  
    257257    case IsJSArray:
    258258    case IsArrayConstructor:
     259    case IsEmpty:
    259260    case IsUndefined:
    260261    case IsBoolean:
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp

    r200084 r200102  
    44144414    }
    44154415
     4416    case IsEmpty: {       
     4417        JSValueOperand value(this, node->child1());
     4418        GPRTemporary result(this, Reuse, value, TagWord);
     4419        m_jit.comparePtr(JITCompiler::Equal, value.tagGPR(), TrustedImm32(JSValue::EmptyValueTag), result.gpr());
     4420        booleanResult(result.gpr(), node);
     4421        break;
     4422    }
     4423
    44164424    case IsUndefined: {
    44174425        JSValueOperand value(this, node->child1());
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r200084 r200102  
    44334433        break;
    44344434    }
     4435
     4436    case IsEmpty: {       
     4437        JSValueOperand value(this, node->child1());
     4438        GPRTemporary result(this, Reuse, value);
     4439
     4440        m_jit.comparePtr(JITCompiler::Equal, value.gpr(), TrustedImm32(JSValue::encode(JSValue())), result.gpr());
     4441        m_jit.or32(TrustedImm32(ValueFalse), result.gpr());
     4442
     4443        jsValueResult(result.gpr(), node, DataFormatJSBoolean);
     4444        break;
     4445    }
    44354446       
    44364447    case IsUndefined: {
  • trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp

    r200084 r200102  
    180180    case IsJSArray:
    181181    case IsArrayConstructor:
     182    case IsEmpty:
    182183    case IsUndefined:
    183184    case IsBoolean:
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r200096 r200102  
    842842        case InvalidationPoint:
    843843            compileInvalidationPoint();
     844            break;
     845        case IsEmpty:
     846            compileIsEmpty();
    844847            break;
    845848        case IsUndefined:
     
    57595762        // When this abruptly terminates, it could read any heap location.
    57605763        patchpoint->effects.reads = HeapRange::top();
     5764    }
     5765
     5766    void compileIsEmpty()
     5767    {
     5768        setBoolean(m_out.isZero64(lowJSValue(m_node->child1())));
    57615769    }
    57625770   
  • trunk/Source/JavaScriptCore/jit/JIT.cpp

    r200084 r200102  
    236236        DEFINE_OP(op_instanceof)
    237237        DEFINE_OP(op_instanceof_custom)
     238        DEFINE_OP(op_is_empty)
    238239        DEFINE_OP(op_is_undefined)
    239240        DEFINE_OP(op_is_boolean)
  • trunk/Source/JavaScriptCore/jit/JIT.h

    r200084 r200102  
    512512        void emit_op_instanceof(Instruction*);
    513513        void emit_op_instanceof_custom(Instruction*);
     514        void emit_op_is_empty(Instruction*);
    514515        void emit_op_is_undefined(Instruction*);
    515516        void emit_op_is_boolean(Instruction*);
  • trunk/Source/JavaScriptCore/jit/JITOpcodes.cpp

    r200084 r200102  
    176176    addSlowCase(jump());
    177177}
     178   
     179void JIT::emit_op_is_empty(Instruction* currentInstruction)
     180{
     181    int dst = currentInstruction[1].u.operand;
     182    int value = currentInstruction[2].u.operand;
     183
     184    emitGetVirtualRegister(value, regT0);
     185    compare64(Equal, regT0, TrustedImm32(JSValue::encode(JSValue())), regT0);
     186
     187    emitTagBool(regT0);
     188    emitPutVirtualRegister(dst);
     189}
    178190
    179191void JIT::emit_op_is_undefined(Instruction* currentInstruction)
  • trunk/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp

    r200084 r200102  
    291291    emitStoreBool(dst, returnValueGPR);
    292292}
     293   
     294void JIT::emit_op_is_empty(Instruction* currentInstruction)
     295{
     296    int dst = currentInstruction[1].u.operand;
     297    int value = currentInstruction[2].u.operand;
     298   
     299    emitLoad(value, regT1, regT0);
     300    compare32(Equal, regT1, TrustedImm32(JSValue::EmptyValueTag), regT0);
     301
     302    emitStoreBool(dst, regT0);
     303}
    293304
    294305void JIT::emit_op_is_undefined(Instruction* currentInstruction)
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm

    r200084 r200102  
    12111211    callSlowPath(_llint_slow_path_instanceof_custom)
    12121212    dispatch(5)
     1213
     1214
     1215_llint_op_is_empty:
     1216    traceExecution()
     1217    loadi 8[PC], t1
     1218    loadi 4[PC], t0
     1219    loadConstantOrVariable(t1, t2, t3)
     1220    cieq t2, EmptyValueTag, t3
     1221    storei BooleanTag, TagOffset[cfr, t0, 8]
     1222    storei t3, PayloadOffset[cfr, t0, 8]
     1223    dispatch(3)
    12131224
    12141225
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

    r200084 r200102  
    10991099    dispatch(5)
    11001100
     1101
     1102_llint_op_is_empty:
     1103    traceExecution()
     1104    loadisFromInstruction(2, t1)
     1105    loadisFromInstruction(1, t2)
     1106    loadConstantOrVariable(t1, t0)
     1107    cqeq t0, ValueEmpty, t3
     1108    orq ValueFalse, t3
     1109    storeq t3, [cfr, t2, 8]
     1110    dispatch(3)
     1111
     1112
    11011113_llint_op_is_undefined:
    11021114    traceExecution()
Note: See TracChangeset for help on using the changeset viewer.