Bug 864218 - Improve performance when accessing variables defined in run-once closures, r=luke,jandem.

This commit is contained in:
Brian Hackett 2013-05-23 05:59:53 -06:00
parent 1bfc965bdc
commit c73f548b39
34 changed files with 513 additions and 136 deletions

View File

@ -2478,6 +2478,22 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
bce->switchToMain();
}
/*
* Emit a prologue for run-once scripts which will deoptimize JIT code if
* the script ends up running multiple times via foo.caller related
* shenanigans.
*/
bool runOnce = bce->parent &&
bce->parent->emittingRunOnceLambda &&
!funbox->argumentsHasLocalBinding() &&
!funbox->isGenerator();
if (runOnce) {
bce->switchToProlog();
if (!Emit1(cx, bce, JSOP_RUNONCE) < 0)
return false;
bce->switchToMain();
}
if (!EmitTree(cx, bce, body))
return false;
@ -2495,8 +2511,10 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
* If this function is only expected to run once, mark the script so that
* initializers created within it may be given more precise types.
*/
if (bce->parent && bce->parent->emittingRunOnceLambda)
if (runOnce) {
bce->script->treatAsRunOnce = true;
JS_ASSERT(!bce->script->hasRunOnce);
}
/*
* Mark as singletons any function which will only be executed once, or

View File

@ -1666,14 +1666,22 @@ BaselineCompiler::emit_JSOP_DELPROP()
return true;
}
Address
BaselineCompiler::getScopeCoordinateAddress(Register reg)
void
BaselineCompiler::getScopeCoordinateObject(Register reg)
{
ScopeCoordinate sc(pc);
masm.loadPtr(frame.addressOfScopeChain(), reg);
for (unsigned i = sc.hops; i; i--)
masm.extractObject(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg);
}
Address
BaselineCompiler::getScopeCoordinateAddress(Register reg)
{
getScopeCoordinateObject(reg);
ScopeCoordinate sc(pc);
Shape *shape = ScopeCoordinateToStaticScopeShape(cx, script, pc);
Address addr;
@ -1710,6 +1718,29 @@ BaselineCompiler::emit_JSOP_CALLALIASEDVAR()
bool
BaselineCompiler::emit_JSOP_SETALIASEDVAR()
{
JSScript *outerScript = ScopeCoordinateFunctionScript(cx, script, pc);
if (outerScript && outerScript->treatAsRunOnce) {
// Type updates for this operation might need to be tracked, so treat
// this as a SETPROP.
// Load rhs into R1.
frame.syncStack(1);
frame.popValue(R1);
// Load and box lhs into R0.
getScopeCoordinateObject(R2.scratchReg());
masm.tagValue(JSVAL_TYPE_OBJECT, R2.scratchReg(), R0);
// Call SETPROP IC.
ICSetProp_Fallback::Compiler compiler(cx);
if (!emitOpIC(compiler.getStub(&stubSpace_)))
return false;
// The IC will return the RHS value in R0, mark it as pushed value.
frame.push(R0);
return true;
}
// Sync everything except the top value, so that we can use R0 as scratch
// (storeValue does not touch it if the top value is in R0).
frame.syncStack(1);
@ -2435,6 +2466,23 @@ BaselineCompiler::emit_JSOP_ARGUMENTS()
return true;
}
typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript);
static const VMFunction RunOnceScriptPrologueInfo =
FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue);
bool
BaselineCompiler::emit_JSOP_RUNONCE()
{
frame.syncStack(0);
prepareVMCall();
masm.movePtr(ImmGCPtr(script), R0.scratchReg());
pushArg(R0.scratchReg());
return callVM(RunOnceScriptPrologueInfo);
}
bool
BaselineCompiler::emit_JSOP_REST()
{

View File

@ -152,6 +152,7 @@ namespace ion {
_(JSOP_EXCEPTION) \
_(JSOP_DEBUGGER) \
_(JSOP_ARGUMENTS) \
_(JSOP_RUNONCE) \
_(JSOP_REST) \
_(JSOP_TOID) \
_(JSOP_TABLESWITCH) \
@ -236,6 +237,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
bool addPCMappingEntry(bool addIndexEntry);
void getScopeCoordinateObject(Register reg);
Address getScopeCoordinateAddress(Register reg);
};

View File

@ -6191,9 +6191,17 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub,
JSOp op = JSOp(*pc);
FallbackICSpew(cx, stub, "SetProp(%s)", js_CodeName[op]);
JS_ASSERT(op == JSOP_SETPROP || op == JSOP_SETNAME || op == JSOP_SETGNAME || op == JSOP_INITPROP);
JS_ASSERT(op == JSOP_SETPROP ||
op == JSOP_SETNAME ||
op == JSOP_SETGNAME ||
op == JSOP_INITPROP ||
op == JSOP_SETALIASEDVAR);
RootedPropertyName name(cx, script->getName(pc));
RootedPropertyName name(cx);
if (op == JSOP_SETALIASEDVAR)
name = ScopeCoordinateName(cx, script, pc);
else
name = script->getName(pc);
RootedId id(cx, NameToId(name));
RootedObject obj(cx, ToObjectFromStack(cx, lhs));
@ -6209,6 +6217,8 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub,
} else if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
if (!SetNameOperation(cx, script, pc, obj, rhs))
return false;
} else if (op == JSOP_SETALIASEDVAR) {
obj->asScope().setAliasedVar(cx, pc, name, rhs);
} else if (script->strict) {
if (!js::SetProperty<true>(cx, obj, id, rhs))
return false;

View File

@ -2799,7 +2799,7 @@ CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
return true;
}
typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape,
typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape,
HandleTypeObject, HeapSlot *);
static const VMFunction NewCallObjectInfo =
FunctionInfo<NewCallObjectFn>(NewCallObject);
@ -2815,25 +2815,33 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir)
OutOfLineCode *ool;
if (lir->slots()->isRegister()) {
ool = oolCallVM(NewCallObjectInfo, lir,
(ArgList(), ImmGCPtr(templateObj->lastProperty()),
ImmGCPtr(templateObj->type()),
(ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
ImmGCPtr(templateObj->lastProperty()),
ImmGCPtr(templateObj->hasLazyType() ? NULL : templateObj->type()),
ToRegister(lir->slots())),
StoreRegisterTo(obj));
} else {
ool = oolCallVM(NewCallObjectInfo, lir,
(ArgList(), ImmGCPtr(templateObj->lastProperty()),
ImmGCPtr(templateObj->type()),
(ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
ImmGCPtr(templateObj->lastProperty()),
ImmGCPtr(templateObj->hasLazyType() ? NULL : templateObj->type()),
ImmWord((void *)NULL)),
StoreRegisterTo(obj));
}
if (!ool)
return false;
masm.newGCThing(obj, templateObj, ool->entry());
masm.initGCThing(obj, templateObj);
if (lir->mir()->needsSingletonType()) {
// Objects can only be given singleton types in VM calls.
masm.jump(ool->entry());
} else {
masm.newGCThing(obj, templateObj, ool->entry());
masm.initGCThing(obj, templateObj);
if (lir->slots()->isRegister())
masm.storePtr(ToRegister(lir->slots()), Address(obj, JSObject::offsetOfSlots()));
}
if (lir->slots()->isRegister())
masm.storePtr(ToRegister(lir->slots()), Address(obj, JSObject::offsetOfSlots()));
masm.bind(ool->rejoin());
return true;
}
@ -5000,6 +5008,17 @@ CodeGenerator::visitGetArgument(LGetArgument *lir)
return true;
}
typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript);
static const VMFunction RunOnceScriptPrologueInfo =
FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue);
bool
CodeGenerator::visitRunOncePrologue(LRunOncePrologue *lir)
{
pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
return callVM(RunOnceScriptPrologueInfo, lir);
}
bool
CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
Register temp0, Register temp1, unsigned numFormals,
@ -5670,7 +5689,7 @@ CodeGenerator::visitBindNameIC(OutOfLineUpdateCache *ool, BindNameIC *ic)
}
typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
HandlePropertyName, const HandleValue, bool, bool);
HandlePropertyName, const HandleValue, bool, int);
static const VMFunction SetPropertyInfo =
FunctionInfo<SetPropertyFn>(SetProperty);
@ -5680,10 +5699,9 @@ CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value));
const Register objReg = ToRegister(ins->getOperand(0));
jsbytecode *pc = ins->mir()->resumePoint()->pc();
bool isSetName = JSOp(*pc) == JSOP_SETNAME || JSOp(*pc) == JSOP_SETGNAME;
JSOp op = JSOp(*ins->mir()->resumePoint()->pc());
pushArg(Imm32(isSetName));
pushArg(Imm32(op));
pushArg(Imm32(ins->mir()->strict()));
pushArg(value);

View File

@ -215,6 +215,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitIteratorEnd(LIteratorEnd *lir);
bool visitArgumentsLength(LArgumentsLength *lir);
bool visitGetArgument(LGetArgument *lir);
bool visitRunOncePrologue(LRunOncePrologue *lir);
bool emitRest(LInstruction *lir, Register array, Register numActuals,
Register temp0, Register temp1, unsigned numFormals,
JSObject *templateObject, const VMFunction &f);

View File

@ -1191,6 +1191,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_ARGUMENTS:
return jsop_arguments();
case JSOP_RUNONCE:
return jsop_runonce();
case JSOP_REST:
return jsop_rest();
@ -1302,7 +1305,8 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_CALLGNAME:
{
RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
return jsop_getgname(name);
RootedObject obj(cx, &script()->global());
return getStaticName(obj, name);
}
case JSOP_BINDGNAME:
@ -1311,7 +1315,8 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_SETGNAME:
{
RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
return jsop_setgname(name);
RootedObject obj(cx, &script()->global());
return setStaticName(obj, name);
}
case JSOP_NAME:
@ -4279,8 +4284,9 @@ IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
// Allocate the actual object. It is important that no intervening
// instructions could potentially bailout, thus leaking the dynamic slots
// pointer.
MInstruction *callObj = MNewCallObject::New(templateObj, slots);
// pointer. Run-once scripts need a singleton type, so always do a VM call
// in such cases.
MInstruction *callObj = MNewCallObject::New(templateObj, script()->treatAsRunOnce, slots);
current->add(callObj);
// Initialize the object's reserved slots. No post barrier is needed here,
@ -5900,44 +5906,45 @@ IonBuilder::pushTypeBarrier(MInstruction *ins, types::StackTypeSet *observed, bo
}
bool
IonBuilder::jsop_getgname(HandlePropertyName name)
IonBuilder::getStaticName(HandleObject staticObject, HandlePropertyName name)
{
// Optimize undefined, NaN, and Infinity.
if (name == cx->names().undefined)
return pushConstant(UndefinedValue());
if (name == cx->names().NaN)
return pushConstant(cx->runtime->NaNValue);
if (name == cx->names().Infinity)
return pushConstant(cx->runtime->positiveInfinityValue);
JS_ASSERT(staticObject->isGlobal() || staticObject->isCall());
RootedObject globalObj(cx, &script()->global());
JS_ASSERT(globalObj->isNative());
if (staticObject->isGlobal()) {
// Optimize undefined, NaN, and Infinity.
if (name == cx->names().undefined)
return pushConstant(UndefinedValue());
if (name == cx->names().NaN)
return pushConstant(cx->runtime->NaNValue);
if (name == cx->names().Infinity)
return pushConstant(cx->runtime->positiveInfinityValue);
}
RootedId id(cx, NameToId(name));
// For the fastest path, the property must be found, and it must be found
// as a normal data property on exactly the global object.
RootedShape shape(cx, globalObj->nativeLookup(cx, id));
RootedShape shape(cx, staticObject->nativeLookup(cx, id));
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
return jsop_getname(name);
types::TypeObject *globalType = globalObj->getType(cx);
if (!globalType)
types::TypeObject *staticType = staticObject->getType(cx);
if (!staticType)
return false;
types::HeapTypeSet *propertyTypes = NULL;
if (!globalType->unknownProperties()) {
propertyTypes = globalType->getProperty(cx, id, false);
if (!staticType->unknownProperties()) {
propertyTypes = staticType->getProperty(cx, id, false);
if (!propertyTypes)
return false;
}
if (propertyTypes && propertyTypes->isOwnProperty(cx, globalType, true)) {
if (propertyTypes && propertyTypes->isOwnProperty(cx, staticType, true)) {
// The property has been reconfigured as non-configurable, non-enumerable
// or non-writable.
return jsop_getname(name);
}
types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, globalType, name, types);
bool barrier = PropertyReadNeedsTypeBarrier(cx, staticType, name, types);
// If the property is permanent, a shape guard isn't necessary.
@ -5948,7 +5955,7 @@ IonBuilder::jsop_getgname(HandlePropertyName name)
if (singleton) {
// Try to inline a known constant value.
bool isKnownConstant;
if (!TestSingletonProperty(cx, globalObj, singleton, id, &isKnownConstant))
if (!TestSingletonProperty(cx, staticObject, singleton, id, &isKnownConstant))
return false;
if (isKnownConstant)
return pushConstant(ObjectValue(*singleton));
@ -5959,27 +5966,19 @@ IonBuilder::jsop_getgname(HandlePropertyName name)
return pushConstant(NullValue());
}
MInstruction *global = MConstant::New(ObjectValue(*globalObj));
current->add(global);
MInstruction *obj = MConstant::New(ObjectValue(*staticObject));
current->add(obj);
// If we have a property typeset, the isOwnProperty call will trigger recompilation if
// the property is deleted or reconfigured.
if (!propertyTypes && shape->configurable())
global = addShapeGuard(global, globalObj->lastProperty(), Bailout_ShapeGuard);
obj = addShapeGuard(obj, staticObject->lastProperty(), Bailout_ShapeGuard);
JS_ASSERT(shape->slot() >= globalObj->numFixedSlots());
MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
if (barrier)
rvalType = MIRType_Value;
MSlots *slots = MSlots::New(global);
current->add(slots);
MLoadSlot *load = MLoadSlot::New(slots, shape->slot() - globalObj->numFixedSlots());
current->add(load);
// Slot loads can be typed, if they have a single, known, definitive type.
if (knownType != JSVAL_TYPE_UNKNOWN && !barrier)
load->setResultType(MIRTypeFromValueType(knownType));
current->push(load);
return pushTypeBarrier(load, types, barrier);
return loadSlot(obj, shape, rvalType, barrier, types);
}
// Whether 'types' includes all possible values represented by input/inputTypes.
@ -6016,34 +6015,33 @@ ion::NeedsPostBarrier(CompileInfo &info, MDefinition *value)
}
bool
IonBuilder::jsop_setgname(HandlePropertyName name)
IonBuilder::setStaticName(HandleObject staticObject, HandlePropertyName name)
{
RootedObject globalObj(cx, &script()->global());
RootedId id(cx, NameToId(name));
JS_ASSERT(globalObj->isNative());
JS_ASSERT(staticObject->isGlobal() || staticObject->isCall());
MDefinition *value = current->peek(-1);
if (globalObj->watched())
if (staticObject->watched())
return jsop_setprop(name);
// For the fastest path, the property must be found, and it must be found
// as a normal data property on exactly the global object.
RootedShape shape(cx, globalObj->nativeLookup(cx, id));
RootedShape shape(cx, staticObject->nativeLookup(cx, id));
if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot())
return jsop_setprop(name);
types::TypeObject *globalType = globalObj->getType(cx);
if (!globalType)
types::TypeObject *staticType = staticObject->getType(cx);
if (!staticType)
return false;
types::HeapTypeSet *propertyTypes = NULL;
if (!globalType->unknownProperties()) {
propertyTypes = globalType->getProperty(cx, id, false);
if (!staticType->unknownProperties()) {
propertyTypes = staticType->getProperty(cx, id, false);
if (!propertyTypes)
return false;
}
if (!propertyTypes || propertyTypes->isOwnProperty(cx, globalType, true)) {
if (!propertyTypes || propertyTypes->isOwnProperty(cx, staticType, true)) {
// The property has been reconfigured as non-configurable, non-enumerable
// or non-writable.
return jsop_setprop(name);
@ -6051,48 +6049,36 @@ IonBuilder::jsop_setgname(HandlePropertyName name)
if (!TypeSetIncludes(propertyTypes, value->type(), value->resultTypeSet()))
return jsop_setprop(name);
MInstruction *global = MConstant::New(ObjectValue(*globalObj));
current->add(global);
current->pop();
// Pop the bound object on the stack.
MDefinition *obj = current->pop();
JS_ASSERT(&obj->toConstant()->value().toObject() == staticObject);
// If we have a property type set, the isOwnProperty call will trigger recompilation
// if the property is deleted or reconfigured. Without TI, we always need a shape guard
// to guard against the property being reconfigured as non-writable.
if (!propertyTypes)
global = addShapeGuard(global, globalObj->lastProperty(), Bailout_ShapeGuard);
JS_ASSERT(shape->slot() >= globalObj->numFixedSlots());
MSlots *slots = MSlots::New(global);
current->add(slots);
obj = addShapeGuard(obj, staticObject->lastProperty(), Bailout_ShapeGuard);
// Note: we do not use a post barrier when writing to the global object.
// Slots in the global object will be treated as roots during a minor GC.
current->pop();
MStoreSlot *store = MStoreSlot::New(slots, shape->slot() - globalObj->numFixedSlots(), value);
current->add(store);
// Determine whether write barrier is required.
if (!propertyTypes || propertyTypes->needsBarrier(cx))
store->setNeedsBarrier();
// Pop the global object pushed by bindgname.
DebugOnly<MDefinition *> pushedGlobal = current->pop();
JS_ASSERT(&pushedGlobal->toConstant()->value().toObject() == globalObj);
if (!staticObject->isGlobal() && NeedsPostBarrier(info(), value))
current->add(MPostWriteBarrier::New(obj, value));
// If the property has a known type, we may be able to optimize typed stores by not
// storing the type tag. This only works if the property does not have its initial
// |undefined| value; if |undefined| is assigned at a later point, it will be added
// to the type set.
if (propertyTypes && !globalObj->getSlot(shape->slot()).isUndefined()) {
MIRType slotType = MIRType_None;
if (propertyTypes && !staticObject->getSlot(shape->slot()).isUndefined()) {
JSValueType knownType = propertyTypes->getKnownTypeTag(cx);
if (knownType != JSVAL_TYPE_UNKNOWN)
store->setSlotType(MIRTypeFromValueType(knownType));
slotType = MIRTypeFromValueType(knownType);
}
JS_ASSERT_IF(store->needsBarrier(), store->slotType() != MIRType_None);
current->push(value);
return resumeAfter(store);
bool needsBarrier = !propertyTypes || propertyTypes->needsBarrier(cx);
return storeSlot(obj, shape, value, needsBarrier, slotType);
}
bool
@ -7018,6 +7004,14 @@ GetDefiniteSlot(JSContext *cx, types::StackTypeSet *types, JSAtom *atom)
return propertyTypes;
}
bool
IonBuilder::jsop_runonce()
{
MRunOncePrologue *ins = MRunOncePrologue::New();
current->add(ins);
return resumeAfter(ins);
}
bool
IonBuilder::jsop_not()
{
@ -7364,7 +7358,8 @@ IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
}
bool
IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier)
IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
MIRType slotType /* = MIRType_None */)
{
JS_ASSERT(shape->hasDefaultSetter());
JS_ASSERT(shape->writable());
@ -7387,6 +7382,8 @@ IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool n
current->push(value);
if (needsBarrier)
store->setNeedsBarrier();
if (slotType != MIRType_None)
store->setSlotType(slotType);
return resumeAfter(store);
}
@ -8095,9 +8092,70 @@ IonBuilder::walkScopeChain(unsigned hops)
return scope;
}
bool
IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, MutableHandleObject pcall)
{
JSScript *outerScript = ScopeCoordinateFunctionScript(cx, script(), pc);
if (!outerScript || !outerScript->treatAsRunOnce)
return false;
types::TypeObject *funType = outerScript->function()->getType(cx);
if (!funType)
return false;
if (types::HeapTypeSet::HasObjectFlags(cx, funType, types::OBJECT_FLAG_RUNONCE_INVALIDATED))
return false;
// The script this aliased var operation is accessing will run only once,
// so there will be only one call object and the aliased var access can be
// compiled in the same manner as a global access. We still need to find
// the call object though.
// Look for the call object on the current script's function's scope chain.
// If the current script is inner to the outer script and the function has
// singleton type then it should show up here.
MDefinition *scope = current->getSlot(info().scopeChainSlot());
scope->setFoldedUnchecked();
JSObject *environment = script()->function()->environment();
while (environment && !environment->isGlobal()) {
if (environment->isCall() &&
!environment->asCall().isForEval() &&
environment->asCall().callee().nonLazyScript() == outerScript)
{
JS_ASSERT(environment->hasSingletonType());
pcall.set(environment);
return true;
}
environment = environment->enclosingScope();
}
// Look for the call object on the current frame, if we are compiling the
// outer script itself. Don't do this if we are at entry to the outer
// script, as the call object we see will not be the real one --- after
// entering the Ion code a different call object will be created.
if (script() == outerScript && fp && info().osrPc()) {
JSObject *scope = fp.scopeChain();
if (scope->isCall() && scope->asCall().callee().nonLazyScript() == outerScript) {
JS_ASSERT(scope->hasSingletonType());
pcall.set(scope);
return true;
}
}
return true;
}
bool
IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc)
{
RootedObject call(cx);
if (hasStaticScopeObject(sc, &call) && call) {
RootedPropertyName name(cx, ScopeCoordinateName(cx, script(), pc));
return getStaticName(call, name);
}
MDefinition *obj = walkScopeChain(sc.hops);
RootedShape shape(cx, ScopeCoordinateToStaticScopeShape(cx, script(), pc));
@ -8122,6 +8180,34 @@ IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc)
bool
IonBuilder::jsop_setaliasedvar(ScopeCoordinate sc)
{
RootedObject call(cx);
if (hasStaticScopeObject(sc, &call)) {
uint32_t depth = current->stackDepth() + 1;
if (depth > current->nslots()) {
if (!current->increaseSlots(depth - current->nslots()))
return false;
}
MDefinition *value = current->pop();
RootedPropertyName name(cx, ScopeCoordinateName(cx, script(), pc));
if (call) {
// Push the object on the stack to match the bound object expected in
// the global and property set cases.
MInstruction *constant = MConstant::New(ObjectValue(*call));
current->add(constant);
current->push(constant);
current->push(value);
return setStaticName(call, name);
}
// The call object has type information we need to respect but we
// couldn't find it. Just do a normal property assign.
MDefinition *obj = walkScopeChain(sc.hops);
current->push(obj);
current->push(value);
return jsop_setprop(name);
}
MDefinition *rval = current->peek(-1);
MDefinition *obj = walkScopeChain(sc.hops);

View File

@ -339,9 +339,11 @@ class IonBuilder : public MIRGenerator
bool invalidatedIdempotentCache();
bool hasStaticScopeObject(ScopeCoordinate sc, MutableHandleObject pcall);
bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
bool barrier, types::StackTypeSet *types);
bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier);
bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
MIRType slotType = MIRType_None);
// jsop_getprop() helpers.
bool getPropTryArgumentsLength(bool *emitted);
@ -381,8 +383,8 @@ class IonBuilder : public MIRGenerator
bool jsop_dup2();
bool jsop_loophead(jsbytecode *pc);
bool jsop_compare(JSOp op);
bool jsop_getgname(HandlePropertyName name);
bool jsop_setgname(HandlePropertyName name);
bool getStaticName(HandleObject staticObject, HandlePropertyName name);
bool setStaticName(HandleObject staticObject, HandlePropertyName name);
bool jsop_getname(HandlePropertyName name);
bool jsop_intrinsic(HandlePropertyName name);
bool jsop_bindname(PropertyName *name);
@ -406,6 +408,7 @@ class IonBuilder : public MIRGenerator
bool jsop_arguments_length();
bool jsop_arguments_getelem();
bool jsop_arguments_setelem(MDefinition *object, MDefinition *index, MDefinition *value);
bool jsop_runonce();
bool jsop_rest();
bool jsop_not();
bool jsop_getprop(HandlePropertyName name);

View File

@ -4106,6 +4106,16 @@ class LGetArgument : public LInstructionHelper<BOX_PIECES, 1, 0>
}
};
class LRunOncePrologue : public LCallInstructionHelper<0, 0, 0>
{
public:
LIR_HEADER(RunOncePrologue)
MRunOncePrologue *mir() const {
return mir_->toRunOncePrologue();
}
};
// Create the rest parameter.
class LRest : public LCallInstructionHelper<1, 1, 3>
{
@ -4151,7 +4161,6 @@ class LParRest : public LCallInstructionHelper<1, 2, 3>
}
};
class LParWriteGuard : public LCallInstructionHelper<0, 2, 1>
{
public:

View File

@ -205,6 +205,7 @@
_(StringLength) \
_(ArgumentsLength) \
_(GetArgument) \
_(RunOncePrologue) \
_(Rest) \
_(ParRest) \
_(TypeOfV) \

View File

@ -2472,6 +2472,13 @@ LIRGenerator::visitGetArgument(MGetArgument *ins)
return defineBox(lir, ins);
}
bool
LIRGenerator::visitRunOncePrologue(MRunOncePrologue *ins)
{
LRunOncePrologue *lir = new LRunOncePrologue;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitRest(MRest *ins)
{

View File

@ -219,6 +219,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitStringLength(MStringLength *ins);
bool visitArgumentsLength(MArgumentsLength *ins);
bool visitGetArgument(MGetArgument *ins);
bool visitRunOncePrologue(MRunOncePrologue *ins);
bool visitRest(MRest *ins);
bool visitParRest(MParRest *ins);
bool visitThrow(MThrow *ins);

View File

@ -2273,6 +2273,23 @@ class MSetArgumentsObjectArg
}
};
class MRunOncePrologue
: public MNullaryInstruction
{
protected:
MRunOncePrologue()
{
setGuard();
}
public:
INSTRUCTION_HEADER(RunOncePrologue)
static MRunOncePrologue *New() {
return new MRunOncePrologue();
}
};
// Given a MIRType_Value A and a MIRType_Object B:
// If the Value may be safely unboxed to an Object, return Object(A).
// Otherwise, return B.
@ -7350,10 +7367,12 @@ class MNewDeclEnvObject : public MNullaryInstruction
class MNewCallObject : public MUnaryInstruction
{
CompilerRootObject templateObj_;
bool needsSingletonType_;
MNewCallObject(HandleObject templateObj, MDefinition *slots)
MNewCallObject(HandleObject templateObj, bool needsSingletonType, MDefinition *slots)
: MUnaryInstruction(slots),
templateObj_(templateObj)
templateObj_(templateObj),
needsSingletonType_(needsSingletonType)
{
setResultType(MIRType_Object);
}
@ -7361,8 +7380,8 @@ class MNewCallObject : public MUnaryInstruction
public:
INSTRUCTION_HEADER(NewCallObject)
static MNewCallObject *New(HandleObject templateObj, MDefinition *slots) {
return new MNewCallObject(templateObj, slots);
static MNewCallObject *New(HandleObject templateObj, bool needsSingletonType, MDefinition *slots) {
return new MNewCallObject(templateObj, needsSingletonType, slots);
}
MDefinition *slots() {
@ -7371,6 +7390,9 @@ class MNewCallObject : public MUnaryInstruction
JSObject *templateObject() {
return templateObj_;
}
bool needsSingletonType() {
return needsSingletonType_;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}

View File

@ -154,6 +154,7 @@ namespace ion {
_(StringLength) \
_(ArgumentsLength) \
_(GetArgument) \
_(RunOncePrologue) \
_(Rest) \
_(Floor) \
_(Round) \

View File

@ -239,6 +239,7 @@ class ParallelArrayVisitor : public MInstructionVisitor
SAFE_OP(StringLength)
UNSAFE_OP(ArgumentsLength)
UNSAFE_OP(GetArgument)
UNSAFE_OP(RunOncePrologue)
CUSTOM_OP(Rest)
SAFE_OP(ParRest)
SAFE_OP(Floor)

View File

@ -388,13 +388,22 @@ StringFromCharCode(JSContext *cx, int32_t code)
bool
SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
bool strict, bool isSetName)
bool strict, int jsop)
{
RootedValue v(cx, value);
RootedId id(cx, NameToId(name));
if (jsop == JSOP_SETALIASEDVAR) {
// Aliased var assigns ignore readonly attributes on the property, as
// required for initializing 'const' closure variables.
Shape *shape = obj->nativeLookup(cx, name);
JS_ASSERT(shape && shape->hasSlot());
JSObject::nativeSetSlotWithType(cx, obj, shape, value);
return true;
}
if (JS_LIKELY(!obj->getOps()->setProperty)) {
unsigned defineHow = isSetName ? DNP_UNQUALIFIED : 0;
unsigned defineHow = (jsop == JSOP_SETNAME || jsop == JSOP_SETGNAME) ? DNP_UNQUALIFIED : 0;
return baseops::SetPropertyHelper(cx, obj, obj, id, defineHow, &v, strict);
}
@ -425,9 +434,10 @@ NewSlots(JSRuntime *rt, unsigned nslots)
}
JSObject *
NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
NewCallObject(JSContext *cx, HandleScript script,
HandleShape shape, HandleTypeObject type, HeapSlot *slots)
{
return CallObject::create(cx, shape, type, slots);
return CallObject::create(cx, script, shape, type, slots);
}
JSObject *

View File

@ -510,12 +510,13 @@ bool CharCodeAt(JSContext *cx, HandleString str, int32_t index, uint32_t *code);
JSFlatString *StringFromCharCode(JSContext *cx, int32_t code);
bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
bool strict, bool isSetName);
bool strict, int jsop);
bool InterruptCheck(JSContext *cx);
HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
JSObject *NewCallObject(JSContext *cx, HandleScript script,
HandleShape shape, HandleTypeObject type, HeapSlot *slots);
JSObject *NewStringObject(JSContext *cx, HandleString str);
bool SPSEnter(JSContext *cx, HandleScript script);

