mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 12:27:29 +09:00
Issue #2142 - Handle fields in derived classes
* Don't name field initializer lambdas Based-on: m-c 1534721, 1551454, 1542448
This commit is contained in:
parent
e6335ded89
commit
235ca77949
13 changed files with 361 additions and 190 deletions
|
|
@ -676,8 +676,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
||||||
if (lazy->hasBeenCloned())
|
if (lazy->hasBeenCloned())
|
||||||
script->setHasBeenCloned();
|
script->setHasBeenCloned();
|
||||||
|
|
||||||
|
FieldInitializers fieldInitializers = FieldInitializers::Invalid();
|
||||||
|
if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
|
||||||
|
fieldInitializers = lazy->getFieldInitializers();
|
||||||
|
}
|
||||||
|
|
||||||
BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->as<FunctionNode>().funbox(), script, lazy,
|
BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->as<FunctionNode>().funbox(), script, lazy,
|
||||||
pn->pn_pos, BytecodeEmitter::LazyFunction);
|
pn->pn_pos, BytecodeEmitter::LazyFunction, fieldInitializers);
|
||||||
if (!bce.init())
|
if (!bce.init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,8 @@ class MOZ_RAII OptionalEmitter
|
||||||
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||||
Parser<FullParseHandler>* parser, SharedContext* sc,
|
Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||||
HandleScript script, Handle<LazyScript*> lazyScript,
|
HandleScript script, Handle<LazyScript*> lazyScript,
|
||||||
uint32_t lineNum, EmitterMode emitterMode)
|
uint32_t lineNum, EmitterMode emitterMode,
|
||||||
|
FieldInitializers fieldInitializers /* = FieldInitializers::Invalid() */)
|
||||||
: sc(sc),
|
: sc(sc),
|
||||||
cx(sc->context),
|
cx(sc->context),
|
||||||
parent(parent),
|
parent(parent),
|
||||||
|
|
@ -166,6 +167,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||||
main(cx, lineNum),
|
main(cx, lineNum),
|
||||||
current(&main),
|
current(&main),
|
||||||
parser(parser),
|
parser(parser),
|
||||||
|
fieldInitializers_(fieldInitializers),
|
||||||
atomIndices(cx->frontendCollectionPool()),
|
atomIndices(cx->frontendCollectionPool()),
|
||||||
firstLine(lineNum),
|
firstLine(lineNum),
|
||||||
maxFixedSlots(0),
|
maxFixedSlots(0),
|
||||||
|
|
@ -178,10 +180,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||||
innermostNestableControl(nullptr),
|
innermostNestableControl(nullptr),
|
||||||
innermostEmitterScope_(nullptr),
|
innermostEmitterScope_(nullptr),
|
||||||
innermostTDZCheckCache(nullptr),
|
innermostTDZCheckCache(nullptr),
|
||||||
fieldInitializers_(parent
|
|
||||||
? parent->fieldInitializers_
|
|
||||||
: lazyScript ? lazyScript->getFieldInitializers()
|
|
||||||
: FieldInitializers::Invalid()),
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
unstableEmitterScope(false),
|
unstableEmitterScope(false),
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -202,10 +200,11 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||||
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||||
Parser<FullParseHandler>* parser, SharedContext* sc,
|
Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||||
HandleScript script, Handle<LazyScript*> lazyScript,
|
HandleScript script, Handle<LazyScript*> lazyScript,
|
||||||
TokenPos bodyPosition, EmitterMode emitterMode)
|
TokenPos bodyPosition, EmitterMode emitterMode,
|
||||||
|
FieldInitializers fieldInitializers)
|
||||||
: BytecodeEmitter(parent, parser, sc, script, lazyScript,
|
: BytecodeEmitter(parent, parser, sc, script, lazyScript,
|
||||||
parser->tokenStream.srcCoords.lineNum(bodyPosition.begin),
|
parser->tokenStream.srcCoords.lineNum(bodyPosition.begin),
|
||||||
emitterMode)
|
emitterMode, fieldInitializers)
|
||||||
{
|
{
|
||||||
setScriptStartOffsetIfUnset(bodyPosition.begin);
|
setScriptStartOffsetIfUnset(bodyPosition.begin);
|
||||||
setFunctionBodyEndPos(bodyPosition.end);
|
setFunctionBodyEndPos(bodyPosition.end);
|
||||||
|
|
@ -2321,6 +2320,10 @@ BytecodeEmitter::emitSetThis(BinaryNode* setThisNode)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!emitInitializeInstanceFields(true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2399,6 +2402,9 @@ BytecodeEmitter::emitFunctionScript(FunctionNode* funNode)
|
||||||
ListNode* paramsBody = &funNode->body()->as<ListNode>();
|
ListNode* paramsBody = &funNode->body()->as<ListNode>();
|
||||||
FunctionBox* funbox = sc->asFunctionBox();
|
FunctionBox* funbox = sc->asFunctionBox();
|
||||||
|
|
||||||
|
MOZ_ASSERT(fieldInitializers_.valid == (funbox->function()->kind() ==
|
||||||
|
JSFunction::FunctionKind::ClassConstructor));
|
||||||
|
|
||||||
setScriptStartOffsetIfUnset(paramsBody->pn_pos.begin);
|
setScriptStartOffsetIfUnset(paramsBody->pn_pos.begin);
|
||||||
|
|
||||||
// [stack]
|
// [stack]
|
||||||
|
|
@ -2433,6 +2439,8 @@ BytecodeEmitter::emitFunctionScript(FunctionNode* funNode)
|
||||||
if (!fse.initScript())
|
if (!fse.initScript())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
script->setFieldInitializers(fieldInitializers_);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5593,11 +5601,14 @@ BytecodeEmitter::emitComprehensionFor(ForNode* forNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_NEVER_INLINE bool
|
MOZ_NEVER_INLINE bool
|
||||||
BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
|
BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto /* = false */,
|
||||||
|
ListNode* classContentsIfConstructor /* = nullptr */)
|
||||||
{
|
{
|
||||||
FunctionBox* funbox = funNode->funbox();
|
FunctionBox* funbox = funNode->funbox();
|
||||||
RootedFunction fun(cx, funbox->function());
|
RootedFunction fun(cx, funbox->function());
|
||||||
|
|
||||||
|
MOZ_ASSERT((classContentsIfConstructor != nullptr) == (funbox->function()->kind() ==
|
||||||
|
JSFunction::FunctionKind::ClassConstructor));
|
||||||
// [stack]
|
// [stack]
|
||||||
|
|
||||||
FunctionEmitter fe(this, funbox, funNode->syntaxKind(),
|
FunctionEmitter fe(this, funbox, funNode->syntaxKind(),
|
||||||
|
|
@ -5627,6 +5638,9 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (classContentsIfConstructor) {
|
||||||
|
fun->lazyScript()->setFieldInitializers(setupFieldInitializers(classContentsIfConstructor));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5651,8 +5665,13 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
|
||||||
if (!script)
|
if (!script)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
FieldInitializers fieldInitializers = FieldInitializers::Invalid();
|
||||||
|
if (classContentsIfConstructor) {
|
||||||
|
fieldInitializers = setupFieldInitializers(classContentsIfConstructor);
|
||||||
|
}
|
||||||
|
|
||||||
BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr,
|
BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr,
|
||||||
funNode->pn_pos, emitterMode);
|
funNode->pn_pos, emitterMode, fieldInitializers);
|
||||||
if (!bce2.init())
|
if (!bce2.init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -5660,6 +5679,8 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
|
||||||
if (!bce2.emitFunctionScript(funNode))
|
if (!bce2.emitFunctionScript(funNode))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// fieldInitializers are copied to the JSScript inside BytecodeEmitter
|
||||||
|
|
||||||
if (funbox->isLikelyConstructorWrapper()) {
|
if (funbox->isLikelyConstructorWrapper()) {
|
||||||
script->setLikelyConstructorWrapper();
|
script->setLikelyConstructorWrapper();
|
||||||
}
|
}
|
||||||
|
|
@ -7932,7 +7953,7 @@ BytecodeEmitter::emitCreateFieldKeys(ListNode* obj)
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj)
|
BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj)
|
||||||
{
|
{
|
||||||
const FieldInitializers& fieldInitializers = fieldInitializers_;
|
FieldInitializers fieldInitializers = setupFieldInitializers(obj);
|
||||||
MOZ_ASSERT(fieldInitializers.valid);
|
MOZ_ASSERT(fieldInitializers.valid);
|
||||||
size_t numFields = fieldInitializers.numFieldInitializers;
|
size_t numFields = fieldInitializers.numFieldInitializers;
|
||||||
|
|
||||||
|
|
@ -7989,6 +8010,132 @@ BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FieldInitializers&
|
||||||
|
BytecodeEmitter::findFieldInitializersForCall()
|
||||||
|
{
|
||||||
|
for (BytecodeEmitter* current = this; current; current = current->parent) {
|
||||||
|
if (current->sc->isFunctionBox()) {
|
||||||
|
FunctionBox* box = current->sc->asFunctionBox();
|
||||||
|
if (box->function()->kind() == JSFunction::FunctionKind::ClassConstructor) {
|
||||||
|
const FieldInitializers& fieldInitializers = current->getFieldInitializers();
|
||||||
|
MOZ_ASSERT(fieldInitializers.valid);
|
||||||
|
return fieldInitializers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ScopeIter si(innermostScope()); si; si++) {
|
||||||
|
if (si.scope()->is<FunctionScope>()) {
|
||||||
|
JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
|
||||||
|
if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
|
||||||
|
const FieldInitializers& fieldInitializers = fun->isInterpretedLazy()
|
||||||
|
? fun->lazyScript()->getFieldInitializers()
|
||||||
|
: fun->nonLazyScript()->getFieldInitializers();
|
||||||
|
MOZ_ASSERT(fieldInitializers.valid);
|
||||||
|
return fieldInitializers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_CRASH("Constructor for field initializers not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BytecodeEmitter::emitCopyInitializersToLocalInitializers()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(sc->asFunctionBox()->isDerivedClassConstructor());
|
||||||
|
if (getFieldInitializers().numFieldInitializers == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
NameOpEmitter noe(this, cx->names().dotLocalInitializers, NameOpEmitter::Kind::Initialize);
|
||||||
|
if (!noe.prepareForRhs()) {
|
||||||
|
// [stack]
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitGetName(cx->names().dotInitializers)) {
|
||||||
|
// [stack] .initializers
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noe.emitAssignment()) {
|
||||||
|
// [stack] .initializers
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emit1(JSOP_POP)) {
|
||||||
|
// [stack]
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BytecodeEmitter::emitInitializeInstanceFields(bool isSuperCall)
|
||||||
|
{
|
||||||
|
const FieldInitializers& fieldInitializers = findFieldInitializersForCall();
|
||||||
|
size_t numFields = fieldInitializers.numFieldInitializers;
|
||||||
|
|
||||||
|
if (numFields == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSuperCall) {
|
||||||
|
if (!emitGetName(cx->names().dotLocalInitializers)) {
|
||||||
|
// [stack] ARRAY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!emitGetName(cx->names().dotInitializers)) {
|
||||||
|
// [stack] ARRAY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
|
||||||
|
if (fieldIndex < numFields - 1) {
|
||||||
|
// We DUP to keep the array around (it is consumed in the bytecode below)
|
||||||
|
// for next iterations of this loop, except for the last iteration, which
|
||||||
|
// avoids an extra POP at the end of the loop.
|
||||||
|
if (!emit1(JSOP_DUP)) {
|
||||||
|
// [stack] ARRAY ARRAY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitNumberOp(fieldIndex)) {
|
||||||
|
// [stack] ARRAY? ARRAY INDEX
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use CALLELEM here, because the receiver of the call != the receiver
|
||||||
|
// of this getelem. (Specifically, the call receiver is `this`, and the
|
||||||
|
// receiver of this getelem is `.initializers`)
|
||||||
|
if (!emit1(JSOP_GETELEM)) {
|
||||||
|
// [stack] ARRAY? FUNC
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is guaranteed to run after super(), so we don't need TDZ checks.
|
||||||
|
if (!emitGetName(cx->names().dotThis)) {
|
||||||
|
// [stack] ARRAY? FUNC THIS
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitCall(JSOP_CALL_IGNORES_RV, 0)) {
|
||||||
|
// [stack] ARRAY? RVAL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emit1(JSOP_POP)) {
|
||||||
|
// [stack] ARRAY?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
|
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
|
||||||
// the comment on emitSwitch.
|
// the comment on emitSwitch.
|
||||||
|
|
@ -8446,20 +8593,6 @@ BytecodeEmitter::emitLexicalInitialization(JSAtom* name)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
class AutoResetFieldInitializers
|
|
||||||
{
|
|
||||||
BytecodeEmitter* bce;
|
|
||||||
FieldInitializers oldFieldInfo;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AutoResetFieldInitializers(BytecodeEmitter* bce, FieldInitializers newFieldInfo)
|
|
||||||
: bce(bce), oldFieldInfo(bce->fieldInitializers_)
|
|
||||||
{
|
|
||||||
bce->fieldInitializers_ = newFieldInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
~AutoResetFieldInitializers() { bce->fieldInitializers_ = oldFieldInfo; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
|
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
|
||||||
// (BindingClassDeclarationEvaluation).
|
// (BindingClassDeclarationEvaluation).
|
||||||
|
|
@ -8485,15 +8618,11 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set this->fieldInitializers_
|
|
||||||
AutoResetFieldInitializers _innermostClassAutoReset(this, setupFieldInitializers(classMembers));
|
|
||||||
|
|
||||||
// [stack]
|
// [stack]
|
||||||
|
|
||||||
ClassEmitter ce(this);
|
ClassEmitter ce(this);
|
||||||
RootedAtom innerName(cx);
|
RootedAtom innerName(cx);
|
||||||
ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
|
ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
|
||||||
|
|
||||||
if (names) {
|
if (names) {
|
||||||
innerName = names->innerBinding()->as<NameNode>().atom();
|
innerName = names->innerBinding()->as<NameNode>().atom();
|
||||||
MOZ_ASSERT(innerName);
|
MOZ_ASSERT(innerName);
|
||||||
|
|
@ -8503,8 +8632,10 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
|
||||||
MOZ_ASSERT(names->outerBinding()->as<NameNode>().atom() == innerName);
|
MOZ_ASSERT(names->outerBinding()->as<NameNode>().atom() == innerName);
|
||||||
kind = ClassEmitter::Kind::Declaration;
|
kind = ClassEmitter::Kind::Declaration;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ce.emitScopeForNamedClass(classNode->scopeBindings())) {
|
if (!classNode->isEmptyScope()) {
|
||||||
|
if (!ce.emitScope(classNode->scopeBindings(), classNode->names() != nullptr)) {
|
||||||
// [stack]
|
// [stack]
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -8534,7 +8665,7 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
|
||||||
if (constructor) {
|
if (constructor) {
|
||||||
bool needsHomeObject = constructor->funbox()->needsHomeObject();
|
bool needsHomeObject = constructor->funbox()->needsHomeObject();
|
||||||
// HERITAGE is consumed inside emitFunction.
|
// HERITAGE is consumed inside emitFunction.
|
||||||
if (!emitFunction(constructor, isDerived)) {
|
if (!emitFunction(constructor, isDerived, classMembers)) {
|
||||||
// [stack] HOMEOBJ CTOR
|
// [stack] HOMEOBJ CTOR
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
|
|
||||||
/* field info for enclosing class */
|
/* field info for enclosing class */
|
||||||
FieldInitializers fieldInitializers_;
|
FieldInitializers fieldInitializers_;
|
||||||
|
const FieldInitializers& getFieldInitializers() { return fieldInitializers_; }
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool unstableEmitterScope;
|
bool unstableEmitterScope;
|
||||||
|
|
@ -251,13 +252,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
*/
|
*/
|
||||||
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||||
HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
|
HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
|
||||||
EmitterMode emitterMode = Normal);
|
EmitterMode emitterMode = Normal,
|
||||||
|
FieldInitializers fieldInitializers = FieldInitializers::Invalid());
|
||||||
|
|
||||||
// An alternate constructor that uses a TokenPos for the starting
|
// An alternate constructor that uses a TokenPos for the starting
|
||||||
// line and that sets functionBodyEndPos as well.
|
// line and that sets functionBodyEndPos as well.
|
||||||
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||||
HandleScript script, Handle<LazyScript*> lazyScript,
|
HandleScript script, Handle<LazyScript*> lazyScript,
|
||||||
TokenPos bodyPosition, EmitterMode emitterMode = Normal);
|
TokenPos bodyPosition, EmitterMode emitterMode = Normal,
|
||||||
|
FieldInitializers fieldInitializers = FieldInitializers::Invalid());
|
||||||
|
|
||||||
MOZ_MUST_USE bool init();
|
MOZ_MUST_USE bool init();
|
||||||
|
|
||||||
|
|
@ -514,7 +517,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
|
MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
|
||||||
MOZ_MUST_USE bool emitRegExp(uint32_t index);
|
MOZ_MUST_USE bool emitRegExp(uint32_t index);
|
||||||
|
|
||||||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(FunctionNode* funNode, bool needsProto = false);
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(FunctionNode* funNode,
|
||||||
|
bool needsProto = false,
|
||||||
|
ListNode* classContentsIfConstructor = nullptr);
|
||||||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ListNode* objNode);
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ListNode* objNode);
|
||||||
|
|
||||||
MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
|
MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
|
||||||
|
|
@ -527,6 +532,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
FieldInitializers setupFieldInitializers(ListNode* classMembers);
|
FieldInitializers setupFieldInitializers(ListNode* classMembers);
|
||||||
MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj);
|
MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj);
|
||||||
MOZ_MUST_USE bool emitCreateFieldInitializers(ListNode* obj);
|
MOZ_MUST_USE bool emitCreateFieldInitializers(ListNode* obj);
|
||||||
|
const FieldInitializers& findFieldInitializersForCall();
|
||||||
|
MOZ_MUST_USE bool emitCopyInitializersToLocalInitializers();
|
||||||
|
MOZ_MUST_USE bool emitInitializeInstanceFields(bool isSuperCall);
|
||||||
|
|
||||||
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
|
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
|
||||||
// not used to unconditionally emit JSOP_GETLOCAL. Variable access should
|
// not used to unconditionally emit JSOP_GETLOCAL. Variable access should
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,9 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
||||||
return literal;
|
return literal;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassNodeType newClass(Node name, Node heritage, Node memberBlock, const TokenPos& pos) {
|
ClassNodeType newClass(Node name, Node heritage, LexicalScopeNodeType memberBlock,
|
||||||
|
const TokenPos& pos)
|
||||||
|
{
|
||||||
return new_<ClassNode>(name, heritage, memberBlock, pos);
|
return new_<ClassNode>(name, heritage, memberBlock, pos);
|
||||||
}
|
}
|
||||||
ListNodeType newClassMemberList(uint32_t begin) {
|
ListNodeType newClassMemberList(uint32_t begin) {
|
||||||
|
|
|
||||||
|
|
@ -477,10 +477,17 @@ bool FunctionScriptEmitter::prepareForBody()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (funbox_->function()->kind() == JSFunction::FunctionKind::ClassConstructor) {
|
if (funbox_->function()->kind() == JSFunction::FunctionKind::ClassConstructor) {
|
||||||
if (!emitInitializeInstanceFields()) {
|
if (funbox_->isDerivedClassConstructor()) {
|
||||||
|
if (!bce_->emitCopyInitializersToLocalInitializers()) {
|
||||||
// [stack]
|
// [stack]
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!bce_->emitInitializeInstanceFields(false)) {
|
||||||
|
// [stack]
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -551,64 +558,6 @@ bool FunctionScriptEmitter::emitExtraBodyVarScope()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionScriptEmitter::emitInitializeInstanceFields()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(bce_->fieldInitializers_.valid);
|
|
||||||
size_t numFields = bce_->fieldInitializers_.numFieldInitializers;
|
|
||||||
|
|
||||||
if (numFields == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bce_->emitGetName(bce_->cx->names().dotInitializers)) {
|
|
||||||
// [stack] ARRAY
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
|
|
||||||
if (fieldIndex < numFields - 1) {
|
|
||||||
// We DUP to keep the array around (it is consumed in the bytecode below)
|
|
||||||
// for next iterations of this loop, except for the last iteration, which
|
|
||||||
// avoids an extra POP at the end of the loop.
|
|
||||||
if (!bce_->emit1(JSOP_DUP)) {
|
|
||||||
// [stack] ARRAY ARRAY
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bce_->emitNumberOp(fieldIndex)) {
|
|
||||||
// [stack] ARRAY? ARRAY INDEX
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't use CALLELEM here, because the receiver of the call != the receiver
|
|
||||||
// of this getelem. (Specifically, the call receiver is `this`, and the
|
|
||||||
// receiver of this getelem is `.initializers`)
|
|
||||||
if (!bce_->emit1(JSOP_GETELEM)) {
|
|
||||||
// [stack] ARRAY? FUNC
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is guaranteed to run after super(), so we don't need TDZ checks.
|
|
||||||
if (!bce_->emitGetName(bce_->cx->names().dotThis)) {
|
|
||||||
// [stack] ARRAY? FUNC THIS
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bce_->emitCall(JSOP_CALL_IGNORES_RV, 0)) {
|
|
||||||
// [stack] ARRAY? RVAL
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bce_->emit1(JSOP_POP)) {
|
|
||||||
// [stack] ARRAY?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FunctionScriptEmitter::emitEndBody()
|
bool FunctionScriptEmitter::emitEndBody()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(state_ == State::Body);
|
MOZ_ASSERT(state_ == State::Body);
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,6 @@ class MOZ_STACK_CLASS FunctionScriptEmitter {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MOZ_MUST_USE bool emitExtraBodyVarScope();
|
MOZ_MUST_USE bool emitExtraBodyVarScope();
|
||||||
MOZ_MUST_USE bool emitInitializeInstanceFields();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class for emitting function parameters.
|
// Class for emitting function parameters.
|
||||||
|
|
|
||||||
|
|
@ -531,14 +531,16 @@ ClassEmitter::ClassEmitter(BytecodeEmitter* bce)
|
||||||
isClass_ = true;
|
isClass_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClassEmitter::emitScopeForNamedClass(JS::Handle<LexicalScope::Data*> scopeBindings)
|
bool ClassEmitter::emitScope(JS::Handle<LexicalScope::Data*> scopeBindings, bool hasName)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||||
MOZ_ASSERT(classState_ == ClassState::Start);
|
MOZ_ASSERT(classState_ == ClassState::Start);
|
||||||
|
|
||||||
|
if (hasName)
|
||||||
tdzCacheForInnerName_.emplace(bce_);
|
tdzCacheForInnerName_.emplace(bce_);
|
||||||
innerNameScope_.emplace(bce_);
|
|
||||||
if (!innerNameScope_->enterLexical(bce_, ScopeKind::Lexical, scopeBindings))
|
innerScope_.emplace(bce_);
|
||||||
|
if (!innerScope_->enterLexical(bce_, ScopeKind::Lexical, scopeBindings))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -716,7 +718,7 @@ bool ClassEmitter::emitEnd(Kind kind)
|
||||||
|
|
||||||
if (name_ != bce_->cx->names().empty) {
|
if (name_ != bce_->cx->names().empty) {
|
||||||
MOZ_ASSERT(tdzCacheForInnerName_.isSome());
|
MOZ_ASSERT(tdzCacheForInnerName_.isSome());
|
||||||
MOZ_ASSERT(innerNameScope_.isSome());
|
MOZ_ASSERT(innerScope_.isSome());
|
||||||
|
|
||||||
if (!bce_->emitLexicalInitialization(name_)) {
|
if (!bce_->emitLexicalInitialization(name_)) {
|
||||||
// [stack] CTOR
|
// [stack] CTOR
|
||||||
|
|
@ -724,9 +726,9 @@ bool ClassEmitter::emitEnd(Kind kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop the inner scope.
|
// Pop the inner scope.
|
||||||
if (!innerNameScope_->leave(bce_))
|
if (!innerScope_->leave(bce_))
|
||||||
return false;
|
return false;
|
||||||
innerNameScope_.reset();
|
innerScope_.reset();
|
||||||
|
|
||||||
if (kind == Kind::Declaration) {
|
if (kind == Kind::Declaration) {
|
||||||
if (!bce_->emitLexicalInitialization(name_)) {
|
if (!bce_->emitLexicalInitialization(name_)) {
|
||||||
|
|
@ -742,9 +744,18 @@ bool ClassEmitter::emitEnd(Kind kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
tdzCacheForInnerName_.reset();
|
tdzCacheForInnerName_.reset();
|
||||||
|
} else if (innerScope_.isSome()) {
|
||||||
|
// [stack] CTOR
|
||||||
|
MOZ_ASSERT(kind == Kind::Expression);
|
||||||
|
MOZ_ASSERT(tdzCacheForInnerName_.isNothing());
|
||||||
|
|
||||||
|
if (!innerScope_->leave(bce_))
|
||||||
|
return false;
|
||||||
|
innerScope_.reset();
|
||||||
}else {
|
}else {
|
||||||
// [stack] CTOR
|
// [stack] CTOR
|
||||||
|
|
||||||
|
MOZ_ASSERT(kind == Kind::Expression);
|
||||||
MOZ_ASSERT(tdzCacheForInnerName_.isNothing());
|
MOZ_ASSERT(tdzCacheForInnerName_.isNothing());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -464,6 +464,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
|
||||||
//
|
//
|
||||||
// `class {}`
|
// `class {}`
|
||||||
// ClassEmitter ce(this);
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitScope(scopeBindings, false);
|
||||||
// ce.emitClass();
|
// ce.emitClass();
|
||||||
//
|
//
|
||||||
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
||||||
|
|
@ -473,6 +474,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
|
||||||
//
|
//
|
||||||
// `class { constructor() { ... } }`
|
// `class { constructor() { ... } }`
|
||||||
// ClassEmitter ce(this);
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitScope(scopeBindings, false);
|
||||||
// ce.emitClass();
|
// ce.emitClass();
|
||||||
//
|
//
|
||||||
// emit(function_for_constructor);
|
// emit(function_for_constructor);
|
||||||
|
|
@ -482,7 +484,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
|
||||||
//
|
//
|
||||||
// `class X { constructor() { ... } }`
|
// `class X { constructor() { ... } }`
|
||||||
// ClassEmitter ce(this);
|
// ClassEmitter ce(this);
|
||||||
// ce.emitScopeForNamedClass(scopeBindingForName);
|
// ce.emitScope(scopeBindings, true);
|
||||||
// ce.emitClass(atom_of_X);
|
// ce.emitClass(atom_of_X);
|
||||||
//
|
//
|
||||||
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
||||||
|
|
@ -492,7 +494,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
|
||||||
//
|
//
|
||||||
// `class X { constructor() { ... } }`
|
// `class X { constructor() { ... } }`
|
||||||
// ClassEmitter ce(this);
|
// ClassEmitter ce(this);
|
||||||
// ce.emitScopeForNamedClass(scopeBindingForName);
|
// ce.emitScope(scopeBindings, true);
|
||||||
// ce.emitClass(atom_of_X);
|
// ce.emitClass(atom_of_X);
|
||||||
//
|
//
|
||||||
// emit(function_for_constructor);
|
// emit(function_for_constructor);
|
||||||
|
|
@ -502,7 +504,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
|
||||||
//
|
//
|
||||||
// `class X extends Y { constructor() { ... } }`
|
// `class X extends Y { constructor() { ... } }`
|
||||||
// ClassEmitter ce(this);
|
// ClassEmitter ce(this);
|
||||||
// ce.emitScopeForNamedClass(scopeBindingForName);
|
// ce.emitScope(scopeBindings, true);
|
||||||
//
|
//
|
||||||
// emit(Y);
|
// emit(Y);
|
||||||
// ce.emitDerivedClass(atom_of_X);
|
// ce.emitDerivedClass(atom_of_X);
|
||||||
|
|
@ -514,7 +516,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
|
||||||
//
|
//
|
||||||
// `class X extends Y { constructor() { ... super.f(); ... } }`
|
// `class X extends Y { constructor() { ... super.f(); ... } }`
|
||||||
// ClassEmitter ce(this);
|
// ClassEmitter ce(this);
|
||||||
// ce.emitScopeForNamedClass(scopeBindingForName);
|
// ce.emitScope(scopeBindings, true);
|
||||||
//
|
//
|
||||||
// emit(Y);
|
// emit(Y);
|
||||||
// ce.emitDerivedClass(atom_of_X);
|
// ce.emitDerivedClass(atom_of_X);
|
||||||
|
|
@ -629,21 +631,21 @@ class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter
|
||||||
bool isDerived_ = false;
|
bool isDerived_ = false;
|
||||||
|
|
||||||
mozilla::Maybe<TDZCheckCache> tdzCacheForInnerName_;
|
mozilla::Maybe<TDZCheckCache> tdzCacheForInnerName_;
|
||||||
mozilla::Maybe<EmitterScope> innerNameScope_;
|
mozilla::Maybe<EmitterScope> innerScope_;
|
||||||
AutoSaveLocalStrictMode strictMode_;
|
AutoSaveLocalStrictMode strictMode_;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// The state of this emitter.
|
// The state of this emitter.
|
||||||
//
|
//
|
||||||
// +-------+
|
// +-------+
|
||||||
// | Start |-+------------------------------------>+-+
|
// | Start |-+------------------------>+-+
|
||||||
// +-------+ | ^ |
|
// +-------+ | ^ |
|
||||||
// | [named class] | |
|
// | [has scope] | |
|
||||||
// | emitScopeForNamedClass +-------+ | |
|
// | emitScope +-------+ | |
|
||||||
// +-------------------------->| Scope |-+ |
|
// +-------------->| Scope |-+ |
|
||||||
// +-------+ |
|
// +-------+ |
|
||||||
// |
|
// |
|
||||||
// +-----------------------------------------------+
|
// +-----------------------------------+
|
||||||
// |
|
// |
|
||||||
// | emitClass +-------+
|
// | emitClass +-------+
|
||||||
// +-+----------------->+->| Class |-+
|
// +-+----------------->+->| Class |-+
|
||||||
|
|
@ -669,7 +671,7 @@ class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter
|
||||||
// The initial state.
|
// The initial state.
|
||||||
Start,
|
Start,
|
||||||
|
|
||||||
// After calling emitScopeForNamedClass.
|
// After calling emitScope.
|
||||||
Scope,
|
Scope,
|
||||||
|
|
||||||
// After calling emitClass or emitDerivedClass.
|
// After calling emitClass or emitDerivedClass.
|
||||||
|
|
@ -689,8 +691,7 @@ class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter
|
||||||
public:
|
public:
|
||||||
explicit ClassEmitter(BytecodeEmitter* bce);
|
explicit ClassEmitter(BytecodeEmitter* bce);
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitScopeForNamedClass(
|
MOZ_MUST_USE bool emitScope(JS::Handle<LexicalScope::Data*> scopeBindings, bool hasName);
|
||||||
JS::Handle<LexicalScope::Data*> scopeBindings);
|
|
||||||
|
|
||||||
// @param name
|
// @param name
|
||||||
// Name of the class (nullptr if this is anonymous class)
|
// Name of the class (nullptr if this is anonymous class)
|
||||||
|
|
|
||||||
|
|
@ -252,10 +252,7 @@ IsTypeofKind(ParseNodeKind kind)
|
||||||
* PNK_CLASS (ClassNode)
|
* PNK_CLASS (ClassNode)
|
||||||
* kid1: PNK_CLASSNAMES for class name. can be null for anonymous class.
|
* kid1: PNK_CLASSNAMES for class name. can be null for anonymous class.
|
||||||
* kid2: expression after `extends`. null if no expression
|
* kid2: expression after `extends`. null if no expression
|
||||||
* kid3: either of
|
* kid3: PNK_LEXICALSCOPE which contains PNK_CLASSMEMBERLIST as scopeBody
|
||||||
* * PNK_CLASSMEMBERLIST, if anonymous class
|
|
||||||
* * PNK_LEXICALSCOPE which contains PNK_CLASSMEMBERLIST as scopeBody,
|
|
||||||
* if named class
|
|
||||||
* PNK_CLASSNAMES (ClassNames)
|
* PNK_CLASSNAMES (ClassNames)
|
||||||
* left: Name node for outer binding, or null if the class is an expression
|
* left: Name node for outer binding, or null if the class is an expression
|
||||||
* that doesn't create an outer binding
|
* that doesn't create an outer binding
|
||||||
|
|
@ -2243,13 +2240,11 @@ class ClassNames : public BinaryNode
|
||||||
class ClassNode : public TernaryNode
|
class ClassNode : public TernaryNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* membersOrBlock,
|
ClassNode(ParseNode* names, ParseNode* heritage, LexicalScopeNode* memberBlock,
|
||||||
const TokenPos& pos)
|
const TokenPos& pos)
|
||||||
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, membersOrBlock, pos)
|
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, memberBlock, pos)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(names, names->is<ClassNames>());
|
MOZ_ASSERT_IF(names, names->is<ClassNames>());
|
||||||
MOZ_ASSERT(membersOrBlock->is<LexicalScopeNode>() ||
|
|
||||||
membersOrBlock->isKind(PNK_CLASSMEMBERLIST));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool test(const ParseNode& node) {
|
static bool test(const ParseNode& node) {
|
||||||
|
|
@ -2265,14 +2260,14 @@ class ClassNode : public TernaryNode
|
||||||
return kid2();
|
return kid2();
|
||||||
}
|
}
|
||||||
ListNode* memberList() const {
|
ListNode* memberList() const {
|
||||||
ParseNode* membersOrBlock = kid3();
|
ListNode* list = &kid3()->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
|
||||||
if (membersOrBlock->isKind(PNK_CLASSMEMBERLIST))
|
|
||||||
return &membersOrBlock->as<ListNode>();
|
|
||||||
|
|
||||||
ListNode* list = &membersOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
|
|
||||||
MOZ_ASSERT(list->isKind(PNK_CLASSMEMBERLIST));
|
MOZ_ASSERT(list->isKind(PNK_CLASSMEMBERLIST));
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
bool isEmptyScope() const {
|
||||||
|
ParseNode* scope = kid3();
|
||||||
|
return scope->as<LexicalScopeNode>().isEmptyScope();
|
||||||
|
}
|
||||||
Handle<LexicalScope::Data*> scopeBindings() const {
|
Handle<LexicalScope::Data*> scopeBindings() const {
|
||||||
ParseNode* scope = kid3();
|
ParseNode* scope = kid3();
|
||||||
return scope->as<LexicalScopeNode>().scopeBindings();
|
return scope->as<LexicalScopeNode>().scopeBindings();
|
||||||
|
|
|
||||||
|
|
@ -2779,6 +2779,12 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
|
||||||
|
if (!noteDeclaredName(context->names().dotLocalInitializers,
|
||||||
|
DeclarationKind::Var, pos()))
|
||||||
|
return null();
|
||||||
|
}
|
||||||
|
|
||||||
return finishLexicalScope(pc->varScope(), body);
|
return finishLexicalScope(pc->varScope(), body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3600,7 +3606,11 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
|
||||||
|
|
||||||
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
|
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
|
||||||
if (fun->isClassConstructor()) {
|
if (fun->isClassConstructor()) {
|
||||||
|
if (fun->isDerivedClassConstructor()) {
|
||||||
|
syntaxKind = FunctionSyntaxKind::DerivedClassConstructor;
|
||||||
|
} else {
|
||||||
syntaxKind = FunctionSyntaxKind::ClassConstructor;
|
syntaxKind = FunctionSyntaxKind::ClassConstructor;
|
||||||
|
}
|
||||||
} else if (fun->isMethod()) {
|
} else if (fun->isMethod()) {
|
||||||
syntaxKind = FunctionSyntaxKind::Method;
|
syntaxKind = FunctionSyntaxKind::Method;
|
||||||
} else if (fun->isGetter()) {
|
} else if (fun->isGetter()) {
|
||||||
|
|
@ -7468,7 +7478,7 @@ Parser<ParseHandler>::classMember(YieldHandling yieldHandling, DefaultHandling d
|
||||||
|
|
||||||
numFields++;
|
numFields++;
|
||||||
|
|
||||||
FunctionNodeType initializer = fieldInitializerOpt(yieldHandling, hasHeritage, propName,
|
FunctionNodeType initializer = fieldInitializerOpt(yieldHandling, hasHeritage,
|
||||||
propAtom, numFieldKeys);
|
propAtom, numFieldKeys);
|
||||||
if (!initializer)
|
if (!initializer)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -7553,8 +7563,9 @@ Parser<ParseHandler>::classMember(YieldHandling yieldHandling, DefaultHandling d
|
||||||
template <typename ParseHandler>
|
template <typename ParseHandler>
|
||||||
bool
|
bool
|
||||||
Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement& classStmt,
|
Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement& classStmt,
|
||||||
HandlePropertyName className, uint32_t classStartOffset,
|
HandlePropertyName className, bool hasHeritage,
|
||||||
uint32_t classEndOffset, size_t numFields,
|
uint32_t classStartOffset, uint32_t classEndOffset,
|
||||||
|
size_t numFields,
|
||||||
ListNodeType& classMembers)
|
ListNodeType& classMembers)
|
||||||
{
|
{
|
||||||
// Fields cannot re-use the constructor obtained via JSOP_CLASSCONSTRUCTOR or
|
// Fields cannot re-use the constructor obtained via JSOP_CLASSCONSTRUCTOR or
|
||||||
|
|
@ -7562,7 +7573,7 @@ Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement&
|
||||||
// initializers in the constructor. So, synthesize a new one.
|
// initializers in the constructor. So, synthesize a new one.
|
||||||
if (classStmt.constructorBox == nullptr && numFields > 0) {
|
if (classStmt.constructorBox == nullptr && numFields > 0) {
|
||||||
// synthesizeConstructor assigns to classStmt.constructorBox
|
// synthesizeConstructor assigns to classStmt.constructorBox
|
||||||
FunctionNodeType synthesizedCtor = synthesizeConstructor(className, classStartOffset);
|
FunctionNodeType synthesizedCtor = synthesizeConstructor(className, classStartOffset, hasHeritage);
|
||||||
if (!synthesizedCtor) {
|
if (!synthesizedCtor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -7600,12 +7611,6 @@ Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement&
|
||||||
if (numFields > 0) {
|
if (numFields > 0) {
|
||||||
ctorbox->function()->lazyScript()->setHasThisBinding();
|
ctorbox->function()->lazyScript()->setHasThisBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field initializers can be retrieved if the class and constructor are
|
|
||||||
// being compiled at the same time, but we need to stash the field
|
|
||||||
// information if the constructor is being compiled lazily.
|
|
||||||
FieldInitializers fieldInfo(numFields);
|
|
||||||
ctorbox->function()->lazyScript()->setFieldInitializers(fieldInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7724,7 +7729,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||||
pc->innermostScope()->id()))
|
pc->innermostScope()->id()))
|
||||||
return null();
|
return null();
|
||||||
if (!noteDeclaredName(context->names().dotInitializers,
|
if (!noteDeclaredName(context->names().dotInitializers,
|
||||||
DeclarationKind::Var, namePos))
|
DeclarationKind::Let, namePos))
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7733,8 +7738,8 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
classEndOffset = pos().end;
|
classEndOffset = pos().end;
|
||||||
if (!finishClassConstructor(classStmt, className, classStartOffset,
|
if (!finishClassConstructor(classStmt, className, hasHeritage,
|
||||||
classEndOffset, numFields, classMembers))
|
classStartOffset, classEndOffset, numFields, classMembers))
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
if (className) {
|
if (className) {
|
||||||
|
|
@ -7779,9 +7784,10 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||||
|
|
||||||
template <class ParseHandler>
|
template <class ParseHandler>
|
||||||
typename ParseHandler::FunctionNodeType
|
typename ParseHandler::FunctionNodeType
|
||||||
Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t classNameOffset)
|
Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t classNameOffset, bool hasHeritage)
|
||||||
{
|
{
|
||||||
FunctionSyntaxKind functionSyntaxKind = FunctionSyntaxKind::ClassConstructor;
|
FunctionSyntaxKind functionSyntaxKind = hasHeritage ? FunctionSyntaxKind::DerivedClassConstructor
|
||||||
|
: FunctionSyntaxKind::ClassConstructor;
|
||||||
|
|
||||||
// Create the function object.
|
// Create the function object.
|
||||||
RootedFunction fun(context, newFunction(className, functionSyntaxKind,
|
RootedFunction fun(context, newFunction(className, functionSyntaxKind,
|
||||||
|
|
@ -7820,6 +7826,8 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class
|
||||||
funbox->function()->setArgCount(0);
|
funbox->function()->setArgCount(0);
|
||||||
funbox->setStart(tokenStream);
|
funbox->setStart(tokenStream);
|
||||||
|
|
||||||
|
pc->functionScope().useAsVarScope(pc);
|
||||||
|
|
||||||
// Push a LexicalScope on to the stack.
|
// Push a LexicalScope on to the stack.
|
||||||
ParseContext::Scope lexicalScope(this);
|
ParseContext::Scope lexicalScope(this);
|
||||||
if (!lexicalScope.init(pc))
|
if (!lexicalScope.init(pc))
|
||||||
|
|
@ -7835,10 +7843,55 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class
|
||||||
// One might expect a noteUsedName(".initializers") here. See comment in
|
// One might expect a noteUsedName(".initializers") here. See comment in
|
||||||
// GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
|
// GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
|
||||||
|
|
||||||
|
if (hasHeritage) {
|
||||||
|
if (!noteDeclaredName(context->names().dotLocalInitializers,
|
||||||
|
DeclarationKind::Var, synthesizedBodyPos))
|
||||||
|
return null();
|
||||||
|
}
|
||||||
|
|
||||||
bool canSkipLazyClosedOverBindings = handler.canSkipLazyClosedOverBindings();
|
bool canSkipLazyClosedOverBindings = handler.canSkipLazyClosedOverBindings();
|
||||||
if (!declareFunctionThis(canSkipLazyClosedOverBindings))
|
if (!declareFunctionThis(canSkipLazyClosedOverBindings))
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
|
if (hasHeritage) {
|
||||||
|
// {Goanna} Need a different this-NameNode for SuperBase and SetThis or the recycling
|
||||||
|
// by ParseNodeAllocator runs into all sorts of problems because the
|
||||||
|
// same ParseNode gets cleaned up twice.
|
||||||
|
// Parser<ParseHandler>::memberExpr does the same.
|
||||||
|
NameNodeType thisNameBase = newThisName();
|
||||||
|
if (!thisNameBase)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
UnaryNodeType superBase = handler.newSuperBase(thisNameBase, synthesizedBodyPos);
|
||||||
|
if (!superBase)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
ListNodeType arguments = handler.newArguments(synthesizedBodyPos);
|
||||||
|
if (!arguments)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
BinaryNodeType superCall = handler.newSuperCall(superBase, arguments, false);
|
||||||
|
if (!superCall)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
NameNodeType thisName = newThisName();
|
||||||
|
if (!thisName)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
BinaryNodeType setThis = handler.newSetThis(thisName, superCall);
|
||||||
|
if (!setThis)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
if (!noteUsedName(context->names().dotLocalInitializers))
|
||||||
|
return null();
|
||||||
|
|
||||||
|
UnaryNodeType exprStatement = handler.newExprStatement(setThis, synthesizedBodyPos.end);
|
||||||
|
if (!exprStatement)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
handler.addStatementToList(stmtList, exprStatement);
|
||||||
|
}
|
||||||
|
|
||||||
auto initializerBody = finishLexicalScope(lexicalScope, stmtList);
|
auto initializerBody = finishLexicalScope(lexicalScope, stmtList);
|
||||||
if (!initializerBody)
|
if (!initializerBody)
|
||||||
return null();
|
return null();
|
||||||
|
|
@ -7860,7 +7913,7 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class
|
||||||
template <class ParseHandler>
|
template <class ParseHandler>
|
||||||
typename ParseHandler::FunctionNodeType
|
typename ParseHandler::FunctionNodeType
|
||||||
Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
|
Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
|
||||||
Node propName, HandleAtom propAtom, size_t& numFieldKeys)
|
HandleAtom propAtom, size_t& numFieldKeys)
|
||||||
{
|
{
|
||||||
bool hasInitializer = false;
|
bool hasInitializer = false;
|
||||||
if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
|
if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
|
||||||
|
|
@ -7878,9 +7931,9 @@ Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasH
|
||||||
firstTokenPos = TokenPos(endPos, endPos);
|
firstTokenPos = TokenPos(endPos, endPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the function object.
|
// Create the anonymous function object.
|
||||||
RootedFunction fun(context,
|
RootedFunction fun(context,
|
||||||
newFunction(propAtom, FunctionSyntaxKind::Expression,
|
newFunction(nullptr, FunctionSyntaxKind::Expression,
|
||||||
GeneratorKind::NotGenerator,
|
GeneratorKind::NotGenerator,
|
||||||
FunctionAsyncKind::SyncFunction));
|
FunctionAsyncKind::SyncFunction));
|
||||||
if (!fun)
|
if (!fun)
|
||||||
|
|
@ -7981,7 +8034,12 @@ Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasH
|
||||||
if (!propAssignFieldAccess)
|
if (!propAssignFieldAccess)
|
||||||
return null();
|
return null();
|
||||||
} else if (propAtom->isIndex(&indexValue)) {
|
} else if (propAtom->isIndex(&indexValue)) {
|
||||||
propAssignFieldAccess = handler.newPropertyByValue(propAssignThis, propName, wholeInitializerPos.end);
|
// {Goanna} Can't reuse propName here, see comment in synthesizeConstructor
|
||||||
|
Node indexNode = handler.newNumber(indexValue, DecimalPoint::NoDecimal, wholeInitializerPos);
|
||||||
|
if (!indexNode)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
propAssignFieldAccess = handler.newPropertyByValue(propAssignThis, indexNode, wholeInitializerPos.end);
|
||||||
if (!propAssignFieldAccess)
|
if (!propAssignFieldAccess)
|
||||||
return null();
|
return null();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -9908,6 +9966,9 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||||
nextMember = handler.newSetThis(thisName, nextMember);
|
nextMember = handler.newSetThis(thisName, nextMember);
|
||||||
if (!nextMember)
|
if (!nextMember)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
|
if (!noteUsedName(context->names().dotLocalInitializers))
|
||||||
|
return null();
|
||||||
} else {
|
} else {
|
||||||
nextMember = memberCall(tt, lhs, yieldHandling, possibleError);
|
nextMember = memberCall(tt, lhs, yieldHandling, possibleError);
|
||||||
if (!nextMember)
|
if (!nextMember)
|
||||||
|
|
|
||||||
|
|
@ -1498,14 +1498,15 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
||||||
ListNodeType& classMembers, bool* done);
|
ListNodeType& classMembers, bool* done);
|
||||||
MOZ_MUST_USE bool finishClassConstructor(
|
MOZ_MUST_USE bool finishClassConstructor(
|
||||||
const ParseContext::ClassStatement& classStmt,
|
const ParseContext::ClassStatement& classStmt,
|
||||||
HandlePropertyName className, uint32_t classStartOffset,
|
HandlePropertyName className, bool hasHeritage,
|
||||||
uint32_t classEndOffset, size_t numFieldsWithInitializers,
|
uint32_t classStartOffset, uint32_t classEndOffset,
|
||||||
ListNodeType& classMembers);
|
size_t numFieldsWithInitializers, ListNodeType& classMembers);
|
||||||
|
|
||||||
FunctionNodeType fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
|
FunctionNodeType fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
|
||||||
Node name, HandleAtom atom, size_t& numFieldKeys);
|
HandleAtom atom, size_t& numFieldKeys);
|
||||||
FunctionNodeType synthesizeConstructor(HandleAtom className,
|
FunctionNodeType synthesizeConstructor(HandleAtom className,
|
||||||
uint32_t classNameOffset);
|
uint32_t classNameOffset,
|
||||||
|
bool hasHeritage);
|
||||||
|
|
||||||
bool checkLabelOrIdentifierReference(PropertyName* ident,
|
bool checkLabelOrIdentifierReference(PropertyName* ident,
|
||||||
uint32_t offset,
|
uint32_t offset,
|
||||||
|
|
|
||||||
|
|
@ -704,6 +704,35 @@ class ScriptSourceObject : public NativeObject
|
||||||
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
|
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
|
||||||
enum FunctionAsyncKind { SyncFunction, AsyncFunction };
|
enum FunctionAsyncKind { SyncFunction, AsyncFunction };
|
||||||
|
|
||||||
|
struct FieldInitializers
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
bool valid;
|
||||||
|
#endif
|
||||||
|
// This struct will eventually have a vector of constant values for optimizing
|
||||||
|
// field initializers.
|
||||||
|
size_t numFieldInitializers;
|
||||||
|
|
||||||
|
explicit FieldInitializers(size_t numFieldInitializers)
|
||||||
|
:
|
||||||
|
#ifdef DEBUG
|
||||||
|
valid(true),
|
||||||
|
#endif
|
||||||
|
numFieldInitializers(numFieldInitializers) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static FieldInitializers Invalid() { return FieldInitializers(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FieldInitializers()
|
||||||
|
:
|
||||||
|
#ifdef DEBUG
|
||||||
|
valid(false),
|
||||||
|
#endif
|
||||||
|
numFieldInitializers(0) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static inline unsigned
|
static inline unsigned
|
||||||
GeneratorKindAsBits(GeneratorKind generatorKind) {
|
GeneratorKindAsBits(GeneratorKind generatorKind) {
|
||||||
return static_cast<unsigned>(generatorKind);
|
return static_cast<unsigned>(generatorKind);
|
||||||
|
|
@ -855,6 +884,8 @@ class JSScript : public js::gc::TenuredCell
|
||||||
|
|
||||||
private:
|
private:
|
||||||
js::SharedScriptData* scriptData_;
|
js::SharedScriptData* scriptData_;
|
||||||
|
|
||||||
|
js::FieldInitializers fieldInitializers_ = js::FieldInitializers::Invalid();
|
||||||
public:
|
public:
|
||||||
uint8_t* data; /* pointer to variable-length data array (see
|
uint8_t* data; /* pointer to variable-length data array (see
|
||||||
comment above Create() for details) */
|
comment above Create() for details) */
|
||||||
|
|
@ -1454,6 +1485,11 @@ class JSScript : public js::gc::TenuredCell
|
||||||
return functionHasThisBinding_;
|
return functionHasThisBinding_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setFieldInitializers(js::FieldInitializers fieldInitializers) {
|
||||||
|
fieldInitializers_ = fieldInitializers;
|
||||||
|
}
|
||||||
|
const js::FieldInitializers& getFieldInitializers() const { return fieldInitializers_; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arguments access (via JSOP_*ARG* opcodes) must access the canonical
|
* Arguments access (via JSOP_*ARG* opcodes) must access the canonical
|
||||||
* location for the argument. If an arguments object exists AND it's mapped
|
* location for the argument. If an arguments object exists AND it's mapped
|
||||||
|
|
@ -1999,35 +2035,6 @@ static_assert(sizeof(JSScript) % js::gc::CellSize == 0,
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
struct FieldInitializers
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
bool valid;
|
|
||||||
#endif
|
|
||||||
// This struct will eventually have a vector of constant values for optimizing
|
|
||||||
// field initializers.
|
|
||||||
size_t numFieldInitializers;
|
|
||||||
|
|
||||||
explicit FieldInitializers(size_t numFieldInitializers)
|
|
||||||
:
|
|
||||||
#ifdef DEBUG
|
|
||||||
valid(true),
|
|
||||||
#endif
|
|
||||||
numFieldInitializers(numFieldInitializers) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static FieldInitializers Invalid() { return FieldInitializers(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
FieldInitializers()
|
|
||||||
:
|
|
||||||
#ifdef DEBUG
|
|
||||||
valid(false),
|
|
||||||
#endif
|
|
||||||
numFieldInitializers(0) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Information about a script which may be (or has been) lazily compiled to
|
// Information about a script which may be (or has been) lazily compiled to
|
||||||
// bytecode from its source.
|
// bytecode from its source.
|
||||||
class LazyScript : public gc::TenuredCell
|
class LazyScript : public gc::TenuredCell
|
||||||
|
|
@ -2330,7 +2337,7 @@ class LazyScript : public gc::TenuredCell
|
||||||
fieldInitializers_ = fieldInitializers;
|
fieldInitializers_ = fieldInitializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldInitializers getFieldInitializers() const { return fieldInitializers_; }
|
const FieldInitializers& getFieldInitializers() const { return fieldInitializers_; }
|
||||||
|
|
||||||
const char* filename() const {
|
const char* filename() const {
|
||||||
return scriptSource()->filename();
|
return scriptSource()->filename();
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@
|
||||||
macro(dotGenerator, dotGenerator, ".generator") \
|
macro(dotGenerator, dotGenerator, ".generator") \
|
||||||
macro(dotThis, dotThis, ".this") \
|
macro(dotThis, dotThis, ".this") \
|
||||||
macro(dotInitializers, dotInitializers, ".initializers") \
|
macro(dotInitializers, dotInitializers, ".initializers") \
|
||||||
|
macro(dotLocalInitializers, dotLocalInitializers, ".localInitializers") \
|
||||||
macro(dotFieldKeys, dotFieldKeys, ".fieldKeys") \
|
macro(dotFieldKeys, dotFieldKeys, ".fieldKeys") \
|
||||||
macro(each, each, "each") \
|
macro(each, each, "each") \
|
||||||
macro(elementType, elementType, "elementType") \
|
macro(elementType, elementType, "elementType") \
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue