Source/JavaScriptCore/ChangeLog

 12016-04-08 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 NOBODY (OOPS!).
 7
 8 Current patch implement check if 'super()' was called in constructor
 9 more than once and raise RuntimeError if 'super()' called second time.
 10 According to spec we need to raise error just after second super()
 11 is finished, and before new this is assign
 12 https://esdiscuss.org/topic/duplicate-super-call-behaviour.
 13 To implement this behavior was introduced new op code - op_is_empty
 14 that is used to check if 'this' is empty.
 15
 16 * bytecode/BytecodeList.json:
 17 * bytecode/BytecodeUseDef.h:
 18 (JSC::computeUsesForBytecodeOffset):
 19 (JSC::computeDefsForBytecodeOffset):
 20 * bytecode/CodeBlock.cpp:
 21 (JSC::CodeBlock::dumpBytecode):
 22 * bytecompiler/BytecodeGenerator.cpp:
 23 (JSC::BytecodeGenerator::emitIsEmpty):
 24 * bytecompiler/BytecodeGenerator.h:
 25 * bytecompiler/NodesCodegen.cpp:
 26 (JSC::FunctionCallValueNode::emitBytecode):
 27 * dfg/DFGAbstractInterpreterInlines.h:
 28 (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
 29 * dfg/DFGByteCodeParser.cpp:
 30 (JSC::DFG::ByteCodeParser::parseBlock):
 31 * dfg/DFGCapabilities.cpp:
 32 (JSC::DFG::capabilityLevel):
 33 * dfg/DFGClobberize.h:
 34 (JSC::DFG::clobberize):
 35 * dfg/DFGDoesGC.cpp:
 36 (JSC::DFG::doesGC):
 37 * dfg/DFGFixupPhase.cpp:
 38 (JSC::DFG::FixupPhase::fixupNode):
 39 * dfg/DFGNodeType.h:
 40 * dfg/DFGPredictionPropagationPhase.cpp:
 41 (JSC::DFG::PredictionPropagationPhase::propagate):
 42 * dfg/DFGSafeToExecute.h:
 43 (JSC::DFG::safeToExecute):
 44 * dfg/DFGSpeculativeJIT32_64.cpp:
 45 (JSC::DFG::SpeculativeJIT::compile):
 46 * dfg/DFGSpeculativeJIT64.cpp:
 47 (JSC::DFG::SpeculativeJIT::compile):
 48 * ftl/FTLCapabilities.cpp:
 49 (JSC::FTL::canCompile):
 50 * ftl/FTLLowerDFGToB3.cpp:
 51 (JSC::FTL::DFG::LowerDFGToB3::compileNode):
 52 (JSC::FTL::DFG::LowerDFGToB3::compileIsEmpty):
 53 * jit/JIT.cpp:
 54 (JSC::JIT::privateCompileMainPass):
 55 * jit/JIT.h:
 56 * jit/JITOpcodes.cpp:
 57 (JSC::JIT::emit_op_is_empty):
 58 * jit/JITOpcodes32_64.cpp:
 59 (JSC::JIT::emit_op_is_empty):
 60 * llint/LowLevelInterpreter32_64.asm:
 61 * llint/LowLevelInterpreter64.asm:
 62 * tests/stress/class-syntax-double-constructor.js: Added.
 63
 64
1652016-03-31 Skachkov Oleksandr <gskachkov@gmail.com>
266
367 [ES6] Class syntax. Access to new.target inside of the eval should not lead to SyntaxError

Source/JavaScriptCore/bytecode/BytecodeList.json

4949 { "name" : "op_instanceof", "length" : 4 },
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 },
5455 { "name" : "op_is_number", "length" : 3 },

Source/JavaScriptCore/bytecode/BytecodeUseDef.h