View File

@ -0,0 +1,8 @@
assertEq((function() {
for (var i = 0; i < 5; i++) {
print(i);
}
const x = 3;
(function() x++)();
return x
})(), 3);

View File

@ -0,0 +1,28 @@
// Test that closures are deoptimized if they unexpectedly run multiple times.
with({}){}
(function(n) {
if (n == 20) {
outer = (function f() { return f.caller; })();
inner = function g() { return x++; }
}
var x = 0;
for (var i = 0; i < n; i++)
x++;
if (n != 20)
inner = function g() { return x++; }
})(20);
oldInner = inner;
for (var i = 0; i < 5; i++)
assertEq(oldInner(), 20 + i);
outer(40);
for (var i = 0; i < 5; i++)
assertEq(inner(), 40 + i);
for (var i = 0; i < 5; i++)
assertEq(oldInner(), 25 + i);

View File

@ -4123,6 +4123,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferen
case JSOP_TABLESWITCH:
case JSOP_TRY:
case JSOP_LABEL:
case JSOP_RUNONCE:
break;
/* Bytecodes pushing values of known type. */

View File

@ -395,8 +395,14 @@ enum {
/* Whether any objects emulate undefined; see EmulatesUndefined. */
OBJECT_FLAG_EMULATES_UNDEFINED = 0x00400000,
/*
* For the function on a run-once script, whether the function has actually
* run multiple times.
*/
OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00800000,
/* Flags which indicate dynamic properties of represented objects. */
OBJECT_FLAG_DYNAMIC_MASK = 0x007f0000,
OBJECT_FLAG_DYNAMIC_MASK = 0x00ff0000,
/*
* Whether all properties of this object are considered unknown.

View File

@ -13,6 +13,8 @@
#include "jscompartment.h"
#include "jsinfer.h"
#include "jsprf.h"
#include "jsproxy.h"
#include "jstypedarray.h"
#include "builtin/ParallelArray.h"
#ifdef JS_ION

View File

@ -1290,7 +1290,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
/* No-ops for ease of decompilation. */
ADD_EMPTY_CASE(JSOP_NOP)
ADD_EMPTY_CASE(JSOP_UNUSED71)
ADD_EMPTY_CASE(JSOP_UNUSED132)
ADD_EMPTY_CASE(JSOP_UNUSED148)
ADD_EMPTY_CASE(JSOP_UNUSED161)
@ -2523,6 +2522,13 @@ BEGIN_CASE(JSOP_ARGUMENTS)
}
END_CASE(JSOP_ARGUMENTS)
BEGIN_CASE(JSOP_RUNONCE)
{
if (!RunOnceScriptPrologue(cx, script))
goto error;
}
END_CASE(JSOP_RUNONCE)
BEGIN_CASE(JSOP_REST)
{
RootedObject &rest = rootObject0;
@ -2551,7 +2557,13 @@ END_CASE(JSOP_GETALIASEDVAR)
BEGIN_CASE(JSOP_SETALIASEDVAR)
{
ScopeCoordinate sc = ScopeCoordinate(regs.pc);
regs.fp()->aliasedVarScope(sc).setAliasedVar(sc, regs.sp[-1]);
ScopeObject &obj = regs.fp()->aliasedVarScope(sc);
// Avoid computing the name if no type updates are needed, as this may be
// expensive on scopes with large numbers of variables.
PropertyName *name = obj.hasSingletonType() ? ScopeCoordinateName(cx, script, regs.pc) : NULL;
obj.setAliasedVar(cx, sc, name, regs.sp[-1]);
}
END_CASE(JSOP_SETALIASEDVAR)
@ -3644,3 +3656,22 @@ js::ImplicitThisOperation(JSContext *cx, HandleObject scopeObj, HandlePropertyNa
return ComputeImplicitThis(cx, obj, res);
}
bool
js::RunOnceScriptPrologue(JSContext *cx, HandleScript script)
{
JS_ASSERT(script->treatAsRunOnce);
if (!script->hasRunOnce) {
script->hasRunOnce = true;
return true;
}
// Force instantiation of the script's function's type to ensure the flag
// is preserved in type information.
if (!script->function()->getType(cx))
return false;
types::MarkTypeObjectFlags(cx, script->function(), types::OBJECT_FLAG_RUNONCE_INVALIDATED);
return true;
}

View File

@ -414,6 +414,9 @@ IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, MutableHandleValue rv
bool
IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
bool
RunOnceScriptPrologue(JSContext *cx, HandleScript script);
} /* namespace js */
#endif /* jsinterp_h___ */

