diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index fb568b1403b6..8d7474104241 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -2010,6 +2010,46 @@ CodeGenerator::visitInitProp(LInitProp *lir) return callVM(InitPropInfo, lir); } +typedef bool (*CreateThisFn)(JSContext *cx, HandleObject callee, MutableHandleValue rval); +static const VMFunction CreateThisInfo = +FunctionInfo(CreateThis); + +bool +CodeGenerator::visitCreateThis(LCreateThis *lir) +{ + const LAllocation *callee = lir->getCallee(); + + if (callee->isConstant()) + pushArg(ImmGCPtr(&callee->toConstant()->toObject())); + else + pushArg(ToRegister(callee)); + + return callVM(CreateThisInfo, lir); +} + +typedef JSObject *(*CreateThisWithProtoFn)(JSContext *cx, HandleObject callee, JSObject *proto); +static const VMFunction CreateThisWithProtoInfo = +FunctionInfo(js_CreateThisForFunctionWithProto); + +bool +CodeGenerator::visitCreateThisWithProto(LCreateThisWithProto *lir) +{ + const LAllocation *callee = lir->getCallee(); + const LAllocation *proto = lir->getPrototype(); + + if (proto->isConstant()) + pushArg(ImmGCPtr(&proto->toConstant()->toObject())); + else + pushArg(ToRegister(proto)); + + if (callee->isConstant()) + pushArg(ImmGCPtr(&callee->toConstant()->toObject())); + else + pushArg(ToRegister(callee)); + + return callVM(CreateThisWithProtoInfo, lir); +} + typedef JSObject *(*NewGCThingFn)(JSContext *cx, gc::AllocKind allocKind, size_t thingSize); static const VMFunction NewGCThingInfo = FunctionInfo(js::ion::NewGCThing); @@ -2038,59 +2078,6 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir) return true; } -typedef JSObject *(*CreateThisFn)(JSContext *cx, HandleObject callee, JSObject *proto); -static const VMFunction CreateThisInfo = - FunctionInfo(js_CreateThisForFunctionWithProto); - -bool -CodeGenerator::emitCreateThisVM(LInstruction *lir, - const LAllocation *proto, - const LAllocation *callee) -{ - if (proto->isConstant()) - pushArg(ImmGCPtr(&proto->toConstant()->toObject())); - else - pushArg(ToRegister(proto)); - - if (callee->isConstant()) - pushArg(ImmGCPtr(&callee->toConstant()->toObject())); - else - pushArg(ToRegister(callee)); - - return callVM(CreateThisInfo, lir); -} - -bool -CodeGenerator::visitCreateThisV(LCreateThisV *lir) -{ - Label done, vm; - - const LAllocation *proto = lir->getPrototype(); - const LAllocation *callee = lir->getCallee(); - - // When callee could be a native, put MagicValue in return operand. - // Use the VMCall when callee turns out to not be a native. - masm.branchIfInterpreted(ToRegister(callee), &vm); - masm.moveValue(MagicValue(JS_IS_CONSTRUCTING), GetValueOutput(lir)); - masm.jump(&done); - - masm.bind(&vm); - if (!emitCreateThisVM(lir, proto, callee)) - return false; - - masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, GetValueOutput(lir)); - - masm.bind(&done); - - return true; -} - -bool -CodeGenerator::visitCreateThisO(LCreateThisO *lir) -{ - return emitCreateThisVM(lir, lir->getPrototype(), lir->getCallee()); -} - bool CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir) { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index 096f3c1a738d..96918b7f78f0 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -99,9 +99,8 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitNewCallObject(LNewCallObject *lir); bool visitNewStringObject(LNewStringObject *lir); bool visitInitProp(LInitProp *lir); - bool emitCreateThisVM(LInstruction *lir, const LAllocation *proto, const LAllocation *callee); - bool visitCreateThisV(LCreateThisV *lir); - bool visitCreateThisO(LCreateThisO *lir); + bool visitCreateThis(LCreateThis *lir); + bool visitCreateThisWithProto(LCreateThisWithProto *lir); bool visitCreateThisWithTemplate(LCreateThisWithTemplate *lir); bool visitReturnFromCtor(LReturnFromCtor *lir); bool visitArrayLength(LArrayLength *lir); diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index c9957be7d651..69de552e16fd 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -3574,15 +3574,6 @@ IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope) return callObj; } -MDefinition * -IonBuilder::createThisNative() -{ - // Native constructors build the new Object themselves. - MConstant *magic = MConstant::New(MagicValue(JS_IS_CONSTRUCTING)); - current->add(magic); - return magic; -} - MDefinition * IonBuilder::createThisScripted(MDefinition *callee) { @@ -3610,7 +3601,7 @@ IonBuilder::createThisScripted(MDefinition *callee) current->add(getProto); // Create this from prototype - MCreateThis *createThis = MCreateThis::New(callee, getProto); + MCreateThisWithProto *createThis = MCreateThisWithProto::New(callee, getProto); current->add(createThis); return createThis; @@ -3633,8 +3624,13 @@ IonBuilder::getSingletonPrototype(JSFunction *target) } MDefinition * -IonBuilder::createThisScriptedSingleton(HandleFunction target, HandleObject proto, MDefinition *callee) +IonBuilder::createThisScriptedSingleton(HandleFunction target, MDefinition *callee) { + // Get the singleton prototype (if exists) + RootedObject proto(cx, getSingletonPrototype(target)); + if (!proto) + return NULL; + // Generate an inline path to create a new |this| object with // the given singleton prototype. types::TypeObject *type = proto->getNewType(cx, target); @@ -3661,36 +3657,26 @@ MDefinition * IonBuilder::createThis(HandleFunction target, MDefinition *callee) { // Create this for unknown target - if (!target) - return createThisScripted(callee); - - // Create this for native function - if (target->isNative()) { - if (!target->isNativeConstructor()) - return NULL; - return createThisNative(); + if (!target) { + MCreateThis *createThis = MCreateThis::New(callee); + current->add(createThis); + return createThis; } - // Create this with known prototype. - RootedObject proto(cx, getSingletonPrototype(target)); + // Native constructors build the new Object themselves. + if (target->isNative()) { + JS_ASSERT (target->isNativeConstructor()); + MConstant *magic = MConstant::New(MagicValue(JS_IS_CONSTRUCTING)); + current->add(magic); + return magic; + } // Try baking in the prototype. - if (proto) { - MDefinition *createThis = createThisScriptedSingleton(target, proto, callee); - if (createThis) - return createThis; - } + MDefinition *createThis = createThisScriptedSingleton(target, callee); + if (createThis) + return createThis; - MDefinition *createThis = createThisScripted(callee); - if (!createThis) - return NULL; - - // The native function case is already handled upfront. - // Here we can safely remove the native check for MCreateThis. - JS_ASSERT(createThis->isCreateThis()); - createThis->toCreateThis()->removeNativeCheck(); - - return createThis; + return createThisScripted(callee); } bool diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index c8a59d83bfc4..0a490503aa96 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -275,9 +275,8 @@ class IonBuilder : public MIRGenerator JSObject *getSingletonPrototype(JSFunction *target); - MDefinition *createThisNative(); MDefinition *createThisScripted(MDefinition *callee); - MDefinition *createThisScriptedSingleton(HandleFunction target, HandleObject proto, MDefinition *callee); + MDefinition *createThisScriptedSingleton(HandleFunction target, MDefinition *callee); MDefinition *createThis(HandleFunction target, MDefinition *callee); MInstruction *createDeclEnvObject(MDefinition *callee, MDefinition *scopeObj); MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj); diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index 07f90291251c..b51f1c264911 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -432,6 +432,52 @@ class LToIdV : public LCallInstructionHelper } }; +// Allocate an object for |new| on the caller-side, +// when there is no templateObject or prototype known +class LCreateThis : public LCallInstructionHelper +{ + public: + LIR_HEADER(CreateThis) + + LCreateThis(const LAllocation &callee) + { + setOperand(0, callee); + } + + const LAllocation *getCallee() { + return getOperand(0); + } + + MCreateThis *mir() const { + return mir_->toCreateThis(); + } +}; + +// Allocate an object for |new| on the caller-side, +// when the prototype is known. +class LCreateThisWithProto : public LCallInstructionHelper<1, 2, 0> +{ + public: + LIR_HEADER(CreateThisWithProto) + + LCreateThisWithProto(const LAllocation &callee, const LAllocation &prototype) + { + setOperand(0, callee); + setOperand(1, prototype); + } + + const LAllocation *getCallee() { + return getOperand(0); + } + const LAllocation *getPrototype() { + return getOperand(1); + } + + MCreateThis *mir() const { + return mir_->toCreateThis(); + } +}; + // Allocate an object for |new| on the caller-side. // Always performs object initialization with a fast path. class LCreateThisWithTemplate : public LInstructionHelper<1, 0, 0> @@ -447,54 +493,6 @@ class LCreateThisWithTemplate : public LInstructionHelper<1, 0, 0> } }; -// Allocate an object for |new| on the caller-side, when there is no templateObject. -class LCreateThisV : public LCallInstructionHelper -{ - public: - LIR_HEADER(CreateThisV) - - LCreateThisV(const LAllocation &callee, const LAllocation &prototype) - { - setOperand(0, callee); - setOperand(1, prototype); - } - - const LAllocation *getCallee() { - return getOperand(0); - } - const LAllocation *getPrototype() { - return getOperand(1); - } - - MCreateThis *mir() const { - return mir_->toCreateThis(); - } -}; - -// Allocate an object for |new| on the caller-side, when there is no templateObject. -class LCreateThisO : public LCallInstructionHelper<1, 2, 0> -{ - public: - LIR_HEADER(CreateThisO) - - LCreateThisO(const LAllocation &callee, const LAllocation &prototype) - { - setOperand(0, callee); - setOperand(1, prototype); - } - - const LAllocation *getCallee() { - return getOperand(0); - } - const LAllocation *getPrototype() { - return getOperand(1); - } - - MCreateThis *mir() const { - return mir_->toCreateThis(); - } -}; - // If the Value is an Object, return unbox(Value). // Otherwise, return the other Object. class LReturnFromCtor : public LInstructionHelper<1, BOX_PIECES + 1, 0> diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index 8df6a3ec9bd7..a1dbecb5e3cf 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -39,8 +39,8 @@ _(ApplyArgsGeneric) \ _(StackArgT) \ _(StackArgV) \ - _(CreateThisV) \ - _(CreateThisO) \ + _(CreateThis) \ + _(CreateThisWithProto) \ _(CreateThisWithTemplate) \ _(ReturnFromCtor) \ _(BitNotI) \ diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index b791dbfcf4f6..47b79ec14d67 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -241,18 +241,19 @@ LIRGenerator::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins) return define(lir, ins) && assignSafepoint(lir, ins); } +bool +LIRGenerator::visitCreateThisWithProto(MCreateThisWithProto *ins) +{ + LCreateThisWithProto *lir = + new LCreateThisWithProto(useRegisterOrConstantAtStart(ins->getCallee()), + useRegisterOrConstantAtStart(ins->getPrototype())); + return defineReturn(lir, ins) && assignSafepoint(lir, ins); +} + bool LIRGenerator::visitCreateThis(MCreateThis *ins) { - if (ins->needNativeCheck()) { - JS_ASSERT(ins->type() == MIRType_Value); - LCreateThisV *lir = new LCreateThisV(useRegisterAtStart(ins->getCallee()), - useRegisterOrConstantAtStart(ins->getPrototype())); - return defineReturn(lir, ins) && assignSafepoint(lir, ins); - } - - LCreateThisO *lir = new LCreateThisO(useRegisterOrConstantAtStart(ins->getCallee()), - useRegisterOrConstantAtStart(ins->getPrototype())); + LCreateThis *lir = new LCreateThis(useRegisterOrConstantAtStart(ins->getCallee())); return defineReturn(lir, ins) && assignSafepoint(lir, ins); } diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index bfbde745ee31..372f016a776b 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -91,6 +91,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitPrepareCall(MPrepareCall *ins); bool visitPassArg(MPassArg *arg); bool visitCreateThisWithTemplate(MCreateThisWithTemplate *ins); + bool visitCreateThisWithProto(MCreateThisWithProto *ins); bool visitCreateThis(MCreateThis *ins); bool visitReturnFromCtor(MReturnFromCtor *ins); bool visitCall(MCall *call); diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 2c20d0a1ad48..4b7f1d742a2c 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -1688,29 +1688,21 @@ class MCreateThisWithTemplate // Caller-side allocation of |this| for |new|: // Given a prototype operand, construct |this| for JSOP_NEW. -// For native constructors, returns MagicValue(JS_IS_CONSTRUCTING). -class MCreateThis - : public MAryInstruction<2>, +class MCreateThisWithProto + : public MBinaryInstruction, public MixPolicy, ObjectPolicy<1> > { - bool needNativeCheck_; - - MCreateThis(MDefinition *callee, MDefinition *prototype) - : needNativeCheck_(true) + MCreateThisWithProto(MDefinition *callee, MDefinition *prototype) + : MBinaryInstruction(callee, prototype) { - initOperand(0, callee); - initOperand(1, prototype); - - // Type is mostly object, except for native constructors - // therefore the need of Value type. - setResultType(MIRType_Value); + setResultType(MIRType_Object); } public: - INSTRUCTION_HEADER(CreateThis) - static MCreateThis *New(MDefinition *callee, MDefinition *prototype) + INSTRUCTION_HEADER(CreateThisWithProto) + static MCreateThisWithProto *New(MDefinition *callee, MDefinition *prototype) { - return new MCreateThis(callee, prototype); + return new MCreateThisWithProto(callee, prototype); } MDefinition *getCallee() const { @@ -1719,12 +1711,37 @@ class MCreateThis MDefinition *getPrototype() const { return getOperand(1); } - void removeNativeCheck() { - needNativeCheck_ = false; - setResultType(MIRType_Object); + + // Although creation of |this| modifies global state, it is safely repeatable. + AliasSet getAliasSet() const { + return AliasSet::None(); } - bool needNativeCheck() const { - return needNativeCheck_; + TypePolicy *typePolicy() { + return this; + } +}; + +// Caller-side allocation of |this| for |new|: +// Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING). +class MCreateThis + : public MUnaryInstruction, + public ObjectPolicy<0> +{ + MCreateThis(MDefinition *callee) + : MUnaryInstruction(callee) + { + setResultType(MIRType_Value); + } + + public: + INSTRUCTION_HEADER(CreateThis) + static MCreateThis *New(MDefinition *callee) + { + return new MCreateThis(callee); + } + + MDefinition *getCallee() const { + return getOperand(0); } // Although creation of |this| modifies global state, it is safely repeatable. diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index bddade367e82..c8e4724f0a44 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -31,6 +31,7 @@ namespace ion { _(DefVar) \ _(DefFun) \ _(CreateThis) \ + _(CreateThisWithProto) \ _(CreateThisWithTemplate) \ _(PrepareCall) \ _(PassArg) \ diff --git a/js/src/ion/VMFunctions.cpp b/js/src/ion/VMFunctions.cpp index 504da74f3849..745f6e3f4eae 100644 --- a/js/src/ion/VMFunctions.cpp +++ b/js/src/ion/VMFunctions.cpp @@ -437,18 +437,21 @@ NewStringObject(JSContext *cx, HandleString str) return StringObject::create(cx, str); } -bool SPSEnter(JSContext *cx, HandleScript script) +bool +SPSEnter(JSContext *cx, HandleScript script) { return cx->runtime->spsProfiler.enter(cx, script, script->function()); } -bool SPSExit(JSContext *cx, HandleScript script) +bool +SPSExit(JSContext *cx, HandleScript script) { cx->runtime->spsProfiler.exit(cx, script, script->function()); return true; } -bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, JSBool *out) +bool +OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, JSBool *out) { RootedValue dummy(cx); // Disregards atomization changes: no way to propagate. RootedId id(cx); @@ -464,10 +467,25 @@ bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, JSBool *out) return true; } -bool GetIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue rval) +bool +GetIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue rval) { return cx->global()->getIntrinsicValue(cx, name, rval); } +bool +CreateThis(JSContext *cx, HandleObject callee, MutableHandleValue rval) +{ + rval.set(MagicValue(JS_IS_CONSTRUCTING)); + + if (callee->isFunction()) { + JSFunction *fun = callee->toFunction(); + if (fun->isInterpreted()) + rval.set(ObjectValue(*js_CreateThisForFunction(cx, callee, false))); + } + + return true; +} + } // namespace ion } // namespace js diff --git a/js/src/ion/VMFunctions.h b/js/src/ion/VMFunctions.h index 7e3f833dd1e4..e09e23d22611 100644 --- a/js/src/ion/VMFunctions.h +++ b/js/src/ion/VMFunctions.h @@ -464,6 +464,7 @@ bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, JSBool *out); bool GetIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue rval); +bool CreateThis(JSContext *cx, HandleObject callee, MutableHandleValue rval); } // namespace ion } // namespace js diff --git a/js/src/jit-test/tests/ion/bug825705.js b/js/src/jit-test/tests/ion/bug825705.js new file mode 100644 index 000000000000..05689f6edec0 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug825705.js @@ -0,0 +1,23 @@ +// Test 1: When constructing x, we shouldn't take the prototype for this. +// it will crash if that happens +evalcx("\ + var x = newGlobal().Object;\ + function f() { return new x; }\ + f();\ + f();\ +", newGlobal()); + +// Test 2: Don't take the prototype of proxy's to create |this|, +// as this will throw... Not expected behaviour. +var O = new Proxy(function() {}, { + get: function() { + throw "get trap"; + } +}); + +function f() { + new O(); +} + +f(); +f();