@@void computeUsesForBytecodeOffset(
146146 case op_get_by_id:
147147 case op_get_array_length:
148148 case op_typeof:
 149 case op_is_empty:
149150 case op_is_undefined:
150151 case op_is_boolean:
151152 case op_is_number:

@@void computeDefsForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* bloc
374375 case op_instanceof_custom:
375376 case op_get_by_val:
376377 case op_typeof:
 378 case op_is_empty:
377379 case op_is_undefined:
378380 case op_is_boolean:
379381 case op_is_number:

Source/JavaScriptCore/bytecode/CodeBlock.cpp

@@void CodeBlock::dumpBytecode(
10631063 printUnaryOp(out, exec, location, it, "typeof");
10641064 break;
10651065 }
 1066 case op_is_empty: {
 1067 printUnaryOp(out, exec, location, it, "is_empty");
 1068 break;
 1069 }
10661070 case op_is_undefined: {
10671071 printUnaryOp(out, exec, location, it, "is_undefined");
10681072 break;

Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

@@RegisterID* BytecodeGenerator::emitIsUndefined(RegisterID* dst, RegisterID* src)
40854085 return dst;
40864086}
40874087
 4088RegisterID* BytecodeGenerator::emitIsEmpty(RegisterID* dst, RegisterID* src)
 4089{
 4090 emitOpcode(op_is_empty);
 4091 instructions().append(dst->index());
 4092 instructions().append(src->index());
 4093 return dst;
 4094}
 4095
40884096RegisterID* BytecodeGenerator::emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node)
40894097{
40904098 {

Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

@@namespace JSC {
617617
618618 RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
619619 RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
 620 RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src);
620621 void emitRequireObjectCoercible(RegisterID* value, const String& error);
621622
622623 RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node);

Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

@@RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re
758758 ASSERT(generator.constructorKind() == ConstructorKind::Derived || generator.derivedContextType() == DerivedContextType::DerivedConstructorContext);
759759 generator.emitMove(callArguments.thisRegister(), generator.newTarget());
760760 RegisterID* ret = generator.emitConstruct(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
 761
 762 bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Derived;
 763 bool doPutAndLoadThisFromArrowFunctionLexicalEnvironment = generator.isDerivedConstructorContext() || (isConstructorKindDerived && generator.needsToUpdateArrowFunctionContext());
 764
 765 if (doPutAndLoadThisFromArrowFunctionLexicalEnvironment)
 766 generator.emitLoadThisFromArrowFunctionLexicalEnvironment();
 767
 768 RefPtr<Label> thisIsEmptyLabel = generator.newLabel();
 769 generator.emitJumpIfTrue(generator.emitIsEmpty(generator.newTemporary(), generator.thisRegister()), thisIsEmptyLabel.get());
 770 generator.emitThrowReferenceError(ASCIILiteral("'super()' can't be called more than once in constructor."));
 771 generator.emitLabel(thisIsEmptyLabel.get());
 772
761773 generator.emitMove(generator.thisRegister(), ret);
762774
763  bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Derived;
764  if (generator.isDerivedConstructorContext() || (isConstructorKindDerived && generator.needsToUpdateArrowFunctionContext()))
 775 if (doPutAndLoadThisFromArrowFunctionLexicalEnvironment)
765776 generator.emitPutThisToArrowFunctionContextScope();
766777
767778 return ret;

Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h

@@bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
955955 }
956956 break;
957957 }
958 
 958
 959 case IsEmpty:
959960 case IsUndefined:
960961 case IsBoolean:
961962 case IsNumber:

@@bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
10151016 } else
10161017 setConstant(node, jsBoolean(false));
10171018 break;
 1019 case IsEmpty:
 1020 setConstant(node, jsBoolean(child.value().isEmpty()));
 1021 break;
10181022 default:
10191023 constantWasSet = false;
10201024 break;

Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

@@bool ByteCodeParser::parseBlock(unsigned limit)
36103610 set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(InstanceOfCustom, value, constructor, hasInstanceValue));
36113611 NEXT_OPCODE(op_instanceof_custom);
36123612 }
3613 
 3613 case op_is_empty: {
 3614 Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
 3615 set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsEmpty, value));
 3616 NEXT_OPCODE(op_is_empty);
 3617 }
36143618 case op_is_undefined: {
36153619 Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
36163620 set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsUndefined, value));

Source/JavaScriptCore/dfg/DFGCapabilities.cpp

@@CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
131131 case op_overrides_has_instance:
132132 case op_instanceof:
133133 case op_instanceof_custom:
 134 case op_is_empty:
134135 case op_is_undefined:
135136 case op_is_boolean:
136137 case op_is_number:

Source/JavaScriptCore/dfg/DFGClobberize.h

@@void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
142142 case GetGlobalObject:
143143 case StringCharCodeAt:
144144 case CompareStrictEq:
 145 case IsEmpty:
145146 case IsUndefined:
146147 case IsBoolean:
147148 case IsNumber:

Source/JavaScriptCore/dfg/DFGDoesGC.cpp

@@bool doesGC(Graph& graph, Node* node)
151151 case OverridesHasInstance:
152152 case InstanceOf:
153153 case InstanceOfCustom:
 154 case IsEmpty:
154155 case IsUndefined:
155156 case IsBoolean:
156157 case IsNumber:

Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

@@private:
14901490 case NewRegexp:
14911491 case ProfileWillCall:
14921492 case ProfileDidCall:
 1493 case IsEmpty:
14931494 case IsUndefined:
14941495 case IsBoolean:
14951496 case IsNumber:

Source/JavaScriptCore/dfg/DFGNodeType.h