View File

@ -160,7 +160,12 @@ OPDEF(JSOP_AND, 69, "and", NULL, 5, 1, 1, 6, JOF_JUMP|J
/* The switch bytecodes have variable length. */
OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING)
OPDEF(JSOP_UNUSED71, 71, "unused71", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Prologue emitted in scripts expected to run once, which deoptimizes code if
* it executes multiple times.
*/
OPDEF(JSOP_RUNONCE, 71, "runonce", NULL, 1, 0, 0, 0, JOF_BYTE)
/* New, infallible/transitive identity ops. */
OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC|JOF_ARITH)

View File

@ -2776,7 +2776,7 @@ js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame,
JS_ASSERT(*pc == JSOP_SETALIASEDVAR);
if (frame.callObj().asScope().aliasedVar(pc).isMagic(JS_OPTIMIZED_ARGUMENTS))
frame.callObj().asScope().setAliasedVar(pc, ObjectValue(*argsobj));
frame.callObj().asScope().setAliasedVar(cx, pc, cx->names().arguments, ObjectValue(*argsobj));
} else {
if (frame.unaliasedLocal(var).isMagic(JS_OPTIMIZED_ARGUMENTS))
frame.unaliasedLocal(var) = ObjectValue(*argsobj);

View File

@ -411,6 +411,7 @@ class JSScript : public js::gc::Cell
script */
bool hasSingletons:1; /* script has singleton objects */
bool treatAsRunOnce:1; /* script is a lambda to treat as running once. */
bool hasRunOnce:1; /* if treatAsRunOnce, whether script has executed. */
bool hasBeenCloned:1; /* script has been reused for a clone. */
bool isActiveEval:1; /* script came from eval(), and is still active */
bool isCachedEval:1; /* script came from eval(), and is in eval cache */

View File

@ -81,7 +81,7 @@ ArgumentsObject::element(uint32_t i) const
}
inline void
ArgumentsObject::setElement(uint32_t i, const Value &v)
ArgumentsObject::setElement(JSContext *cx, uint32_t i, const Value &v)
{
JS_ASSERT(!isElementDeleted(i));
HeapValue &lhs = data()->args[i];
@ -89,7 +89,7 @@ ArgumentsObject::setElement(uint32_t i, const Value &v)
CallObject &callobj = getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall();
for (AliasedFormalIter fi(callobj.callee().nonLazyScript()); ; fi++) {
if (fi.frameIndex() == i) {
callobj.setAliasedVar(fi, v);
callobj.setAliasedVar(cx, fi, fi->name(), v);
return;
}
}

View File

@ -352,7 +352,7 @@ ArgSetter(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, MutableHa
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
argsobj.setElement(arg, vp);
argsobj.setElement(cx, arg, vp);
if (arg < script->function()->nargs) {
if (!script->ensureHasTypes(cx))
return false;
@ -478,7 +478,7 @@ StrictArgSetter(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, Mut
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj->initialLength()) {
argsobj->setElement(arg, vp);
argsobj->setElement(cx, arg, vp);
return true;
}
} else {

View File

@ -177,7 +177,7 @@ class ArgumentsObject : public JSObject
* needed, the frontend should have emitted JSOP_GETALIASEDVAR.
*/
inline const Value &element(uint32_t i) const;
inline void setElement(uint32_t i, const Value &v);
inline void setElement(JSContext *cx, uint32_t i, const Value &v);
inline const Value &arg(unsigned i) const;
inline void setArg(unsigned i, const Value &v);

View File

@ -9,6 +9,7 @@
#include "ScopeObject.h"
#include "jsinferinlines.h"
#include "jsscriptinlines.h"
namespace js {
@ -42,11 +43,17 @@ ScopeObject::aliasedVar(ScopeCoordinate sc)
}
inline void
ScopeObject::setAliasedVar(ScopeCoordinate sc, const Value &v)
ScopeObject::setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v)
{
JS_ASSERT(isCall() || isClonedBlock());
JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS);
// name may be null for non-singletons, whose types do not need to be tracked.
JS_ASSERT_IF(hasSingletonType(), name);
setSlot(sc.slot, v);
if (hasSingletonType())
types::AddTypePropertyId(cx, this, NameToId(name), v);
}
/*static*/ inline size_t
@ -77,9 +84,12 @@ CallObject::aliasedVar(AliasedFormalIter fi)
}
inline void
CallObject::setAliasedVar(AliasedFormalIter fi, const Value &v)
CallObject::setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name, const Value &v)
{
JS_ASSERT(name == fi->name());
setSlot(fi.scopeSlot(), v);
if (hasSingletonType())
types::AddTypePropertyId(cx, this, NameToId(name), v);
}
/*static*/ inline size_t

