зеркало из https://github.com/mozilla/gecko-dev.git
Bug 589199 - Support global lexicals in Ion. (r=jandem)
This commit is contained in:
Родитель
934bb0bb13
Коммит
e168c18e3b
|
@ -706,14 +706,14 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
|
|||
}
|
||||
} else {
|
||||
// For global scripts without a non-syntactic scope the scope
|
||||
// chain is the script's global (Ion does not compile scripts
|
||||
// with a non-syntactic global scope). Also note that it's
|
||||
// invalid to resume into the prologue in this case because
|
||||
// the prologue expects the scope chain in R1 for eval and
|
||||
// global scripts.
|
||||
// chain is the script's global lexical scope (Ion does not
|
||||
// compile scripts with a non-syntactic global scope). Also
|
||||
// note that it's invalid to resume into the prologue in this
|
||||
// case because the prologue expects the scope chain in R1 for
|
||||
// eval and global scripts.
|
||||
MOZ_ASSERT(!script->isForEval());
|
||||
MOZ_ASSERT(!script->hasNonSyntacticScope());
|
||||
scopeChain = &(script->global());
|
||||
scopeChain = &(script->global().lexicalScope());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,7 @@ using namespace js::jit;
|
|||
bool
|
||||
FrameInfo::init(TempAllocator& alloc)
|
||||
{
|
||||
// The minimum stack size is two. One slot is always needed for
|
||||
// this/arguments type checks. Two slots are needed because INITGLEXICAL
|
||||
// (stack depth 1) is compiled as a SETPROP (stack depth 2) on the global
|
||||
// lexical scope.
|
||||
size_t nstack = Max(script->nslots() - script->nfixed(), size_t(2));
|
||||
size_t nstack = Max(script->nslots() - script->nfixed(), size_t(MinJITStackSize));
|
||||
if (!stack.init(alloc, nstack))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -167,8 +167,6 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
|
|||
case JSOP_LAMBDA_ARROW:
|
||||
case JSOP_DEFFUN:
|
||||
case JSOP_DEFVAR:
|
||||
case JSOP_DEFCONST:
|
||||
case JSOP_SETCONST:
|
||||
usesScopeChain_ = true;
|
||||
break;
|
||||
|
||||
|
|
|
@ -3713,9 +3713,8 @@ CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir)
|
|||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
typedef bool (*DefVarOrConstFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
|
||||
static const VMFunction DefVarOrConstInfo =
|
||||
FunctionInfo<DefVarOrConstFn>(DefVarOrConst);
|
||||
typedef bool (*DefVarFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
|
||||
static const VMFunction DefVarInfo = FunctionInfo<DefVarFn>(DefVar);
|
||||
|
||||
void
|
||||
CodeGenerator::visitDefVar(LDefVar* lir)
|
||||
|
@ -3726,7 +3725,19 @@ CodeGenerator::visitDefVar(LDefVar* lir)
|
|||
pushArg(Imm32(lir->mir()->attrs())); // unsigned
|
||||
pushArg(ImmGCPtr(lir->mir()->name())); // PropertyName*
|
||||
|
||||
callVM(DefVarOrConstInfo, lir);
|
||||
callVM(DefVarInfo, lir);
|
||||
}
|
||||
|
||||
typedef bool (*DefLexicalFn)(JSContext*, HandlePropertyName, unsigned);
|
||||
static const VMFunction DefLexicalInfo = FunctionInfo<DefLexicalFn>(DefLexicalOperation);
|
||||
|
||||
void
|
||||
CodeGenerator::visitDefLexical(LDefLexical* lir)
|
||||
{
|
||||
pushArg(Imm32(lir->mir()->attrs())); // unsigned
|
||||
pushArg(ImmGCPtr(lir->mir()->name())); // PropertyName*
|
||||
|
||||
callVM(DefLexicalInfo, lir);
|
||||
}
|
||||
|
||||
typedef bool (*DefFunOperationFn)(JSContext*, HandleScript, HandleObject, HandleFunction);
|
||||
|
|
|
@ -73,6 +73,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
void visitStart(LStart* lir);
|
||||
void visitReturn(LReturn* ret);
|
||||
void visitDefVar(LDefVar* lir);
|
||||
void visitDefLexical(LDefLexical* lir);
|
||||
void visitDefFun(LDefFun* lir);
|
||||
void visitOsrEntry(LOsrEntry* lir);
|
||||
void visitOsrScopeChain(LOsrScopeChain* lir);
|
||||
|
|
|
@ -221,7 +221,7 @@ class CompileInfo
|
|||
nbodyfixed_ = script->nbodyfixed();
|
||||
nlocals_ = script->nfixed();
|
||||
fixedLexicalBegin_ = script->fixedLexicalBegin();
|
||||
nstack_ = script->nslots() - script->nfixed();
|
||||
nstack_ = Max<unsigned>(script->nslots() - script->nfixed(), MinJITStackSize);
|
||||
nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
|
||||
}
|
||||
|
||||
|
|
|
@ -1235,10 +1235,10 @@ IonBuilder::initScopeChain(MDefinition* callee)
|
|||
scope = constant(ObjectValue(module->initialEnvironment()));
|
||||
} else {
|
||||
// For global scripts without a non-syntactic global scope, the scope
|
||||
// chain is the global object.
|
||||
// chain is the global lexical scope.
|
||||
MOZ_ASSERT(!script()->isForEval());
|
||||
MOZ_ASSERT(!script()->hasNonSyntacticScope());
|
||||
scope = constant(ObjectValue(script()->global()));
|
||||
scope = constant(ObjectValue(script()->global().lexicalScope()));
|
||||
}
|
||||
|
||||
current->setScopeChain(scope);
|
||||
|
@ -1675,9 +1675,12 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
return jsop_andor(op);
|
||||
|
||||
case JSOP_DEFVAR:
|
||||
case JSOP_DEFCONST:
|
||||
return jsop_defvar(GET_UINT32_INDEX(pc));
|
||||
|
||||
case JSOP_DEFLET:
|
||||
case JSOP_DEFCONST:
|
||||
return jsop_deflexical(GET_UINT32_INDEX(pc));
|
||||
|
||||
case JSOP_DEFFUN:
|
||||
return jsop_deffun(GET_UINT32_INDEX(pc));
|
||||
|
||||
|
@ -1764,6 +1767,13 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
current->setLocal(GET_LOCALNO(pc));
|
||||
return true;
|
||||
|
||||
case JSOP_INITGLEXICAL: {
|
||||
MDefinition* value = current->pop();
|
||||
current->push(constant(ObjectValue(script()->global().lexicalScope())));
|
||||
current->push(value);
|
||||
return jsop_setprop(info().getAtom(pc)->asPropertyName());
|
||||
}
|
||||
|
||||
case JSOP_CHECKALIASEDLEXICAL:
|
||||
return jsop_checkaliasedlet(ScopeCoordinate(pc));
|
||||
|
||||
|
@ -1870,7 +1880,7 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
PropertyName* name = info().getAtom(pc)->asPropertyName();
|
||||
if (script()->hasNonSyntacticScope())
|
||||
return jsop_setprop(name);
|
||||
JSObject* obj = &script()->global();
|
||||
JSObject* obj = testGlobalLexicalBinding(name);
|
||||
return setStaticName(obj, name);
|
||||
}
|
||||
|
||||
|
@ -1887,8 +1897,10 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
}
|
||||
|
||||
case JSOP_BINDGNAME:
|
||||
if (!script()->hasNonSyntacticScope())
|
||||
return pushConstant(ObjectValue(script()->global()));
|
||||
if (!script()->hasNonSyntacticScope()) {
|
||||
JSObject* scope = testGlobalLexicalBinding(info().getName(pc));
|
||||
return pushConstant(ObjectValue(*scope));
|
||||
}
|
||||
// Fall through to JSOP_BINDNAME
|
||||
case JSOP_BINDNAME:
|
||||
return jsop_bindname(info().getName(pc));
|
||||
|
@ -7945,6 +7957,17 @@ NumFixedSlots(JSObject* object)
|
|||
return gc::GetGCKindSlots(kind, object->getClass());
|
||||
}
|
||||
|
||||
static bool
|
||||
IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
|
||||
{
|
||||
ClonedBlockObject &globalLexical = obj->as<ClonedBlockObject>();
|
||||
MOZ_ASSERT(globalLexical.isGlobal());
|
||||
Shape* shape = globalLexical.lookupPure(name);
|
||||
if (!shape)
|
||||
return false;
|
||||
return globalLexical.getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded,
|
||||
MDefinition* lexicalCheck)
|
||||
|
@ -7953,7 +7976,11 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
|
|||
|
||||
jsid id = NameToId(name);
|
||||
|
||||
MOZ_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
|
||||
bool isGlobalLexical = staticObject->is<ClonedBlockObject>() &&
|
||||
staticObject->as<ClonedBlockObject>().isGlobal();
|
||||
MOZ_ASSERT(isGlobalLexical ||
|
||||
staticObject->is<GlobalObject>() ||
|
||||
staticObject->is<CallObject>());
|
||||
MOZ_ASSERT(staticObject->isSingleton());
|
||||
|
||||
*psucceeded = true;
|
||||
|
@ -8000,6 +8027,13 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
|
|||
return true;
|
||||
}
|
||||
|
||||
// Don't optimize global lexical bindings if they aren't initialized at
|
||||
// compile time.
|
||||
if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name)) {
|
||||
*psucceeded = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
TemporaryTypeSet* types = bytecodeTypes(pc);
|
||||
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
|
||||
name, types, /* updateObserved = */ true);
|
||||
|
@ -8055,7 +8089,11 @@ IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
|
|||
{
|
||||
jsid id = NameToId(name);
|
||||
|
||||
MOZ_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
|
||||
bool isGlobalLexical = staticObject->is<ClonedBlockObject>() &&
|
||||
staticObject->as<ClonedBlockObject>().isGlobal();
|
||||
MOZ_ASSERT(isGlobalLexical ||
|
||||
staticObject->is<GlobalObject>() ||
|
||||
staticObject->is<CallObject>());
|
||||
|
||||
MDefinition* value = current->peek(-1);
|
||||
|
||||
|
@ -8077,6 +8115,11 @@ IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
|
|||
if (!CanWriteProperty(alloc(), constraints(), property, value))
|
||||
return jsop_setprop(name);
|
||||
|
||||
// Don't optimize global lexical bindings if they aren't initialized at
|
||||
// compile time.
|
||||
if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
|
||||
return jsop_setprop(name);
|
||||
|
||||
current->pop();
|
||||
|
||||
// Pop the bound object on the stack.
|
||||
|
@ -8098,10 +8141,47 @@ IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
|
|||
value, needsBarrier, slotType);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
IonBuilder::testGlobalLexicalBinding(PropertyName* name)
|
||||
{
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME ||
|
||||
JSOp(*pc) == JSOP_GETGNAME ||
|
||||
JSOp(*pc) == JSOP_SETGNAME ||
|
||||
JSOp(*pc) == JSOP_STRICTSETGNAME);
|
||||
|
||||
// The global isn't the global lexical scope's prototype, but its
|
||||
// enclosing scope. Test for the existence of |name| manually on the
|
||||
// global lexical scope. If it is not found, look for it on the global
|
||||
// itself.
|
||||
|
||||
NativeObject* obj = &script()->global().lexicalScope();
|
||||
TypeSet::ObjectKey* lexicalKey = TypeSet::ObjectKey::get(obj);
|
||||
jsid id = NameToId(name);
|
||||
if (analysisContext)
|
||||
lexicalKey->ensureTrackedProperty(analysisContext, id);
|
||||
if (!lexicalKey->unknownProperties()) {
|
||||
// If the property is not found on the global lexical scope but it is
|
||||
// found on the global and is configurable, freeze the typeset for its
|
||||
// non-existence.
|
||||
//
|
||||
// In the case that it is found on the global but is non-configurable,
|
||||
// the binding cannot be shadowed by a global lexical binding.
|
||||
HeapTypeSetKey lexicalProperty = lexicalKey->property(id);
|
||||
if (!obj->containsPure(name)) {
|
||||
Shape* shape = script()->global().lookupPure(name);
|
||||
if (!shape || !shape->configurable())
|
||||
MOZ_ALWAYS_FALSE(lexicalProperty.isOwnProperty(constraints()));
|
||||
obj = &script()->global();
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_getgname(PropertyName* name)
|
||||
{
|
||||
JSObject* obj = &script()->global();
|
||||
JSObject* obj = testGlobalLexicalBinding(name);
|
||||
bool emitted = false;
|
||||
if (!getStaticName(obj, name, &emitted) || emitted)
|
||||
return emitted;
|
||||
|
@ -8121,7 +8201,7 @@ IonBuilder::jsop_getname(PropertyName* name)
|
|||
{
|
||||
MDefinition* object;
|
||||
if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope()) {
|
||||
MInstruction* global = constant(ObjectValue(script()->global()));
|
||||
MInstruction* global = constant(ObjectValue(script()->global().lexicalScope()));
|
||||
object = global;
|
||||
} else {
|
||||
current->push(current->scopeChain());
|
||||
|
@ -12560,16 +12640,12 @@ IonBuilder::jsop_setarg(uint32_t arg)
|
|||
bool
|
||||
IonBuilder::jsop_defvar(uint32_t index)
|
||||
{
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_DEFVAR || JSOp(*pc) == JSOP_DEFCONST);
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_DEFVAR);
|
||||
|
||||
PropertyName* name = script()->getName(index);
|
||||
|
||||
// Bake in attrs.
|
||||
unsigned attrs = JSPROP_ENUMERATE;
|
||||
if (JSOp(*pc) == JSOP_DEFCONST)
|
||||
attrs |= JSPROP_READONLY;
|
||||
else
|
||||
attrs |= JSPROP_PERMANENT;
|
||||
unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
|
||||
MOZ_ASSERT(!script()->isForEval());
|
||||
|
||||
// Pass the ScopeChain.
|
||||
|
@ -12582,6 +12658,22 @@ IonBuilder::jsop_defvar(uint32_t index)
|
|||
return resumeAfter(defvar);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_deflexical(uint32_t index)
|
||||
{
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_DEFLET || JSOp(*pc) == JSOP_DEFCONST);
|
||||
|
||||
PropertyName* name = script()->getName(index);
|
||||
unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
|
||||
if (JSOp(*pc) == JSOP_DEFCONST)
|
||||
attrs |= JSPROP_READONLY;
|
||||
|
||||
MDefLexical* deflex = MDefLexical::New(alloc(), name, attrs);
|
||||
current->add(deflex);
|
||||
|
||||
return resumeAfter(deflex);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_deffun(uint32_t index)
|
||||
{
|
||||
|
|
|
@ -652,6 +652,7 @@ class IonBuilder
|
|||
bool jsop_tostring();
|
||||
bool jsop_setarg(uint32_t arg);
|
||||
bool jsop_defvar(uint32_t index);
|
||||
bool jsop_deflexical(uint32_t index);
|
||||
bool jsop_deffun(uint32_t index);
|
||||
bool jsop_notearg();
|
||||
bool jsop_checklexical();
|
||||
|
@ -961,6 +962,8 @@ class IonBuilder
|
|||
|
||||
MGetPropertyCache* getInlineableGetPropertyCache(CallInfo& callInfo);
|
||||
|
||||
JSObject* testGlobalLexicalBinding(PropertyName* name);
|
||||
|
||||
JSObject* testSingletonProperty(JSObject* obj, jsid id);
|
||||
JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);
|
||||
|
||||
|
|
|
@ -1092,6 +1092,11 @@ GenerateCallGetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
|||
Label pop1AndFail;
|
||||
Label* maybePopAndFail = failures;
|
||||
|
||||
// If we're calling a getter on the global, inline the logic for the
|
||||
// 'this' hook on the global lexical scope and manually push the global.
|
||||
if (IsGlobalLexicalScope(obj))
|
||||
masm.extractObject(Address(object, ScopeObject::offsetOfEnclosingScope()), object);
|
||||
|
||||
// Save off the object register if it aliases the scratchReg
|
||||
if (spillObjReg) {
|
||||
masm.push(object);
|
||||
|
@ -3332,8 +3337,15 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex
|
|||
}
|
||||
|
||||
// Set/Add the property on the object, the inlined cache are setup for the next execution.
|
||||
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
|
||||
return false;
|
||||
if (JSOp(*cache.pc()) == JSOP_INITGLEXICAL) {
|
||||
RootedScript script(cx);
|
||||
jsbytecode* pc;
|
||||
cache.getScriptedLocation(&script, &pc);
|
||||
InitGlobalLexicalOperation(cx, script, pc, value);
|
||||
} else {
|
||||
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// A GC may have caused cache.value() to become stale as it is not traced.
|
||||
// In this case the IonScript will have been invalidated, so check for that.
|
||||
|
|
|
@ -1000,6 +1000,12 @@ GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes);
|
|||
CalleeToken
|
||||
MarkCalleeToken(JSTracer* trc, CalleeToken token);
|
||||
|
||||
// The minimum stack size is two. Two slots are needed because INITGLEXICAL
|
||||
// (stack depth 1) is compiled as a SETPROP (stack depth 2) on the global
|
||||
// lexical scope. Baseline also requires one slot for this/argument type
|
||||
// checks.
|
||||
static const uint32_t MinJITStackSize = 2;
|
||||
|
||||
} /* namespace jit */
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -153,6 +153,14 @@ LIRGenerator::visitDefVar(MDefVar* ins)
|
|||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitDefLexical(MDefLexical* ins)
|
||||
{
|
||||
LDefLexical* lir = new(alloc()) LDefLexical();
|
||||
add(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitDefFun(MDefFun* ins)
|
||||
{
|
||||
|
|
|
@ -88,6 +88,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitInitPropGetterSetter(MInitPropGetterSetter* ins);
|
||||
void visitCheckOverRecursed(MCheckOverRecursed* ins);
|
||||
void visitDefVar(MDefVar* ins);
|
||||
void visitDefLexical(MDefLexical* ins);
|
||||
void visitDefFun(MDefFun* ins);
|
||||
void visitCreateThisWithTemplate(MCreateThisWithTemplate* ins);
|
||||
void visitCreateThisWithProto(MCreateThisWithProto* ins);
|
||||
|
|
|
@ -7318,6 +7318,33 @@ class MDefVar
|
|||
}
|
||||
};
|
||||
|
||||
class MDefLexical
|
||||
: public MNullaryInstruction
|
||||
{
|
||||
CompilerPropertyName name_; // Target name to be defined.
|
||||
unsigned attrs_; // Attributes to be set.
|
||||
|
||||
private:
|
||||
MDefLexical(PropertyName* name, unsigned attrs)
|
||||
: name_(name),
|
||||
attrs_(attrs)
|
||||
{ }
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(DefLexical)
|
||||
|
||||
static MDefLexical* New(TempAllocator& alloc, PropertyName* name, unsigned attrs) {
|
||||
return new(alloc) MDefLexical(name, attrs);
|
||||
}
|
||||
|
||||
PropertyName* name() const {
|
||||
return name_;
|
||||
}
|
||||
unsigned attrs() const {
|
||||
return attrs_;
|
||||
}
|
||||
};
|
||||
|
||||
class MDefFun
|
||||
: public MUnaryInstruction,
|
||||
public NoTypePolicy::Data
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace jit {
|
|||
_(UnarySharedStub) \
|
||||
_(CheckOverRecursed) \
|
||||
_(DefVar) \
|
||||
_(DefLexical) \
|
||||
_(DefFun) \
|
||||
_(CreateThis) \
|
||||
_(CreateThisWithProto) \
|
||||
|
|
|
@ -1228,6 +1228,16 @@ class LDefVar : public LCallInstructionHelper<0, 1, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LDefLexical : public LCallInstructionHelper<0, 0, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(DefLexical)
|
||||
|
||||
MDefLexical* mir() const {
|
||||
return mir_->toDefLexical();
|
||||
}
|
||||
};
|
||||
|
||||
class LDefFun : public LCallInstructionHelper<0, 1, 0>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
_(InitPropGetterSetter) \
|
||||
_(CheckOverRecursed) \
|
||||
_(DefVar) \
|
||||
_(DefLexical) \
|
||||
_(DefFun) \
|
||||
_(CallKnown) \
|
||||
_(CallGeneric) \
|
||||
|
|
Загрузка…
Ссылка в новой задаче