@@namespace JSC { namespace DFG {
296296 macro(OverridesHasInstance, NodeMustGenerate | NodeResultBoolean) \
297297 macro(InstanceOf, NodeResultBoolean) \
298298 macro(InstanceOfCustom, NodeMustGenerate | NodeResultBoolean) \
 299 macro(IsEmpty, NodeResultBoolean) \
299300 macro(IsUndefined, NodeResultBoolean) \
300301 macro(IsBoolean, NodeResultBoolean) \
301302 macro(IsNumber, NodeResultBoolean) \

Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp

@@private:
418418 case OverridesHasInstance:
419419 case InstanceOf:
420420 case InstanceOfCustom:
 421 case IsEmpty:
421422 case IsUndefined:
422423 case IsBoolean:
423424 case IsNumber:

Source/JavaScriptCore/dfg/DFGSafeToExecute.h

@@bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
251251 case OverridesHasInstance:
252252 case InstanceOf:
253253 case InstanceOfCustom:
 254 case IsEmpty:
254255 case IsUndefined:
255256 case IsBoolean:
256257 case IsNumber:

Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp

@@void SpeculativeJIT::compile(Node* node)
43864386 break;
43874387 }
43884388
 4389 case IsEmpty: {
 4390 JSValueOperand value(this, node->child1());
 4391 GPRTemporary result(this, Reuse, value);
 4392
 4393 JITCompiler::Jump isEmpty = m_jit.branch32(JITCompiler::Equal, value.tagGPR(), TrustedImm32(JSValue::EmptyValueTag));
 4394 m_jit.or32(TrustedImm32(ValueFalse), result.gpr());
 4395 JITCompiler::Jump done = m_jit.jump();
 4396
 4397 isEmpty.link(&m_jit);
 4398
 4399 m_jit.or32(TrustedImm32(ValueTrue), result.gpr());
 4400 done.link(&m_jit);
 4401 jsValueResult(result.gpr(), node, DataFormatJSBoolean);
 4402 break;
 4403 }
 4404
43894405 case IsUndefined: {
43904406 JSValueOperand value(this, node->child1());
43914407 GPRTemporary result(this);

Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

@@void SpeculativeJIT::compile(Node* node)
43954395 compileInstanceOfCustom(node);
43964396 break;
43974397 }
 4398
 4399 case IsEmpty: {
 4400 JSValueOperand value(this, node->child1());
 4401 GPRTemporary result(this, Reuse, value);
 4402
 4403 JITCompiler::Jump isEmpty = m_jit.branchTest64(JITCompiler::Zero, value.gpr());
 4404 m_jit.or32(TrustedImm32(ValueFalse), result.gpr());
 4405 JITCompiler::Jump done = m_jit.jump();
 4406
 4407 isEmpty.link(&m_jit);
 4408
 4409 m_jit.or32(TrustedImm32(ValueTrue), result.gpr());
 4410 done.link(&m_jit);
 4411 jsValueResult(result.gpr(), node, DataFormatJSBoolean);
 4412 break;
 4413 }
43984414
43994415 case IsUndefined: {
44004416 JSValueOperand value(this, node->child1());

Source/JavaScriptCore/ftl/FTLCapabilities.cpp

@@inline CapabilityLevel canCompile(Node* node)
175175 case Throw:
176176 case ThrowReferenceError:
177177 case Unreachable:
 178 case IsEmpty:
178179 case IsUndefined:
179180 case IsBoolean:
180181 case IsNumber:

Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

@@private:
834834 case InvalidationPoint:
835835 compileInvalidationPoint();
836836 break;
 837 case IsEmpty:
 838 compileIsEmpty();
 839 break;
837840 case IsUndefined:
838841 compileIsUndefined();
839842 break;

@@private:
56565659 // When this abruptly terminates, it could read any heap location.
56575660 patchpoint->effects.reads = HeapRange::top();
56585661 }
 5662
 5663 void compileIsEmpty()
 5664 {
 5665 setBoolean(m_out.isZero64(lowJSValue(m_node->child1())));
 5666 }
56595667
56605668 void compileIsUndefined()
56615669 {

Source/JavaScriptCore/jit/JIT.cpp

@@void JIT::privateCompileMainPass()
234234 DEFINE_OP(op_overrides_has_instance)
235235 DEFINE_OP(op_instanceof)
236236 DEFINE_OP(op_instanceof_custom)
 237 DEFINE_OP(op_is_empty)
237238 DEFINE_OP(op_is_undefined)
238239 DEFINE_OP(op_is_boolean)
239240 DEFINE_OP(op_is_number)

Source/JavaScriptCore/jit/JIT.h

@@namespace JSC {
510510 void emit_op_overrides_has_instance(Instruction*);
511511 void emit_op_instanceof(Instruction*);
512512 void emit_op_instanceof_custom(Instruction*);
 513 void emit_op_is_empty(Instruction*);
513514 void emit_op_is_undefined(Instruction*);
514515 void emit_op_is_boolean(Instruction*);
515516 void emit_op_is_number(Instruction*);

Source/JavaScriptCore/jit/JITOpcodes.cpp

@@void JIT::emit_op_instanceof_custom(Instruction*)
175175 // This always goes to slow path since we expect it to be rare.
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 Jump isEmpty = branchTest64(Zero, regT0);
 186 move(TrustedImm32(ValueFalse), regT0);
 187
 188 Jump done = jump();
 189 isEmpty.link(this);
 190
 191 move(TrustedImm32(ValueTrue), regT0);
 192 done.link(this);
 193 emitTagBool(regT0);
 194 emitPutVirtualRegister(dst);
 195}
178196
179197void JIT::emit_op_is_undefined(Instruction* currentInstruction)
180198{

Source/JavaScriptCore/jit/JITOpcodes32_64.cpp

@@void JIT::emitSlow_op_instanceof_custom(Instruction* currentInstruction, Vector<
290290 callOperation(operationInstanceOfCustom, regT1, regT0, regT2, regT4, regT3);
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 Jump isEmpty = Branch32(Equal, regT0, TrustedImm32(JSValue::EmptyValueTag));
 301 move(TrustedImm32(0), regT0);
 302
 303 Jump done = jump();
 304 isEmpty.link(this);
 305
 306 move(TrustedImm32(1), regT0);
 307 done.link(this);
 308 emitStoreBool(dst, regT0);
 309}
293310
294311void JIT::emit_op_is_undefined(Instruction* currentInstruction)
295312{

Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm

@@_llint_op_instanceof_custom:
12111211 callSlowPath(_llint_slow_path_instanceof_custom)
12121212 dispatch(5)
12131213
 1214_llint_op_is_empty:
 1215 traceExecution()
 1216 loadisFromInstruction(1, t0)
 1217 loadConstantOrVariableTag(t0, t1)
 1218 bineq t1, EmptyValueTag, .opIsEmpty
 1219 storei t1, PayloadOffset[cfr, t0, 8]
 1220 dispatch(3)
 1221.opIsEmpty:
 1222 storei 0, PayloadOffset[cfr, t0, 8]
 1223 dispatch(3)
 1224
12141225
12151226_llint_op_is_undefined:
12161227 traceExecution()

Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

@@_llint_op_instanceof_custom:
10981098 callSlowPath(_llint_slow_path_instanceof_custom)
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 bqneq t0, ValueEmpty, .opIsNotEmpty
 1108 storeq ValueTrue, [cfr, t2, 8]
 1109 dispatch(3)
 1110.opIsNotEmpty:
 1111 storeq ValueFalse, [cfr, t2, 8]
 1112 dispatch(3)
 1113
 1114
11011115_llint_op_is_undefined:
11021116 traceExecution()
11031117 loadisFromInstruction(2, t1)

Source/JavaScriptCore/tests/stress/class-syntax-double-constructor.js

 1function tryCatch(klass) {
 2 let result = false;
 3 try {
 4 new klass();
 5 } catch(e) {
 6 result = e instanceof ReferenceError;
 7 }
 8 return result;
 9}
 10
 11var testCase = function (actual, expected, message) {
 12 if (actual !== expected) {
 13 throw message + ". Expected '" + expected + "', but was '" + actual + "'";
 14 }
 15};
 16
 17let count = 0;
 18class A {
 19 constructor() {
 20 this.id = 0;
 21 count++;
 22 }
 23}
 24
 25class B extends A {
 26 constructor() {
 27 super();
 28 super();
 29 super();
 30 }
 31}
 32
 33testCase(tryCatch(B), true, '');
 34testCase(count, 2, '');
 35
 36count = 0;
 37class C extends A {
 38 constructor() {
 39 (()=>super())();
 40 (()=>super())();
 41 (()=>super())();
 42 }
 43}
 44
 45testCase(tryCatch(C), true, '');
 46testCase(count, 2, '');
 47
 48count = 0;
 49class D extends A {
 50 constructor() {
 51 eval('super()');
 52 eval('super()');
 53 eval('super()');
 54 }
 55}
 56
 57testCase(tryCatch(D), true, '');
 58testCase(count, 2, '');
 59
 60count = 0;
 61class E extends A {
 62 constructor() {
 63 (()=>eval('super()'))();
 64 (()=>eval('super()'))();
 65 (()=>eval('super()'))();
 66 }
 67}
 68
 69testCase(tryCatch(E), true, '');
 70testCase(count, 2, '');
 71
 72noInline(B);
 73for (var i = 0; i < 100000; i++) {
 74 count = 0;
 75 let result = false;
 76 try {
 77 new B();
 78 } catch(e) {
 79 result = e instanceof ReferenceError;
 80 }
 81
 82 testCase(result, true, '');
 83 testCase(count, 2, '');
 84}