View File

@ -28,7 +28,7 @@ typedef Rooted<ArgumentsObject *> RootedArgumentsObject;
/*****************************************************************************/
StaticScopeIter::StaticScopeIter(JSContext *cx, HandleObject objArg)
StaticScopeIter::StaticScopeIter(JSContext *cx, JSObject *objArg)
: obj(cx, objArg), onNamedLambda(false)
{
JS_ASSERT_IF(obj, obj->isStaticBlock() || obj->isFunction());
@ -97,20 +97,23 @@ StaticScopeIter::funScript() const
/*****************************************************************************/
Shape *
js::ScopeCoordinateToStaticScopeShape(JSContext *cx, JSScript *script, jsbytecode *pc)
static JSObject *
InnermostStaticScope(JSScript *script, jsbytecode *pc)
{
JS_ASSERT(pc >= script->code && pc < script->code + script->length);
JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
uint32_t blockIndex = GET_UINT32_INDEX(pc + 2 * sizeof(uint16_t));
RootedObject innermostStaticScope(cx, NULL);
if (blockIndex == UINT32_MAX)
innermostStaticScope = script->function();
else
innermostStaticScope = &script->getObject(blockIndex)->asStaticBlock();
StaticScopeIter ssi(cx, innermostStaticScope);
if (blockIndex == UINT32_MAX)
return script->function();
return &script->getObject(blockIndex)->asStaticBlock();
}
Shape *
js::ScopeCoordinateToStaticScopeShape(JSContext *cx, JSScript *script, jsbytecode *pc)
{
StaticScopeIter ssi(cx, InnermostStaticScope(script, pc));
ScopeCoordinate sc(pc);
while (true) {
if (ssi.hasDynamicScopeObject()) {
@ -138,6 +141,24 @@ js::ScopeCoordinateName(JSContext *cx, JSScript *script, jsbytecode *pc)
return JSID_TO_ATOM(id)->asPropertyName();
}
JSScript *
js::ScopeCoordinateFunctionScript(JSContext *cx, JSScript *script, jsbytecode *pc)
{
StaticScopeIter ssi(cx, InnermostStaticScope(script, pc));
ScopeCoordinate sc(pc);
while (true) {
if (ssi.hasDynamicScopeObject()) {
if (!sc.hops)
break;
sc.hops--;
}
ssi++;
}
if (ssi.type() != StaticScopeIter::FUNCTION)
return NULL;
return ssi.funScript();
}
/*****************************************************************************/
/*
@ -145,16 +166,24 @@ js::ScopeCoordinateName(JSContext *cx, JSScript *script, jsbytecode *pc)
* The call object must be further initialized to be usable.
*/
CallObject *
CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
{
gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
JS_ASSERT(CanBeFinalizedInBackground(kind, &CallClass));
kind = gc::GetBackgroundAllocKind(kind);
JSObject *obj = JSObject::create(cx, kind, GetInitialHeap(GenericObject, &CallClass),
shape, type, slots);
gc::InitialHeap heap = script->treatAsRunOnce ? gc::TenuredHeap : gc::DefaultHeap;
JSObject *obj = JSObject::create(cx, kind, heap, shape, type, slots);
if (!obj)
return NULL;
if (script->treatAsRunOnce) {
RootedObject nobj(cx, obj);
if (!JSObject::setSingletonType(cx, nobj))
return NULL;
return &nobj->asCall();
}
return &obj->asCall();
}
@ -199,6 +228,14 @@ CallObject::create(JSContext *cx, HandleScript script, HandleObject enclosing, H
callobj->asScope().setEnclosingScope(enclosing);
callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
if (script->treatAsRunOnce) {
Rooted<CallObject*> ncallobj(cx, callobj);
if (!JSObject::setSingletonType(cx, ncallobj))
return NULL;
return ncallobj;
}
return callobj;
}
@ -236,8 +273,10 @@ CallObject::createForFunction(JSContext *cx, AbstractFramePtr frame)
return NULL;
/* Copy in the closed-over formal arguments. */
for (AliasedFormalIter i(frame.script()); i; i++)
callobj->setAliasedVar(i, frame.unaliasedFormal(i.frameIndex(), DONT_CHECK_ALIASING));
for (AliasedFormalIter i(frame.script()); i; i++) {
callobj->setAliasedVar(cx, i, i->name(),
frame.unaliasedFormal(i.frameIndex(), DONT_CHECK_ALIASING));
}
return callobj;
}

View File

@ -59,7 +59,7 @@ class StaticScopeIter
bool onNamedLambda;
public:
explicit StaticScopeIter(JSContext *cx, HandleObject obj);
explicit StaticScopeIter(JSContext *cx, JSObject *obj);
bool done() const;
void operator++(int);
@ -106,6 +106,10 @@ ScopeCoordinateToStaticScopeShape(JSContext *cx, JSScript *script, jsbytecode *p
extern PropertyName *
ScopeCoordinateName(JSContext *cx, JSScript *script, jsbytecode *pc);
/* Return the function script accessed by the given ALIASEDVAR op, or NULL. */
extern JSScript *
ScopeCoordinateFunctionScript(JSContext *cx, JSScript *script, jsbytecode *pc);
/*****************************************************************************/
/*
@ -167,7 +171,7 @@ class ScopeObject : public JSObject
* take a ScopeCoordinate instead of just the slot index.
*/
inline const Value &aliasedVar(ScopeCoordinate sc);
inline void setAliasedVar(ScopeCoordinate sc, const Value &v);
inline void setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v);
/* For jit access. */
static inline size_t offsetOfEnclosingScope();
@ -187,7 +191,7 @@ class CallObject : public ScopeObject
public:
/* These functions are internal and are exposed only for JITs. */
static CallObject *
create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
static CallObject *
createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap);
@ -210,7 +214,7 @@ class CallObject : public ScopeObject
/* Get/set the aliased variable referred to by 'bi'. */
inline const Value &aliasedVar(AliasedFormalIter fi);
inline void setAliasedVar(AliasedFormalIter fi, const Value &v);
inline void setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name, const Value &v);
/* For jit access. */
static inline size_t offsetOfCallee();

View File

@ -26,7 +26,7 @@ namespace js {
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 144);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 145);
class XDRBuffer {
public: