Bug 825705: Creating this on caller-side shouldn't query prototype for unknown objects, r=jandem

This commit is contained in:
Hannes Verschore 2013-01-04 17:11:32 +01:00
Родитель aa4c0c0f58
Коммит c95d254368
13 изменённых файлов: 209 добавлений и 178 удалений

Просмотреть файл

@ -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<CreateThisFn>(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<CreateThisWithProtoFn>(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<NewGCThingFn>(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<CreateThisFn>(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)
{

Просмотреть файл

@ -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);

Просмотреть файл

@ -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

Просмотреть файл

@ -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);

Просмотреть файл

@ -432,6 +432,52 @@ class LToIdV : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0>
}
};
// Allocate an object for |new| on the caller-side,
// when there is no templateObject or prototype known
class LCreateThis : public LCallInstructionHelper<BOX_PIECES, 1, 0>
{
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<BOX_PIECES, 2, 0>
{
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>

Просмотреть файл

@ -39,8 +39,8 @@
_(ApplyArgsGeneric) \
_(StackArgT) \
_(StackArgV) \
_(CreateThisV) \
_(CreateThisO) \
_(CreateThis) \
_(CreateThisWithProto) \
_(CreateThisWithTemplate) \
_(ReturnFromCtor) \
_(BitNotI) \

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<0>, 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.

Просмотреть файл

@ -31,6 +31,7 @@ namespace ion {
_(DefVar) \
_(DefFun) \
_(CreateThis) \
_(CreateThisWithProto) \
_(CreateThisWithTemplate) \
_(PrepareCall) \
_(PassArg) \

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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();