mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 16:58:38 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
a248550543
45 changed files with 6846 additions and 1402 deletions
|
|
@ -53,6 +53,8 @@ enum AssignmentOperator {
|
|||
AOP_LSH, AOP_RSH, AOP_URSH,
|
||||
/* binary */
|
||||
AOP_BITOR, AOP_BITXOR, AOP_BITAND,
|
||||
/* short-circuit */
|
||||
AOP_COALESCE, AOP_OR, AOP_AND,
|
||||
|
||||
AOP_LIMIT
|
||||
};
|
||||
|
|
@ -122,6 +124,9 @@ static const char* const aopNames[] = {
|
|||
"|=", /* AOP_BITOR */
|
||||
"^=", /* AOP_BITXOR */
|
||||
"&=" /* AOP_BITAND */
|
||||
"\?\?=", /* AOP_COALESCE */
|
||||
"||=", /* AOP_OR */
|
||||
"&&=", /* AOP_AND */
|
||||
};
|
||||
|
||||
static const char* const binopNames[] = {
|
||||
|
|
@ -539,9 +544,12 @@ class NodeBuilder
|
|||
|
||||
MOZ_MUST_USE bool classDefinition(bool expr, HandleValue name, HandleValue heritage,
|
||||
HandleValue block, TokenPos* pos, MutableHandleValue dst);
|
||||
MOZ_MUST_USE bool classMethods(NodeVector& methods, MutableHandleValue dst);
|
||||
MOZ_MUST_USE bool classMembers(NodeVector& members, MutableHandleValue dst);
|
||||
MOZ_MUST_USE bool classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic,
|
||||
TokenPos* pos, MutableHandleValue dst);
|
||||
MOZ_MUST_USE bool classField(HandleValue name, HandleValue initializer,
|
||||
TokenPos* pos, MutableHandleValue dst);
|
||||
MOZ_MUST_USE bool staticClassBlock(HandleValue body, TokenPos* pos, MutableHandleValue dst);
|
||||
|
||||
/*
|
||||
* expressions
|
||||
|
|
@ -1721,9 +1729,35 @@ NodeBuilder::classMethod(HandleValue name, HandleValue body, PropKind kind, bool
|
|||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::classMethods(NodeVector& methods, MutableHandleValue dst)
|
||||
NodeBuilder::classField(HandleValue name, HandleValue initializer,
|
||||
TokenPos* pos, MutableHandleValue dst)
|
||||
{
|
||||
return newArray(methods, dst);
|
||||
RootedValue cb(cx, callbacks[AST_CLASS_FIELD]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, name, initializer, pos, dst);
|
||||
|
||||
return newNode(AST_CLASS_FIELD, pos,
|
||||
"name", name,
|
||||
"init", initializer,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::staticClassBlock(HandleValue body, TokenPos* pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue cb(cx, callbacks[AST_STATIC_CLASS_BLOCK]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, body, pos, dst);
|
||||
|
||||
return newNode(AST_STATIC_CLASS_BLOCK, pos,
|
||||
"body", body,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::classMembers(NodeVector& members, MutableHandleValue dst)
|
||||
{
|
||||
return newArray(members, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -1853,6 +1887,8 @@ class ASTSerializer
|
|||
bool property(ParseNode* pn, MutableHandleValue dst);
|
||||
|
||||
bool classMethod(ClassMethod* classMethod, MutableHandleValue dst);
|
||||
bool classField(ClassField* classField, MutableHandleValue dst);
|
||||
bool staticClassBlock(StaticClassBlock* staticClassBlock, MutableHandleValue dst);
|
||||
|
||||
bool optIdentifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst) {
|
||||
if (!atom) {
|
||||
|
|
@ -1943,6 +1979,12 @@ ASTSerializer::aop(JSOp op)
|
|||
return AOP_BITXOR;
|
||||
case JSOP_BITAND:
|
||||
return AOP_BITAND;
|
||||
case JSOP_COALESCE:
|
||||
return AOP_COALESCE;
|
||||
case JSOP_OR:
|
||||
return AOP_OR;
|
||||
case JSOP_AND:
|
||||
return AOP_AND;
|
||||
default:
|
||||
return AOP_ERR;
|
||||
}
|
||||
|
|
@ -2457,7 +2499,7 @@ ASTSerializer::classDefinition(ClassNode* pn, bool expr, MutableHandleValue dst)
|
|||
}
|
||||
|
||||
return optExpression(pn->heritage(), &heritage) &&
|
||||
statement(pn->methodList(), &classBody) &&
|
||||
statement(pn->memberList(), &classBody) &&
|
||||
builder.classDefinition(expr, className, heritage, classBody, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
|
|
@ -2676,24 +2718,43 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
|
|||
case PNK_CLASS:
|
||||
return classDefinition(&pn->as<ClassNode>(), false, dst);
|
||||
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSMEMBERLIST:
|
||||
{
|
||||
ListNode* methodList = &pn->as<ListNode>();
|
||||
NodeVector methods(cx);
|
||||
if (!methods.reserve(methodList->count()))
|
||||
ListNode* memberList = &pn->as<ListNode>();
|
||||
NodeVector members(cx);
|
||||
if (!members.reserve(memberList->count()))
|
||||
return false;
|
||||
|
||||
for (ParseNode* item : methodList->contents()) {
|
||||
ClassMethod* method = &item->as<ClassMethod>();
|
||||
MOZ_ASSERT(methodList->pn_pos.encloses(method->pn_pos));
|
||||
for (ParseNode* item : memberList->contents()) {
|
||||
if (item->is<LexicalScopeNode>())
|
||||
item = item->as<LexicalScopeNode>().scopeBody();
|
||||
if (item->is<ClassField>()) {
|
||||
ClassField* field = &item->as<ClassField>();
|
||||
MOZ_ASSERT(memberList->pn_pos.encloses(field->pn_pos));
|
||||
|
||||
RootedValue prop(cx);
|
||||
if (!classMethod(method, &prop))
|
||||
return false;
|
||||
methods.infallibleAppend(prop);
|
||||
RootedValue prop(cx);
|
||||
if (!classField(field, &prop))
|
||||
return false;
|
||||
members.infallibleAppend(prop);
|
||||
} else if (item->is<StaticClassBlock>()) {
|
||||
StaticClassBlock* scb = &item->as<StaticClassBlock>();
|
||||
MOZ_ASSERT(memberList->pn_pos.encloses(scb->pn_pos));
|
||||
RootedValue prop(cx);
|
||||
if (!staticClassBlock(scb, &prop))
|
||||
return false;
|
||||
members.infallibleAppend(prop);
|
||||
} else {
|
||||
ClassMethod* method = &item->as<ClassMethod>();
|
||||
MOZ_ASSERT(memberList->pn_pos.encloses(method->pn_pos));
|
||||
|
||||
RootedValue prop(cx);
|
||||
if (!classMethod(method, &prop))
|
||||
return false;
|
||||
members.infallibleAppend(prop);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.classMethods(methods, dst);
|
||||
return builder.classMembers(members, dst);
|
||||
}
|
||||
|
||||
case PNK_NOP:
|
||||
|
|
@ -2732,6 +2793,47 @@ ASTSerializer::classMethod(ClassMethod* classMethod, MutableHandleValue dst)
|
|||
builder.classMethod(key, val, kind, isStatic, &classMethod->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::classField(ClassField* classField, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue key(cx), val(cx);
|
||||
// Dig through the lambda and get to the actual expression
|
||||
ParseNode* value = classField->initializer()
|
||||
->body()
|
||||
->head()->as<LexicalScopeNode>()
|
||||
.scopeBody()->as<ListNode>()
|
||||
.head()->as<UnaryNode>()
|
||||
.kid()->as<BinaryNode>()
|
||||
.right();
|
||||
// RawUndefinedExpr is the node we use for "there is no initializer". If one
|
||||
// writes, literally, `x = undefined;`, it will not be a RawUndefinedExpr
|
||||
// node, but rather a variable reference.
|
||||
// Behavior for "there is no initializer" should be { ..., "init": null }
|
||||
if (value->getKind() != PNK_RAW_UNDEFINED) {
|
||||
if (!expression(value, &val))
|
||||
return false;
|
||||
} else {
|
||||
val.setNull();
|
||||
}
|
||||
return propertyName(&classField->name(), &key) &&
|
||||
builder.classField(key, val, &classField->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::staticClassBlock(StaticClassBlock* staticClassBlock, MutableHandleValue dst)
|
||||
{
|
||||
FunctionNode* fun = staticClassBlock->function();
|
||||
|
||||
NodeVector args(cx);
|
||||
NodeVector defaults(cx);
|
||||
|
||||
RootedValue body(cx), rest(cx);
|
||||
rest.setNull();
|
||||
return functionArgsAndBody(fun->body(), args, defaults, false, false,
|
||||
&body, &rest) &&
|
||||
builder.staticClassBlock(body, &staticClassBlock->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::leftAssociate(ListNode* node, MutableHandleValue dst)
|
||||
{
|
||||
|
|
@ -3012,6 +3114,9 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
|
|
|
|||
|
|
@ -676,8 +676,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
|||
if (lazy->hasBeenCloned())
|
||||
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,
|
||||
pn->pn_pos, BytecodeEmitter::LazyFunction);
|
||||
pn->pn_pos, BytecodeEmitter::LazyFunction, fieldInitializers);
|
||||
if (!bce.init())
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -110,14 +110,22 @@ CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& opt
|
|||
bool
|
||||
IsIdentifier(JSLinearString* str);
|
||||
|
||||
bool
|
||||
IsIdentifierNameOrPrivateName(JSLinearString* str);
|
||||
|
||||
/*
|
||||
* As above, but taking chars + length.
|
||||
*/
|
||||
bool
|
||||
IsIdentifier(const char* chars, size_t length);
|
||||
IsIdentifier(const Latin1Char* chars, size_t length);
|
||||
bool
|
||||
IsIdentifier(const char16_t* chars, size_t length);
|
||||
|
||||
static bool
|
||||
IsIdentifierNameOrPrivateName(const Latin1Char* chars, size_t length);
|
||||
bool
|
||||
IsIdentifierNameOrPrivateName(const char16_t* chars, size_t length);
|
||||
|
||||
/* True if str is a keyword. Defined in TokenStream.cpp. */
|
||||
bool
|
||||
IsKeyword(JSLinearString* str);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -14,6 +14,7 @@
|
|||
#include "jsscript.h"
|
||||
|
||||
#include "ds/InlineTable.h"
|
||||
#include "frontend/DestructuringFlavor.h"
|
||||
#include "frontend/JumpList.h"
|
||||
#include "frontend/Parser.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
|
|
@ -121,9 +122,11 @@ typedef Vector<jsbytecode, 256> BytecodeVector;
|
|||
typedef Vector<jssrcnote, 64> SrcNotesVector;
|
||||
|
||||
class CallOrNewEmitter;
|
||||
class ClassEmitter;
|
||||
class ElemOpEmitter;
|
||||
class EmitterScope;
|
||||
class NestableControl;
|
||||
class PropertyEmitter;
|
||||
class PropOpEmitter;
|
||||
class TDZCheckCache;
|
||||
|
||||
|
|
@ -177,6 +180,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
EmitterScope* innermostEmitterScope_;
|
||||
TDZCheckCache* innermostTDZCheckCache;
|
||||
|
||||
/* field info for enclosing class */
|
||||
FieldInitializers fieldInitializers_;
|
||||
const FieldInitializers& getFieldInitializers() { return fieldInitializers_; }
|
||||
|
||||
#ifdef DEBUG
|
||||
bool unstableEmitterScope;
|
||||
|
||||
|
|
@ -233,10 +240,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
|
||||
const EmitterMode emitterMode;
|
||||
|
||||
mozilla::Maybe<uint32_t> scriptStartOffset;
|
||||
|
||||
// The end location of a function body that is being emitted.
|
||||
uint32_t functionBodyEndPos;
|
||||
// Whether functionBodyEndPos was set.
|
||||
bool functionBodyEndPosSet;
|
||||
mozilla::Maybe<uint32_t> functionBodyEndPos;
|
||||
|
||||
/*
|
||||
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
|
||||
|
|
@ -246,13 +253,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
*/
|
||||
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||
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
|
||||
// line and that sets functionBodyEndPos as well.
|
||||
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||
HandleScript script, Handle<LazyScript*> lazyScript,
|
||||
TokenPos bodyPosition, EmitterMode emitterMode = Normal);
|
||||
TokenPos bodyPosition, EmitterMode emitterMode = Normal,
|
||||
FieldInitializers fieldInitializers = FieldInitializers::Invalid());
|
||||
|
||||
MOZ_MUST_USE bool init();
|
||||
|
||||
|
|
@ -347,9 +356,14 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
return lastOpcodeIsJumpTarget() ? current->lastTarget.offset : offset();
|
||||
}
|
||||
|
||||
void setFunctionBodyEndPos(TokenPos pos) {
|
||||
functionBodyEndPos = pos.end;
|
||||
functionBodyEndPosSet = true;
|
||||
void setFunctionBodyEndPos(uint32_t pos) {
|
||||
functionBodyEndPos = mozilla::Some(pos);
|
||||
}
|
||||
|
||||
void setScriptStartOffsetIfUnset(uint32_t pos) {
|
||||
if (scriptStartOffset.isNothing()) {
|
||||
scriptStartOffset = mozilla::Some(pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool reportError(ParseNode* pn, unsigned errorNumber, ...);
|
||||
|
|
@ -443,6 +457,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
// Helper to emit JSOP_POP or JSOP_POPN.
|
||||
MOZ_MUST_USE bool emitPopN(unsigned n);
|
||||
|
||||
// Helper to emit JSOP_SWAP or JSOP_UNPICK.
|
||||
MOZ_MUST_USE bool emitUnpickN(unsigned n);
|
||||
|
||||
// Helper to emit JSOP_CHECKISOBJ.
|
||||
MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind);
|
||||
|
||||
|
|
@ -504,16 +521,26 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
|
||||
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_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
|
||||
|
||||
MOZ_MUST_USE bool emitHoistedFunctionsInList(ListNode* stmtList);
|
||||
|
||||
MOZ_MUST_USE bool emitPropertyList(ListNode* obj, MutableHandlePlainObject objp,
|
||||
MOZ_MUST_USE bool emitPropertyList(ListNode* obj, PropertyEmitter& pe,
|
||||
PropListType type);
|
||||
|
||||
enum class FieldPlacement { Instance, Static };
|
||||
FieldInitializers setupFieldInitializers(ListNode* classMembers, FieldPlacement placement);
|
||||
MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj, FieldPlacement placement);
|
||||
MOZ_MUST_USE bool emitCreateFieldInitializers(ClassEmitter& ce, ListNode* obj, FieldPlacement placement);
|
||||
const FieldInitializers& findFieldInitializersForCall();
|
||||
MOZ_MUST_USE bool emitInitializeInstanceFields();
|
||||
MOZ_MUST_USE bool emitInitializeStaticFields(ListNode* classMembers);
|
||||
|
||||
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
|
||||
// not used to unconditionally emit JSOP_GETLOCAL. Variable access should
|
||||
// instead be emitted using EmitVarOp. In special cases, when the caller
|
||||
|
|
@ -590,21 +617,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* switchStmt);
|
||||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(TryNode* tryNode);
|
||||
|
||||
enum DestructuringFlavor {
|
||||
// Destructuring into a declaration.
|
||||
DestructuringDeclaration,
|
||||
|
||||
// Destructuring into a formal parameter, when the formal parameters
|
||||
// contain an expression that might be evaluated, and thus require
|
||||
// this destructuring to assign not into the innermost scope that
|
||||
// contains the function body's vars, but into its enclosing scope for
|
||||
// parameter expressions.
|
||||
DestructuringFormalParameterInVarScope,
|
||||
|
||||
// Destructuring as part of an AssignmentExpression.
|
||||
DestructuringAssignment
|
||||
};
|
||||
|
||||
// emitDestructuringLHSRef emits the lhs expression's reference.
|
||||
// If the lhs expression is object property |OBJ.prop|, it emits |OBJ|.
|
||||
// If it's object element |OBJ[ELEM]|, it emits |OBJ| and |ELEM|.
|
||||
|
|
@ -681,15 +693,19 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
// is called at compile time.
|
||||
MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
|
||||
|
||||
MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name,
|
||||
FunctionPrefixKind prefixKind = FunctionPrefixKind::None);
|
||||
MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name);
|
||||
MOZ_MUST_USE bool setFunName(JSFunction* fun, JSAtom* name);
|
||||
MOZ_MUST_USE bool emitSetClassConstructorName(JSAtom* name);
|
||||
MOZ_MUST_USE bool emitSetFunctionNameFromStack(uint8_t offset);
|
||||
|
||||
MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
|
||||
MOZ_MUST_USE bool emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern);
|
||||
|
||||
MOZ_MUST_USE bool emitCallSiteObject(CallSiteNode* callSiteObj);
|
||||
MOZ_MUST_USE bool emitTemplateString(ListNode* templateString);
|
||||
MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, JSOp compoundOp, ParseNode* rhs);
|
||||
MOZ_MUST_USE bool emitAssignmentOrInit(ParseNodeKind kind, JSOp compoundOp,
|
||||
ParseNode* lhs, ParseNode* rhs);
|
||||
MOZ_MUST_USE bool emitShortCircuitAssignment(ParseNodeKind kind, JSOp op,
|
||||
ParseNode* lhs, ParseNode* rhs);
|
||||
|
||||
MOZ_MUST_USE bool emitReturn(UnaryNode* returnNode);
|
||||
MOZ_MUST_USE bool emitStatement(UnaryNode* exprStmt);
|
||||
|
|
@ -769,21 +785,20 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitDo(BinaryNode* doNode);
|
||||
MOZ_MUST_USE bool emitWhile(BinaryNode* whileNode);
|
||||
|
||||
MOZ_MUST_USE bool emitFor(ForNode* forNode, EmitterScope* headLexicalEmitterScope = nullptr);
|
||||
MOZ_MUST_USE bool emitCStyleFor(ForNode* forNode, EmitterScope* headLexicalEmitterScope);
|
||||
MOZ_MUST_USE bool emitForIn(ForNode* forNode, EmitterScope* headLexicalEmitterScope);
|
||||
MOZ_MUST_USE bool emitForOf(ForNode* forNode, EmitterScope* headLexicalEmitterScope);
|
||||
MOZ_MUST_USE bool emitFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope = nullptr);
|
||||
MOZ_MUST_USE bool emitCStyleFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope);
|
||||
MOZ_MUST_USE bool emitForIn(ForNode* forNode, const EmitterScope* headLexicalEmitterScope);
|
||||
MOZ_MUST_USE bool emitForOf(ForNode* forNode, const EmitterScope* headLexicalEmitterScope);
|
||||
|
||||
MOZ_MUST_USE bool emitInitializeForInOrOfTarget(TernaryNode* forHead);
|
||||
|
||||
MOZ_MUST_USE bool emitBreak(PropertyName* label);
|
||||
MOZ_MUST_USE bool emitContinue(PropertyName* label);
|
||||
|
||||
MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ListNode* paramsBody);
|
||||
MOZ_MUST_USE bool emitFunctionFormalParameters(ListNode* paramsBody);
|
||||
MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
|
||||
MOZ_MUST_USE bool emitFunctionBody(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitLexicalInitialization(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitLexicalInitialization(NameNode* pn);
|
||||
MOZ_MUST_USE bool emitLexicalInitialization(JSAtom* name);
|
||||
|
||||
// Emit bytecode for the spread operator.
|
||||
//
|
||||
|
|
|
|||
73
js/src/frontend/DefaultEmitter.cpp
Normal file
73
js/src/frontend/DefaultEmitter.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "frontend/DefaultEmitter.h"
|
||||
|
||||
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
||||
|
||||
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
|
||||
#include "vm/Opcodes.h" // JSOP_*
|
||||
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
using mozilla::Maybe;
|
||||
using mozilla::Nothing;
|
||||
|
||||
DefaultEmitter::DefaultEmitter(BytecodeEmitter* bce) : bce_(bce) {}
|
||||
|
||||
bool DefaultEmitter::prepareForDefault() {
|
||||
MOZ_ASSERT(state_ == State::Start);
|
||||
|
||||
// [stack] VALUE
|
||||
|
||||
ifUndefined_.emplace(bce_);
|
||||
|
||||
if (!bce_->emit1(JSOP_DUP)) {
|
||||
// [stack] VALUE VALUE
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_UNDEFINED)) {
|
||||
// [stack] VALUE VALUE UNDEFINED
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_STRICTEQ)) {
|
||||
// [stack] VALUE EQ?
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ifUndefined_->emitThen()) {
|
||||
// [stack] VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack]
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Default;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DefaultEmitter::emitEnd() {
|
||||
MOZ_ASSERT(state_ == State::Default);
|
||||
|
||||
// [stack] DEFAULTVALUE
|
||||
|
||||
if (!ifUndefined_->emitEnd()) {
|
||||
// [stack] VALUE/DEFAULTVALUE
|
||||
return false;
|
||||
}
|
||||
ifUndefined_.reset();
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::End;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
65
js/src/frontend/DefaultEmitter.h
Normal file
65
js/src/frontend/DefaultEmitter.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef frontend_DefaultEmitter_h
|
||||
#define frontend_DefaultEmitter_h
|
||||
|
||||
#include "mozilla/Attributes.h" // MOZ_STACK_CLASS, MOZ_MUST_USE
|
||||
#include "mozilla/Maybe.h" // Maybe
|
||||
|
||||
#include "frontend/IfEmitter.h" // IfEmitter
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
struct BytecodeEmitter;
|
||||
|
||||
// Class for emitting default parameter or default value.
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `x = 10` in `function (x = 10) {}`
|
||||
// // the value of arguments[0] is on the stack
|
||||
// DefaultEmitter de(this);
|
||||
// de.prepareForDefault();
|
||||
// emit(10);
|
||||
// de.emitEnd();
|
||||
//
|
||||
class MOZ_STACK_CLASS DefaultEmitter {
|
||||
BytecodeEmitter* bce_;
|
||||
|
||||
mozilla::Maybe<IfEmitter> ifUndefined_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+ prepareForDefault +---------+ emitEnd +-----+
|
||||
// | Start |------------------>| Default |-------->| End |
|
||||
// +-------+ +---------+ +-----+
|
||||
enum class State {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling prepareForDefault.
|
||||
Default,
|
||||
|
||||
// After calling emitEnd.
|
||||
End
|
||||
};
|
||||
State state_ = State::Start;
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit DefaultEmitter(BytecodeEmitter* bce);
|
||||
|
||||
MOZ_MUST_USE bool prepareForDefault();
|
||||
MOZ_MUST_USE bool emitEnd();
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_LabelEmitter_h */
|
||||
31
js/src/frontend/DestructuringFlavor.h
Normal file
31
js/src/frontend/DestructuringFlavor.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef frontend_DestructuringFlavor_h
|
||||
#define frontend_DestructuringFlavor_h
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
enum DestructuringFlavor {
|
||||
// Destructuring into a declaration.
|
||||
DestructuringDeclaration,
|
||||
|
||||
// Destructuring into a formal parameter, when the formal parameters
|
||||
// contain an expression that might be evaluated, and thus require
|
||||
// this destructuring to assign not into the innermost scope that
|
||||
// contains the function body's vars, but into its enclosing scope for
|
||||
// parameter expressions.
|
||||
DestructuringFormalParameterInVarScope,
|
||||
|
||||
// Destructuring as part of an AssignmentExpression.
|
||||
DestructuringAssignment
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_DestructuringFlavor_h */
|
||||
|
|
@ -132,11 +132,11 @@ ElemOpEmitter::emitGet()
|
|||
bool
|
||||
ElemOpEmitter::prepareForRhs()
|
||||
{
|
||||
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
|
||||
MOZ_ASSERT_IF(isSimpleAssignment(), state_ == State::Key);
|
||||
MOZ_ASSERT(isSimpleAssignment() || isPropInit()|| isCompoundAssignment());
|
||||
MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Key);
|
||||
MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
|
||||
|
||||
if (isSimpleAssignment()) {
|
||||
if (isSimpleAssignment() || isPropInit()) {
|
||||
// For CompoundAssignment, SUPERBASE is already emitted by emitGet.
|
||||
if (isSuper()) {
|
||||
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS KEY SUPERBASE
|
||||
|
|
@ -155,7 +155,7 @@ bool
|
|||
ElemOpEmitter::skipObjAndKeyAndRhs()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start);
|
||||
MOZ_ASSERT(isSimpleAssignment());
|
||||
MOZ_ASSERT(isSimpleAssignment() || isPropInit());
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Rhs;
|
||||
|
|
@ -203,12 +203,15 @@ ElemOpEmitter::emitDelete()
|
|||
bool
|
||||
ElemOpEmitter::emitAssignment()
|
||||
{
|
||||
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
|
||||
MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
|
||||
MOZ_ASSERT(state_ == State::Rhs);
|
||||
|
||||
JSOp setOp = isSuper()
|
||||
? bce_->sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER
|
||||
: bce_->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
MOZ_ASSERT_IF(isPropInit(), !isSuper());
|
||||
|
||||
JSOp setOp = isPropInit() ? JSOP_INITELEM
|
||||
: isSuper()
|
||||
? bce_->sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER
|
||||
: bce_->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
if (!bce_->emitElemOpBase(setOp)) { // ELEM
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ class MOZ_STACK_CLASS ElemOpEmitter
|
|||
PostDecrement,
|
||||
PreDecrement,
|
||||
SimpleAssignment,
|
||||
PropInit,
|
||||
CompoundAssignment
|
||||
};
|
||||
enum class ObjKind {
|
||||
|
|
@ -176,6 +177,7 @@ class MOZ_STACK_CLASS ElemOpEmitter
|
|||
// | +--------+ |
|
||||
// | +-------------------+
|
||||
// | [SimpleAssignment] |
|
||||
// | [PropInit] |
|
||||
// | prepareForRhs v +-----+
|
||||
// +--------------------->+-------------->+->| Rhs |-+
|
||||
// | ^ +-----+ |
|
||||
|
|
@ -225,6 +227,10 @@ class MOZ_STACK_CLASS ElemOpEmitter
|
|||
return kind_ == Kind::SimpleAssignment;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool isPropInit() const {
|
||||
return kind_ == Kind::PropInit;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool isDelete() const {
|
||||
return kind_ == Kind::Delete;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ EmitterScope::appendScopeNote(BytecodeEmitter* bce)
|
|||
}
|
||||
|
||||
bool
|
||||
EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart, uint32_t slotEnd)
|
||||
EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart, uint32_t slotEnd) const
|
||||
{
|
||||
// Lexical bindings throw ReferenceErrors if they are used before
|
||||
// initialization. See ES6 8.1.1.1.6.
|
||||
|
|
@ -993,7 +993,7 @@ EmitterScope::enterWith(BytecodeEmitter* bce)
|
|||
}
|
||||
|
||||
bool
|
||||
EmitterScope::deadZoneFrameSlots(BytecodeEmitter* bce)
|
||||
EmitterScope::deadZoneFrameSlots(BytecodeEmitter* bce) const
|
||||
{
|
||||
return deadZoneFrameSlotRange(bce, frameSlotStart(), frameSlotEnd());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class EmitterScope : public Nestable<EmitterScope>
|
|||
MOZ_MUST_USE bool appendScopeNote(BytecodeEmitter* bce);
|
||||
|
||||
MOZ_MUST_USE bool deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart,
|
||||
uint32_t slotEnd);
|
||||
uint32_t slotEnd) const;
|
||||
|
||||
public:
|
||||
explicit EmitterScope(BytecodeEmitter* bce);
|
||||
|
|
@ -105,7 +105,7 @@ class EmitterScope : public Nestable<EmitterScope>
|
|||
MOZ_MUST_USE bool enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc);
|
||||
MOZ_MUST_USE bool enterModule(BytecodeEmitter* module, ModuleSharedContext* modulesc);
|
||||
MOZ_MUST_USE bool enterWith(BytecodeEmitter* bce);
|
||||
MOZ_MUST_USE bool deadZoneFrameSlots(BytecodeEmitter* bce);
|
||||
MOZ_MUST_USE bool deadZoneFrameSlots(BytecodeEmitter* bce) const;
|
||||
|
||||
MOZ_MUST_USE bool leave(BytecodeEmitter* bce, bool nonLocal = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -351,9 +351,13 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_DIV:
|
||||
case PNK_MOD:
|
||||
case PNK_POW:
|
||||
case PNK_INITPROP:
|
||||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
|
|
@ -377,6 +381,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_OPTELEM:
|
||||
case PNK_OPTCALL:
|
||||
case PNK_NAME:
|
||||
case PNK_PRIVATE_NAME:
|
||||
case PNK_TEMPLATE_STRING:
|
||||
case PNK_TEMPLATE_STRING_LIST:
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
|
|
@ -400,8 +405,9 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_FORIN:
|
||||
case PNK_FOROF:
|
||||
case PNK_FORHEAD:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSFIELD:
|
||||
case PNK_STATICCLASSBLOCK:
|
||||
case PNK_CLASSMEMBERLIST:
|
||||
case PNK_CLASSNAMES:
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_IMPORT_META:
|
||||
|
|
@ -1679,6 +1685,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
return true;
|
||||
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_PRIVATE_NAME:
|
||||
case PNK_STRING:
|
||||
case PNK_TEMPLATE_STRING:
|
||||
MOZ_ASSERT(pn->is<NameNode>());
|
||||
|
|
@ -1746,6 +1753,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
case PNK_ARRAYPUSH:
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_COMPUTED_NAME:
|
||||
case PNK_STATICCLASSBLOCK:
|
||||
case PNK_SPREAD:
|
||||
case PNK_EXPORT:
|
||||
case PNK_VOID:
|
||||
|
|
@ -1810,7 +1818,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
case PNK_OBJECT:
|
||||
case PNK_ARRAYCOMP:
|
||||
case PNK_STATEMENTLIST:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSMEMBERLIST:
|
||||
case PNK_CATCHLIST:
|
||||
case PNK_TEMPLATE_STRING_LIST:
|
||||
case PNK_VAR:
|
||||
|
|
@ -1875,9 +1883,13 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
|
||||
case PNK_SWITCH:
|
||||
case PNK_COLON:
|
||||
case PNK_INITPROP:
|
||||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
|
|
@ -1902,6 +1914,16 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
Fold(cx, node->unsafeRightReference(), parser, inGenexpLambda);
|
||||
}
|
||||
|
||||
case PNK_CLASSFIELD: {
|
||||
ClassField* node = &pn->as<ClassField>();
|
||||
if (node->initializer()) {
|
||||
if (!Fold(cx, node->unsafeRightReference(), parser, inGenexpLambda)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_IMPORT_META:{
|
||||
#ifdef DEBUG
|
||||
|
|
|
|||
|
|
@ -367,11 +367,13 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
return literal;
|
||||
}
|
||||
|
||||
ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) {
|
||||
return new_<ClassNode>(name, heritage, methodBlock, pos);
|
||||
ClassNodeType newClass(Node name, Node heritage, LexicalScopeNodeType memberBlock,
|
||||
const TokenPos& pos)
|
||||
{
|
||||
return new_<ClassNode>(name, heritage, memberBlock, pos);
|
||||
}
|
||||
ListNodeType newClassMethodList(uint32_t begin) {
|
||||
return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1));
|
||||
ListNodeType newClassMemberList(uint32_t begin) {
|
||||
return new_<ListNode>(PNK_CLASSMEMBERLIST, TokenPos(begin, begin + 1));
|
||||
}
|
||||
ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
|
||||
return new_<ClassNames>(outer, inner, pos);
|
||||
|
|
@ -457,19 +459,37 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, FunctionNodeType funNode,
|
||||
JSOp op, bool isStatic)
|
||||
MOZ_MUST_USE ClassMethod* newClassMethodDefinition(Node key, FunctionNodeType funNode,
|
||||
JSOp op, bool isStatic)
|
||||
{
|
||||
MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
|
||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
||||
key->isKind(PNK_STRING) ||
|
||||
key->isKind(PNK_COMPUTED_NAME));
|
||||
MOZ_ASSERT(isUsableAsObjectPropertyName(key));
|
||||
|
||||
ClassMethod* classMethod = new_<ClassMethod>(key, funNode, op, isStatic);
|
||||
if (!classMethod)
|
||||
return false;
|
||||
methodList->append(classMethod);
|
||||
return new_<ClassMethod>(key, funNode, op, isStatic);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE ClassField* newClassFieldDefinition(Node name, FunctionNodeType initializer, bool isStatic)
|
||||
{
|
||||
MOZ_ASSERT(isUsableAsObjectPropertyName(name));
|
||||
|
||||
return new_<ClassField>(name, initializer, isStatic);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE StaticClassBlock* newStaticClassBlock(FunctionNodeType block)
|
||||
{
|
||||
return new_<StaticClassBlock>(block);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList, Node member)
|
||||
{
|
||||
MOZ_ASSERT(memberList->isKind(PNK_CLASSMEMBERLIST));
|
||||
// Constructors can be surrounded by LexicalScopes.
|
||||
MOZ_ASSERT(member->isKind(PNK_CLASSMETHOD) ||
|
||||
member->isKind(PNK_CLASSFIELD) ||
|
||||
member->isKind(PNK_STATICCLASSBLOCK) ||
|
||||
(member->isKind(PNK_LEXICALSCOPE) &&
|
||||
member->as<LexicalScopeNode>().scopeBody()->isKind(PNK_CLASSMETHOD)));
|
||||
|
||||
addList(/* list = */ memberList, /* kid = */ member);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -732,8 +752,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
pn->setDirectRHSAnonFunction(true);
|
||||
}
|
||||
|
||||
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind) {
|
||||
return new_<FunctionNode>(syntaxKind, pos());
|
||||
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) {
|
||||
return new_<FunctionNode>(syntaxKind, pos);
|
||||
}
|
||||
|
||||
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) {
|
||||
|
|
@ -819,6 +839,13 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
return node->isKind(PNK_SUPERBASE);
|
||||
}
|
||||
|
||||
bool isUsableAsObjectPropertyName(ParseNode* node) {
|
||||
return node->isKind(PNK_NUMBER) ||
|
||||
node->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
||||
node->isKind(PNK_STRING) ||
|
||||
node->isKind(PNK_COMPUTED_NAME);
|
||||
}
|
||||
|
||||
inline MOZ_MUST_USE bool finishInitializerAssignment(NameNodeType nameNode, Node init);
|
||||
|
||||
void setBeginPosition(Node pn, Node oth) {
|
||||
|
|
@ -853,9 +880,9 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
return new_<ListNode>(kind, op, pos());
|
||||
}
|
||||
|
||||
ListNodeType newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) {
|
||||
ListNodeType newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) {
|
||||
MOZ_ASSERT(!isDeclarationKind(kind));
|
||||
return new_<ListNode>(kind, op, TokenPos(begin, begin + 1));
|
||||
return new_<ListNode>(kind, op, pos);
|
||||
}
|
||||
|
||||
ListNodeType newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
|
||||
|
|
|
|||
1027
js/src/frontend/FunctionEmitter.cpp
Normal file
1027
js/src/frontend/FunctionEmitter.cpp
Normal file
File diff suppressed because it is too large
Load diff
450
js/src/frontend/FunctionEmitter.h
Normal file
450
js/src/frontend/FunctionEmitter.h
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef frontend_FunctionEmitter_h
|
||||
#define frontend_FunctionEmitter_h
|
||||
|
||||
#include "mozilla/Attributes.h" // MOZ_STACK_CLASS, MOZ_MUST_USE
|
||||
|
||||
#include <stdint.h> // uint16_t, uint32_t
|
||||
|
||||
#include "jsopcode.h"
|
||||
#include "jsfun.h" // JSFunction
|
||||
|
||||
#include "frontend/DefaultEmitter.h" // DefaultEmitter
|
||||
#include "frontend/DestructuringFlavor.h" // DestructuringFlavor
|
||||
#include "frontend/EmitterScope.h" // EmitterScope
|
||||
#include "frontend/SharedContext.h" // FunctionBox
|
||||
#include "frontend/TDZCheckCache.h" // TDZCheckCache
|
||||
#include "gc/Rooting.h" // JS::Rooted, JS::Handle
|
||||
#include "vm/String.h" // JSAtom
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
struct BytecodeEmitter;
|
||||
|
||||
// Class for emitting function declaration, expression, or method etc.
|
||||
//
|
||||
// This class handles the enclosing script's part (function object creation,
|
||||
// declaration, etc). The content of the function script is handled by
|
||||
// FunctionScriptEmitter and FunctionParamsEmitter.
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `function f() {}`, non lazy script
|
||||
// FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
|
||||
// false);
|
||||
// fe.prepareForNonLazy();
|
||||
//
|
||||
// // Emit script with FunctionScriptEmitter here.
|
||||
// ...
|
||||
//
|
||||
// fe.emitNonLazyEnd();
|
||||
//
|
||||
// `function f() {}`, lazy script
|
||||
// FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
|
||||
// false);
|
||||
// fe.emitLazy();
|
||||
//
|
||||
// `function f() {}`, emitting hoisted function again
|
||||
// // See emitAgain comment for more details
|
||||
// FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
|
||||
// true);
|
||||
// fe.emitAgain();
|
||||
//
|
||||
// `function f() { "use asm"; }`
|
||||
// FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
|
||||
// false);
|
||||
// fe.emitAsmJSModule();
|
||||
//
|
||||
class MOZ_STACK_CLASS FunctionEmitter {
|
||||
private:
|
||||
BytecodeEmitter* bce_;
|
||||
|
||||
FunctionBox* funbox_;
|
||||
|
||||
// Function linked from funbox_.
|
||||
JS::Rooted<JSFunction*> fun_;
|
||||
|
||||
// Function's explicit name.
|
||||
JS::Rooted<JSAtom*> name_;
|
||||
|
||||
FunctionSyntaxKind syntaxKind_;
|
||||
bool isHoisted_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+
|
||||
// | Start |-+
|
||||
// +-------+ |
|
||||
// |
|
||||
// +-------+
|
||||
// |
|
||||
// | [non-lazy function]
|
||||
// | prepareForNonLazy +---------+ emitNonLazyEnd +-----+
|
||||
// +--------------------->| NonLazy |---------------->+->| End |
|
||||
// | +---------+ ^ +-----+
|
||||
// | |
|
||||
// | [lazy function] |
|
||||
// | emitLazy |
|
||||
// +------------------------------------------------->+
|
||||
// | ^
|
||||
// | [emitting hoisted function again] |
|
||||
// | emitAgain |
|
||||
// +------------------------------------------------->+
|
||||
// | ^
|
||||
// | [asm.js module] |
|
||||
// | emitAsmJSModule |
|
||||
// +--------------------------------------------------+
|
||||
//
|
||||
enum class State {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling prepareForNonLazy.
|
||||
NonLazy,
|
||||
|
||||
// After calling emitNonLazyEnd, emitLazy, emitAgain, or emitAsmJSModule.
|
||||
End
|
||||
};
|
||||
State state_ = State::Start;
|
||||
#endif
|
||||
|
||||
public:
|
||||
FunctionEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
|
||||
FunctionSyntaxKind syntaxKind, bool isHoisted);
|
||||
|
||||
MOZ_MUST_USE bool prepareForNonLazy();
|
||||
MOZ_MUST_USE bool emitNonLazyEnd();
|
||||
|
||||
MOZ_MUST_USE bool emitLazy();
|
||||
|
||||
MOZ_MUST_USE bool emitAgain();
|
||||
|
||||
MOZ_MUST_USE bool emitAsmJSModule();
|
||||
|
||||
private:
|
||||
// Common code for non-lazy and lazy functions.
|
||||
MOZ_MUST_USE bool interpretedCommon();
|
||||
|
||||
// Emit the function declaration, expression, method etc.
|
||||
// This leaves function object on the stack for expression etc,
|
||||
// and doesn't for declaration.
|
||||
MOZ_MUST_USE bool emitFunction();
|
||||
|
||||
// Helper methods used by emitFunction for each case.
|
||||
// `index` is the object index of the function.
|
||||
MOZ_MUST_USE bool emitNonHoisted(unsigned index);
|
||||
MOZ_MUST_USE bool emitHoisted(unsigned index);
|
||||
MOZ_MUST_USE bool emitTopLevelFunction(unsigned index);
|
||||
MOZ_MUST_USE bool emitNewTargetForArrow();
|
||||
};
|
||||
|
||||
// Class for emitting function script.
|
||||
// Parameters are handled by FunctionParamsEmitter.
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `function f(a) { expr }`
|
||||
// FunctionScriptEmitter fse(this, funbox_for_f,
|
||||
// Some(offset_of_opening_paren),
|
||||
// Some(offset_of_closing_brace));
|
||||
// fse.prepareForParameters();
|
||||
//
|
||||
// // Emit parameters with FunctionParamsEmitter here.
|
||||
// ...
|
||||
//
|
||||
// fse.prepareForBody();
|
||||
// emit(expr);
|
||||
// fse.emitEnd();
|
||||
//
|
||||
// // Do NameFunctions operation here if needed.
|
||||
//
|
||||
// fse.initScript();
|
||||
//
|
||||
class MOZ_STACK_CLASS FunctionScriptEmitter {
|
||||
private:
|
||||
BytecodeEmitter* bce_;
|
||||
|
||||
FunctionBox* funbox_;
|
||||
|
||||
// Scope for the function name for a named lambda.
|
||||
// None for anonymous function.
|
||||
mozilla::Maybe<EmitterScope> namedLambdaEmitterScope_;
|
||||
|
||||
// Scope for function body.
|
||||
mozilla::Maybe<EmitterScope> functionEmitterScope_;
|
||||
|
||||
// Scope for the extra body var.
|
||||
// None if `funbox_->hasExtraBodyVarScope() == false`.
|
||||
mozilla::Maybe<EmitterScope> extraBodyVarEmitterScope_;
|
||||
|
||||
mozilla::Maybe<TDZCheckCache> tdzCache_;
|
||||
|
||||
// See the comment for constructor.
|
||||
mozilla::Maybe<uint32_t> paramStart_;
|
||||
mozilla::Maybe<uint32_t> bodyEnd_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+ prepareForParameters +------------+
|
||||
// | Start |---------------------->| Parameters |-+
|
||||
// +-------+ +------------+ |
|
||||
// |
|
||||
// +--------------------------------------------+
|
||||
// |
|
||||
// | prepareForBody +------+ emitEndBody +---------+
|
||||
// +---------------->| Body |------------->| EndBody |-+
|
||||
// +------+ +---------+ |
|
||||
// |
|
||||
// +-------------------------------------------------+
|
||||
// |
|
||||
// | initScript +-----+
|
||||
// +------------>| End |
|
||||
// +-----+
|
||||
enum class State {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling prepareForParameters.
|
||||
Parameters,
|
||||
|
||||
// After calling prepareForBody.
|
||||
Body,
|
||||
|
||||
// After calling emitEndBody.
|
||||
EndBody,
|
||||
|
||||
// After calling initScript.
|
||||
End
|
||||
};
|
||||
State state_ = State::Start;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Parameters are the offset in the source code for each character below:
|
||||
//
|
||||
// function f(a, b, ...c) { ... }
|
||||
// ^ ^
|
||||
// | |
|
||||
// paramStart bodyEnd
|
||||
//
|
||||
// Can be Nothing() if not available.
|
||||
FunctionScriptEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
|
||||
const mozilla::Maybe<uint32_t>& paramStart,
|
||||
const mozilla::Maybe<uint32_t>& bodyEnd)
|
||||
: bce_(bce),
|
||||
funbox_(funbox),
|
||||
paramStart_(paramStart),
|
||||
bodyEnd_(bodyEnd) {}
|
||||
|
||||
MOZ_MUST_USE bool prepareForParameters();
|
||||
MOZ_MUST_USE bool prepareForBody();
|
||||
MOZ_MUST_USE bool emitEndBody();
|
||||
|
||||
// Initialize JSScript for this function.
|
||||
// WARNING: There shouldn't be any fallible operation for the function
|
||||
// compilation after `initScript` call.
|
||||
// See the comment inside JSScript::fullyInitFromEmitter for
|
||||
// more details.
|
||||
MOZ_MUST_USE bool initScript();
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE bool emitExtraBodyVarScope();
|
||||
};
|
||||
|
||||
// Class for emitting function parameters.
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `function f(a, b=10, ...c) {}`
|
||||
// FunctionParamsEmitter fpe(this, funbox_for_f);
|
||||
//
|
||||
// fpe.emitSimple(atom_of_a);
|
||||
//
|
||||
// fpe.prepareForDefault();
|
||||
// emit(10);
|
||||
// fpe.emitDefaultEnd(atom_of_b);
|
||||
//
|
||||
// fpe.emitRest(atom_of_c);
|
||||
//
|
||||
// `function f([a], [b]=[1], ...[c]) {}`
|
||||
// FunctionParamsEmitter fpe(this, funbox_for_f);
|
||||
//
|
||||
// fpe.prepareForDestructuring();
|
||||
// emit(destructuring_for_[a]);
|
||||
// fpe.emitDestructuringEnd();
|
||||
//
|
||||
// fpe.prepareForDestructuringDefaultInitializer();
|
||||
// emit([1]);
|
||||
// fpe.prepareForDestructuringDefault();
|
||||
// emit(destructuring_for_[b]);
|
||||
// fpe.emitDestructuringDefaultEnd();
|
||||
//
|
||||
// fpe.prepareForDestructuringRest();
|
||||
// emit(destructuring_for_[c]);
|
||||
// fpe.emitDestructuringRestEnd();
|
||||
//
|
||||
class MOZ_STACK_CLASS FunctionParamsEmitter {
|
||||
private:
|
||||
BytecodeEmitter* bce_;
|
||||
|
||||
FunctionBox* funbox_;
|
||||
|
||||
// The pointer to `FunctionScriptEmitter::functionEmitterScope_`,
|
||||
// passed via `BytecodeEmitter::innermostEmitterScope()`.
|
||||
EmitterScope* functionEmitterScope_;
|
||||
|
||||
// The slot for the current parameter.
|
||||
// NOTE: after emitting rest parameter, this isn't incremented.
|
||||
uint16_t argSlot_ = 0;
|
||||
|
||||
// DefaultEmitter for default parameter.
|
||||
mozilla::Maybe<DefaultEmitter> default_;
|
||||
|
||||
// Scope for each parameter expression.
|
||||
// Populated only when there's `eval` in parameters.
|
||||
mozilla::Maybe<EmitterScope> paramExprVarEmitterScope_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +----------------------------------------------------------+
|
||||
// | |
|
||||
// | +-------+ |
|
||||
// +->| Start |-+ |
|
||||
// +-------+ | |
|
||||
// | |
|
||||
// +------------+ |
|
||||
// | |
|
||||
// | [single binding, wihtout default] |
|
||||
// | emitSimple |
|
||||
// +--------------------------------------------------------->+
|
||||
// | ^
|
||||
// | [single binding, with default] |
|
||||
// | prepareForDefault +---------+ emitDefaultEnd |
|
||||
// +--------------------->| Default |------------------------>+
|
||||
// | +---------+ ^
|
||||
// | |
|
||||
// | [destructuring, without default] |
|
||||
// | prepareForDestructuring +---------------+ |
|
||||
// +--------------------------->| Destructuring |-+ |
|
||||
// | +---------------+ | |
|
||||
// | | |
|
||||
// | +-----------------------------------------+ |
|
||||
// | | |
|
||||
// | | emitDestructuringEnd |
|
||||
// | +---------------------------------------------------->+
|
||||
// | ^
|
||||
// | [destructuring, with default] |
|
||||
// | prepareForDestructuringDefaultInitializer |
|
||||
// +---------------------------------------------+ |
|
||||
// | | |
|
||||
// | +----------------------------------------+ |
|
||||
// | | |
|
||||
// | | +---------------------------------+ |
|
||||
// | +->| DestructuringDefaultInitializer |-+ |
|
||||
// | +---------------------------------+ | |
|
||||
// | | |
|
||||
// | +------------------------------------+ |
|
||||
// | | |
|
||||
// | | prepareForDestructuringDefault |
|
||||
// | +-------------------------------+ |
|
||||
// | | |
|
||||
// | +-----------------------------+ |
|
||||
// | | |
|
||||
// | | +----------------------+ |
|
||||
// | +->| DestructuringDefault |-+ |
|
||||
// | +----------------------+ | |
|
||||
// | | |
|
||||
// | +-------------------------+ |
|
||||
// | | |
|
||||
// | | emitDestructuringDefaultEnd |
|
||||
// | +---------------------------------------------->+
|
||||
// |
|
||||
// | [single binding rest]
|
||||
// | emitRest +-----+
|
||||
// +--------------------------------------------------------->+->| End |
|
||||
// | ^ +-----+
|
||||
// | [destructuring rest] |
|
||||
// | prepareForDestructuringRest +-------------------+ |
|
||||
// +-------------------------------->| DestructuringRest |-+ |
|
||||
// +-------------------+ | |
|
||||
// | |
|
||||
// +----------------------------------------------------+ |
|
||||
// | |
|
||||
// | emitDestructuringRestEnd |
|
||||
// +-------------------------------------------------------+
|
||||
//
|
||||
enum class State {
|
||||
// The initial state, or after emitting non-rest parameter.
|
||||
Start,
|
||||
|
||||
// After calling prepareForDefault.
|
||||
Default,
|
||||
|
||||
// After calling prepareForDestructuring.
|
||||
Destructuring,
|
||||
|
||||
// After calling prepareForDestructuringDefaultInitializer.
|
||||
DestructuringDefaultInitializer,
|
||||
|
||||
// After calling prepareForDestructuringDefault.
|
||||
DestructuringDefault,
|
||||
|
||||
// After calling prepareForDestructuringRest.
|
||||
DestructuringRest,
|
||||
|
||||
// After calling emitRest or emitDestructuringRestEnd.
|
||||
End,
|
||||
};
|
||||
State state_ = State::Start;
|
||||
#endif
|
||||
|
||||
public:
|
||||
FunctionParamsEmitter(BytecodeEmitter* bce, FunctionBox* funbox);
|
||||
|
||||
// paramName is used only when there's at least one expression in the
|
||||
// paramerters (funbox_->hasParameterExprs == true).
|
||||
MOZ_MUST_USE bool emitSimple(JS::Handle<JSAtom*> paramName);
|
||||
|
||||
MOZ_MUST_USE bool prepareForDefault();
|
||||
MOZ_MUST_USE bool emitDefaultEnd(JS::Handle<JSAtom*> paramName);
|
||||
|
||||
MOZ_MUST_USE bool prepareForDestructuring();
|
||||
MOZ_MUST_USE bool emitDestructuringEnd();
|
||||
|
||||
MOZ_MUST_USE bool prepareForDestructuringDefaultInitializer();
|
||||
MOZ_MUST_USE bool prepareForDestructuringDefault();
|
||||
MOZ_MUST_USE bool emitDestructuringDefaultEnd();
|
||||
|
||||
MOZ_MUST_USE bool emitRest(JS::Handle<JSAtom*> paramName);
|
||||
|
||||
MOZ_MUST_USE bool prepareForDestructuringRest();
|
||||
MOZ_MUST_USE bool emitDestructuringRestEnd();
|
||||
|
||||
MOZ_MUST_USE DestructuringFlavor getDestructuringFlavor();
|
||||
|
||||
private:
|
||||
// Enter/leave var scope for `eval` if necessary.
|
||||
MOZ_MUST_USE bool enterParameterExpressionVarScope();
|
||||
MOZ_MUST_USE bool leaveParameterExpressionVarScope();
|
||||
|
||||
MOZ_MUST_USE bool prepareForInitializer();
|
||||
MOZ_MUST_USE bool emitInitializerEnd();
|
||||
|
||||
MOZ_MUST_USE bool emitRestArray();
|
||||
|
||||
MOZ_MUST_USE bool emitAssignment(JS::Handle<JSAtom*> paramName);
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_FunctionEmitter_h */
|
||||
60
js/src/frontend/LexicalScopeEmitter.cpp
Normal file
60
js/src/frontend/LexicalScopeEmitter.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "frontend/LexicalScopeEmitter.h"
|
||||
|
||||
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
|
||||
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
LexicalScopeEmitter::LexicalScopeEmitter(BytecodeEmitter* bce) : bce_(bce) {}
|
||||
|
||||
bool LexicalScopeEmitter::emitScope(ScopeKind kind, JS::Handle<LexicalScope::Data*> bindings)
|
||||
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start);
|
||||
MOZ_ASSERT(bindings);
|
||||
|
||||
tdzCache_.emplace(bce_);
|
||||
emitterScope_.emplace(bce_);
|
||||
if (!emitterScope_->enterLexical(bce_, kind, bindings))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Scope;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LexicalScopeEmitter::emitEmptyScope()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start);
|
||||
|
||||
tdzCache_.emplace(bce_);
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Scope;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LexicalScopeEmitter::emitEnd()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Scope);
|
||||
|
||||
if (emitterScope_) {
|
||||
if (!emitterScope_->leave(bce_))
|
||||
return false;
|
||||
emitterScope_.reset();
|
||||
}
|
||||
tdzCache_.reset();
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::End;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
99
js/src/frontend/LexicalScopeEmitter.h
Normal file
99
js/src/frontend/LexicalScopeEmitter.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef frontend_LexicalScopeEmitter_h
|
||||
#define frontend_LexicalScopeEmitter_h
|
||||
|
||||
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
||||
#include "mozilla/Attributes.h" // MOZ_STACK_CLASS, MOZ_MUST_USE
|
||||
#include "mozilla/Maybe.h" // Maybe
|
||||
|
||||
#include "frontend/EmitterScope.h" // EmitterScope
|
||||
#include "frontend/TDZCheckCache.h" // TDZCheckCache
|
||||
#include "gc/Rooting.h" // JS::Handle
|
||||
#include "vm/Scope.h" // ScopeKind, LexicalScope
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
struct BytecodeEmitter;
|
||||
|
||||
// Class for emitting bytecode for lexical scope.
|
||||
//
|
||||
// In addition to emitting code for entering and leaving a scope, this RAII
|
||||
// guard affects the code emitted for `break` and other non-structured
|
||||
// control flow. See NonLocalExitControl::prepareForNonLocalJump().
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `{ ... }` -- lexical scope with no bindings
|
||||
// LexicalScopeEmitter lse(this);
|
||||
// lse.emitEmptyScope();
|
||||
// emit(scopeBody);
|
||||
// lse.emitEnd();
|
||||
//
|
||||
// `{ let a; body }`
|
||||
// LexicalScopeEmitter lse(this);
|
||||
// lse.emitScope(ScopeKind::Lexical, scopeBinding);
|
||||
// emit(let_and_body);
|
||||
// lse.emitEnd();
|
||||
//
|
||||
// `catch (e) { body }`
|
||||
// LexicalScopeEmitter lse(this);
|
||||
// lse.emitScope(ScopeKind::SimpleCatch, scopeBinding);
|
||||
// emit(body);
|
||||
// lse.emitEnd();
|
||||
//
|
||||
// `catch ([a, b]) { body }`
|
||||
// LexicalScopeEmitter lse(this);
|
||||
// lse.emitScope(ScopeKind::Catch, scopeBinding);
|
||||
// emit(body);
|
||||
// lse.emitEnd();
|
||||
class MOZ_STACK_CLASS LexicalScopeEmitter
|
||||
{
|
||||
BytecodeEmitter* bce_;
|
||||
|
||||
mozilla::Maybe<TDZCheckCache> tdzCache_;
|
||||
mozilla::Maybe<EmitterScope> emitterScope_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+ emitScope +-------+ emitEnd +-----+
|
||||
// | Start |----------->| Scope |--------->| End |
|
||||
// +-------+ +-------+ +-----+
|
||||
enum class State {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling emitScope/emitEmptyScope.
|
||||
Scope,
|
||||
|
||||
// After calling emitEnd.
|
||||
End,
|
||||
};
|
||||
State state_ = State::Start;
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit LexicalScopeEmitter(BytecodeEmitter* bce);
|
||||
|
||||
// Returns the scope object for non-empty scope.
|
||||
const EmitterScope& emitterScope() const {
|
||||
return *emitterScope_;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool emitScope(ScopeKind kind,
|
||||
JS::Handle<LexicalScope::Data*> bindings);
|
||||
MOZ_MUST_USE bool emitEmptyScope();
|
||||
|
||||
MOZ_MUST_USE bool emitEnd();
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_LexicalScopeEmitter_h */
|
||||
|
|
@ -83,6 +83,7 @@ class NameResolver
|
|||
}
|
||||
|
||||
case PNK_NAME:
|
||||
case PNK_PRIVATE_NAME:
|
||||
*foundName = true;
|
||||
return buf->append(n->as<NameNode>().atom());
|
||||
|
||||
|
|
@ -136,6 +137,7 @@ class NameResolver
|
|||
return cur;
|
||||
|
||||
switch (cur->getKind()) {
|
||||
case PNK_PRIVATE_NAME:
|
||||
case PNK_NAME: return cur; /* found the initialized declaration */
|
||||
case PNK_THIS: return cur; /* Setting a property of 'this'. */
|
||||
case PNK_FUNCTION: return nullptr; /* won't find an assignment or declaration */
|
||||
|
|
@ -404,6 +406,7 @@ class NameResolver
|
|||
break;
|
||||
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_PRIVATE_NAME:
|
||||
case PNK_STRING:
|
||||
case PNK_TEMPLATE_STRING:
|
||||
MOZ_ASSERT(cur->is<NameNode>());
|
||||
|
|
@ -498,6 +501,19 @@ class NameResolver
|
|||
break;
|
||||
}
|
||||
|
||||
case PNK_CLASSFIELD: {
|
||||
ClassField* node = &cur->as<ClassField>();
|
||||
if (!resolve(&node->name(), prefix)) {
|
||||
return false;
|
||||
}
|
||||
if (ParseNode* init = node->initializer()) {
|
||||
if (!resolve(init, prefix)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_ELEM: {
|
||||
PropertyByValue* elem = &cur->as<PropertyByValue>();
|
||||
if (!elem->isSuper() && !resolve(&elem->expression(), prefix))
|
||||
|
|
@ -643,7 +659,7 @@ class NameResolver
|
|||
if (!resolve(heritage, prefix))
|
||||
return false;
|
||||
}
|
||||
if (!resolve(classNode->methodList(), prefix))
|
||||
if (!resolve(classNode->memberList(), prefix))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -757,7 +773,7 @@ class NameResolver
|
|||
}
|
||||
|
||||
case PNK_OBJECT:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSMEMBERLIST:
|
||||
for (ParseNode* element : cur->as<ListNode>().contents()) {
|
||||
if (!resolve(element, prefix))
|
||||
return false;
|
||||
|
|
|
|||
899
js/src/frontend/ObjectEmitter.cpp
Normal file
899
js/src/frontend/ObjectEmitter.cpp
Normal file
|
|
@ -0,0 +1,899 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "frontend/ObjectEmitter.h"
|
||||
|
||||
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
||||
|
||||
#include "jsatominlines.h" // AtomToId
|
||||
#include "jsgcinlines.h" // GetGCObjectKind
|
||||
#include "jsobjinlines.h" // NewBuiltinClassInstance
|
||||
|
||||
|
||||
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
|
||||
#include "frontend/SharedContext.h" // SharedContext
|
||||
#include "frontend/SourceNotes.h" // SRC_*
|
||||
#include "gc/Heap.h" // AllocKind
|
||||
#include "js/Id.h" // jsid
|
||||
#include "js/Value.h" // UndefinedHandleValue
|
||||
#include "vm/NativeObject.h" // NativeDefineDataProperty
|
||||
#include "vm/ObjectGroup.h" // TenuredObject
|
||||
#include "vm/Runtime.h" // JSAtomState (cx->names())
|
||||
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
using mozilla::Maybe;
|
||||
|
||||
PropertyEmitter::PropertyEmitter(BytecodeEmitter* bce)
|
||||
: bce_(bce), obj_(bce->cx) {}
|
||||
|
||||
bool PropertyEmitter::prepareForProtoValue(const Maybe<uint32_t>& keyPos)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
|
||||
if (keyPos) {
|
||||
if (!bce_->updateSourceCoordNotes(*keyPos))
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::ProtoValue;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitMutateProto()
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::ProtoValue);
|
||||
|
||||
// [stack] OBJ PROTO
|
||||
|
||||
if (!bce_->emit1(JSOP_MUTATEPROTO)) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
obj_ = nullptr;
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::Init;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::prepareForSpreadOperand(const Maybe<uint32_t>& spreadPos)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
|
||||
// [stack] OBJ
|
||||
|
||||
if (spreadPos) {
|
||||
if (!bce_->updateSourceCoordNotes(*spreadPos))
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_DUP)) {
|
||||
// [stack] OBJ OBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::SpreadOperand;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitSpread()
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::SpreadOperand);
|
||||
|
||||
// [stack] OBJ OBJ VAL
|
||||
|
||||
if (!bce_->emitCopyDataProperties(BytecodeEmitter::CopyOption::Unfiltered)) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
obj_ = nullptr;
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::Init;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool PropertyEmitter::prepareForProp(const Maybe<uint32_t>& keyPos,
|
||||
bool isStatic, bool isIndexOrComputed)
|
||||
{
|
||||
isStatic_ = isStatic;
|
||||
isIndexOrComputed_ = isIndexOrComputed;
|
||||
|
||||
// [stack] CTOR? OBJ
|
||||
|
||||
if (keyPos) {
|
||||
if (!bce_->updateSourceCoordNotes(*keyPos))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isStatic_) {
|
||||
if (!bce_->emit1(JSOP_DUP2)) {
|
||||
// [stack] CTOR HOMEOBJ CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack] CTOR HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::prepareForPropValue(const Maybe<uint32_t>& keyPos,
|
||||
Kind kind /* = Kind::Prototype */)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
|
||||
// [stack] CTOR? OBJ
|
||||
|
||||
if (!prepareForProp(keyPos,
|
||||
/* isStatic_ = */ kind == Kind::Static,
|
||||
/* isIndexOrComputed = */ false)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::PropValue;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::prepareForIndexPropKey(const Maybe<uint32_t>& keyPos,
|
||||
Kind kind /* = Kind::Prototype */)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
|
||||
// [stack] CTOR? OBJ
|
||||
|
||||
obj_ = nullptr;
|
||||
|
||||
if (!prepareForProp(keyPos,
|
||||
/* isStatic_ = */ kind == Kind::Static,
|
||||
/* isIndexOrComputed = */ true)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::IndexKey;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::prepareForIndexPropValue()
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::IndexKey);
|
||||
|
||||
// [stack] CTOR? OBJ CTOR? KEY
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::IndexValue;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::prepareForComputedPropKey(const Maybe<uint32_t>& keyPos,
|
||||
Kind kind /* = Kind::Prototype */)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
|
||||
// [stack] CTOR? OBJ
|
||||
|
||||
obj_ = nullptr;
|
||||
|
||||
if (!prepareForProp(keyPos,
|
||||
/* isStatic_ = */ kind == Kind::Static,
|
||||
/* isIndexOrComputed = */ true)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::ComputedKey;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::prepareForComputedPropValue()
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::ComputedKey);
|
||||
|
||||
// [stack] CTOR? OBJ CTOR? KEY
|
||||
|
||||
if (!bce_->emit1(JSOP_TOID)) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::ComputedValue;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitHomeObject(FunctionAsyncKind kind /* = FunctionAsyncKind::SyncFunction */)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
|
||||
propertyState_ == PropertyState::IndexValue ||
|
||||
propertyState_ == PropertyState::ComputedValue);
|
||||
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
|
||||
|
||||
bool isAsync = kind == FunctionAsyncKind::AsyncFunction;
|
||||
if (isAsync) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? UNWRAPPED WRAPPED
|
||||
if (!bce_->emit1(JSOP_SWAP)) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED UNWRAPPED
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bce_->emit2(JSOP_INITHOMEOBJECT, isIndexOrComputed_ + isAsync)) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED? FUN
|
||||
return false;
|
||||
}
|
||||
if (isAsync) {
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (propertyState_ == PropertyState::PropValue) {
|
||||
propertyState_ = PropertyState::InitHomeObj;
|
||||
} else if (propertyState_ == PropertyState::IndexValue) {
|
||||
propertyState_ = PropertyState::InitHomeObjForIndex;
|
||||
} else {
|
||||
propertyState_ = PropertyState::InitHomeObjForComputed;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitProp(JS::Handle<JSAtom*> key,
|
||||
bool isPropertyAnonFunctionOrClass /* = false */,
|
||||
JS::Handle<JSFunction*> anonFunction /* = nullptr */)
|
||||
{
|
||||
return emitInit(isClass_ ? JSOP_INITHIDDENPROP : JSOP_INITPROP, key,
|
||||
isPropertyAnonFunctionOrClass, anonFunction);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitGetter(JS::Handle<JSAtom*> key)
|
||||
{
|
||||
obj_ = nullptr;
|
||||
return emitInit(isClass_ ? JSOP_INITHIDDENPROP_GETTER : JSOP_INITPROP_GETTER,
|
||||
key, false, nullptr);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitSetter(JS::Handle<JSAtom*> key)
|
||||
{
|
||||
obj_ = nullptr;
|
||||
return emitInit(isClass_ ? JSOP_INITHIDDENPROP_SETTER : JSOP_INITPROP_SETTER,
|
||||
key, false, nullptr);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitIndexProp(bool isPropertyAnonFunctionOrClass /* = false */)
|
||||
{
|
||||
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
|
||||
FunctionPrefixKind::None,
|
||||
isPropertyAnonFunctionOrClass);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitIndexGetter()
|
||||
{
|
||||
obj_ = nullptr;
|
||||
return emitInitIndexOrComputed(
|
||||
isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
|
||||
FunctionPrefixKind::Get, false);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitIndexSetter()
|
||||
{
|
||||
obj_ = nullptr;
|
||||
return emitInitIndexOrComputed(
|
||||
isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
|
||||
FunctionPrefixKind::Set, false);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitComputedProp(bool isPropertyAnonFunctionOrClass /* = false */)
|
||||
{
|
||||
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
|
||||
FunctionPrefixKind::None,
|
||||
isPropertyAnonFunctionOrClass);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitComputedGetter()
|
||||
{
|
||||
obj_ = nullptr;
|
||||
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
|
||||
FunctionPrefixKind::Get, true);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitComputedSetter()
|
||||
{
|
||||
obj_ = nullptr;
|
||||
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
|
||||
FunctionPrefixKind::Set, true);
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInit(JSOp op, JS::Handle<JSAtom*> key,
|
||||
bool isPropertyAnonFunctionOrClass,
|
||||
JS::Handle<JSFunction*> anonFunction)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
|
||||
propertyState_ == PropertyState::InitHomeObj);
|
||||
|
||||
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP ||
|
||||
op == JSOP_INITPROP_GETTER || op == JSOP_INITHIDDENPROP_GETTER ||
|
||||
op == JSOP_INITPROP_SETTER || op == JSOP_INITHIDDENPROP_SETTER);
|
||||
|
||||
// [stack] CTOR? OBJ CTOR? VAL
|
||||
|
||||
uint32_t index;
|
||||
if (!bce_->makeAtomIndex(key, &index))
|
||||
return false;
|
||||
|
||||
if (obj_) {
|
||||
MOZ_ASSERT(!IsHiddenInitOp(op));
|
||||
MOZ_ASSERT(!obj_->inDictionaryMode());
|
||||
JS::RootedId id(bce_->cx, AtomToId(key));
|
||||
if (!NativeDefineProperty(bce_->cx, obj_, id, UndefinedHandleValue, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
return false;
|
||||
if (obj_->inDictionaryMode())
|
||||
obj_ = nullptr;
|
||||
}
|
||||
|
||||
if (isPropertyAnonFunctionOrClass) {
|
||||
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP);
|
||||
|
||||
if (anonFunction) {
|
||||
if (!bce_->setFunName(anonFunction, key))
|
||||
return false;
|
||||
} else {
|
||||
// NOTE: This is setting the constructor's name of the class which is
|
||||
// the property value. Not of the enclosing class.
|
||||
if (!bce_->emitSetClassConstructorName(key)) {
|
||||
// [stack] CTOR? OBJ CTOR? FUN
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bce_->emitIndex32(op, index)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitPopClassConstructor())
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::Init;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitInitIndexOrComputed(JSOp op, FunctionPrefixKind prefixKind,
|
||||
bool isPropertyAnonFunctionOrClass)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::IndexValue ||
|
||||
propertyState_ == PropertyState::InitHomeObjForIndex ||
|
||||
propertyState_ == PropertyState::ComputedValue ||
|
||||
propertyState_ == PropertyState::InitHomeObjForComputed);
|
||||
|
||||
MOZ_ASSERT(op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM ||
|
||||
op == JSOP_INITELEM_GETTER || op == JSOP_INITHIDDENELEM_GETTER ||
|
||||
op == JSOP_INITELEM_SETTER || op == JSOP_INITHIDDENELEM_SETTER);
|
||||
|
||||
// [stack] CTOR? OBJ CTOR? KEY VAL
|
||||
|
||||
if (isPropertyAnonFunctionOrClass) {
|
||||
if (!bce_->emitDupAt(1)) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY FUN FUN
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY FUN
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bce_->emit1(op)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitPopClassConstructor())
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
propertyState_ = PropertyState::Init;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEmitter::emitPopClassConstructor()
|
||||
{
|
||||
if (isStatic_) {
|
||||
// [stack] CTOR HOMEOBJ CTOR
|
||||
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ObjectEmitter::ObjectEmitter(BytecodeEmitter* bce) : PropertyEmitter(bce) {}
|
||||
|
||||
bool ObjectEmitter::emitObject(size_t propertyCount)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(objectState_ == ObjectState::Start);
|
||||
|
||||
// [stack]
|
||||
|
||||
// Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
|
||||
// a new object and defining (in source order) each property on the object
|
||||
// (or mutating the object's [[Prototype]], in the case of __proto__).
|
||||
top_ = bce_->offset();
|
||||
if (!bce_->emitNewInit(JSProto_Object)) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to construct the shape of the object as we go, so we can emit a
|
||||
// JSOP_NEWOBJECT with the final shape instead.
|
||||
// In the case of computed property names and indices, we cannot fix the
|
||||
// shape at bytecode compile time. When the shape cannot be determined,
|
||||
// |obj| is nulled out.
|
||||
|
||||
// No need to do any guessing for the object kind, since we know the upper
|
||||
// bound of how many properties we plan to have.
|
||||
gc::AllocKind kind = gc::GetGCObjectKind(propertyCount);
|
||||
obj_ = NewBuiltinClassInstance<PlainObject>(bce_->cx, kind, TenuredObject);
|
||||
if (!obj_)
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
objectState_ = ObjectState::Object;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::prepareForFieldInitializers(size_t numFields, bool isStatic)
|
||||
{
|
||||
MOZ_ASSERT_IF(!isStatic, classState_ == ClassState::Class);
|
||||
MOZ_ASSERT_IF(isStatic, classState_ == ClassState::InitConstructor);
|
||||
MOZ_ASSERT(fieldState_ == FieldState::Start);
|
||||
|
||||
// .initializers is a variable that stores an array of lambdas containing
|
||||
// code (the initializer) for each field. Upon an object's construction,
|
||||
// these lambdas will be called, defining the values.
|
||||
HandlePropertyName initializers = isStatic ? bce_->cx->names().dotStaticInitializers
|
||||
: bce_->cx->names().dotInitializers;
|
||||
initializersAssignment_.emplace(bce_, initializers,
|
||||
NameOpEmitter::Kind::Initialize);
|
||||
if (!initializersAssignment_->prepareForRhs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emitUint32Operand(JSOP_NEWARRAY, numFields)) {
|
||||
// [stack] ARRAY
|
||||
return false;
|
||||
}
|
||||
|
||||
fieldIndex_ = 0;
|
||||
#ifdef DEBUG
|
||||
if (isStatic) {
|
||||
classState_ = ClassState::StaticFieldInitializers;
|
||||
} else {
|
||||
classState_ = ClassState::InstanceFieldInitializers;
|
||||
}
|
||||
numFields_ = numFields;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::prepareForFieldInitializer()
|
||||
{
|
||||
MOZ_ASSERT(classState_ == ClassState::InstanceFieldInitializers ||
|
||||
classState_ == ClassState::StaticFieldInitializers);
|
||||
MOZ_ASSERT(fieldState_ == FieldState::Start);
|
||||
|
||||
#ifdef DEBUG
|
||||
fieldState_ = FieldState::Initializer;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitFieldInitializerHomeObject(bool isStatic)
|
||||
{
|
||||
MOZ_ASSERT(fieldState_ == FieldState::Initializer);
|
||||
// [stack] OBJ HERITAGE? ARRAY METHOD
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY METHOD
|
||||
uint8_t ofs = isStatic ? 2
|
||||
// [stack] CTOR HOMEOBJ ARRAY METHOD CTOR
|
||||
: isDerived_ ? 2 : 1;
|
||||
// [stack] OBJ HERITAGE? ARRAY METHOD OBJ
|
||||
if (!bce_->emit2(JSOP_INITHOMEOBJECT, ofs)) {
|
||||
// [stack] OBJ HERITAGE? ARRAY METHOD
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY METHOD
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
fieldState_ = FieldState::InitializerWithHomeObject;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitStoreFieldInitializer()
|
||||
{
|
||||
MOZ_ASSERT(fieldState_ == FieldState::Initializer ||
|
||||
fieldState_ == FieldState::InitializerWithHomeObject);
|
||||
MOZ_ASSERT(fieldIndex_ < numFields_);
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY METHOD
|
||||
|
||||
if (!bce_->emitUint32Operand(JSOP_INITELEM_ARRAY, fieldIndex_)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY
|
||||
return false;
|
||||
}
|
||||
|
||||
fieldIndex_++;
|
||||
#ifdef DEBUG
|
||||
fieldState_ = FieldState::Start;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitFieldInitializersEnd()
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
MOZ_ASSERT(classState_ == ClassState::InstanceFieldInitializers ||
|
||||
classState_ == ClassState::StaticFieldInitializers);
|
||||
MOZ_ASSERT(fieldState_ == FieldState::Start);
|
||||
MOZ_ASSERT(fieldIndex_ == numFields_);
|
||||
|
||||
if (!initializersAssignment_->emitAssignment()) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY
|
||||
return false;
|
||||
}
|
||||
initializersAssignment_.reset();
|
||||
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack] HOMEOBJ HERITAGE?
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (classState_ == ClassState::InstanceFieldInitializers) {
|
||||
classState_ = ClassState::InstanceFieldInitializersEnd;
|
||||
} else {
|
||||
classState_ = ClassState::StaticFieldInitializersEnd;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectEmitter::emitEnd()
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
MOZ_ASSERT(objectState_ == ObjectState::Object);
|
||||
|
||||
// [stack] OBJ
|
||||
|
||||
if (obj_) {
|
||||
// The object survived and has a predictable shape: update the original
|
||||
// bytecode.
|
||||
if (!bce_->replaceNewInitWithNewObject(obj_, top_)) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
objectState_ = ObjectState::End;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoSaveLocalStrictMode::AutoSaveLocalStrictMode(SharedContext* sc) : sc_(sc)
|
||||
{
|
||||
savedStrictness_ = sc_->setLocalStrictMode(true);
|
||||
}
|
||||
|
||||
AutoSaveLocalStrictMode::~AutoSaveLocalStrictMode()
|
||||
{
|
||||
if (sc_) {
|
||||
restore();
|
||||
}
|
||||
}
|
||||
|
||||
void AutoSaveLocalStrictMode::restore()
|
||||
{
|
||||
MOZ_ALWAYS_TRUE(sc_->setLocalStrictMode(savedStrictness_));
|
||||
sc_ = nullptr;
|
||||
}
|
||||
|
||||
ClassEmitter::ClassEmitter(BytecodeEmitter* bce)
|
||||
: PropertyEmitter(bce), strictMode_(bce->sc), name_(bce->cx)
|
||||
{
|
||||
isClass_ = true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitScope(JS::Handle<LexicalScope::Data*> scopeBindings)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(classState_ == ClassState::Start);
|
||||
|
||||
tdzCache_.emplace(bce_);
|
||||
|
||||
innerScope_.emplace(bce_);
|
||||
if (!innerScope_->enterLexical(bce_, ScopeKind::Lexical, scopeBindings))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::Scope;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitClass(JS::Handle<JSAtom*> name)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(classState_ == ClassState::Start ||
|
||||
classState_ == ClassState::Scope);
|
||||
|
||||
// [stack]
|
||||
|
||||
setName(name);
|
||||
isDerived_ = false;
|
||||
|
||||
if (!bce_->emitNewInit(JSProto_Object)) {
|
||||
// [stack] HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::Class;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitDerivedClass(JS::Handle<JSAtom*> name)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(classState_ == ClassState::Start ||
|
||||
classState_ == ClassState::Scope);
|
||||
|
||||
// [stack] HERITAGE
|
||||
|
||||
setName(name);
|
||||
isDerived_ = true;
|
||||
|
||||
if (!bce_->emit1(JSOP_CLASSHERITAGE)) {
|
||||
// [stack] funcProto objProto
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_OBJWITHPROTO)) {
|
||||
// [stack] funcProto HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
// JSOP_CLASSHERITAGE leaves both protos on the stack. After
|
||||
// creating the prototype, swap it to the bottom to make the
|
||||
// constructor.
|
||||
if (!bce_->emit1(JSOP_SWAP)) {
|
||||
// [stack] HOMEOBJ funcProto
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::Class;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassEmitter::setName(JS::Handle<JSAtom*> name)
|
||||
{
|
||||
name_ = name;
|
||||
if (!name_)
|
||||
name_ = bce_->cx->names().empty;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitInitConstructor(bool needsHomeObject)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(classState_ == ClassState::Class ||
|
||||
classState_ == ClassState::InstanceFieldInitializersEnd);
|
||||
|
||||
// [stack] HOMEOBJ CTOR
|
||||
|
||||
if (needsHomeObject) {
|
||||
if (!bce_->emit2(JSOP_INITHOMEOBJECT, 0)) {
|
||||
// [stack] HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!initProtoAndCtor()) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::InitConstructor;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitInitDefaultConstructor(const Maybe<uint32_t>& classStart,
|
||||
const Maybe<uint32_t>& classEnd)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(classState_ == ClassState::Class);
|
||||
|
||||
if (classStart && classEnd) {
|
||||
// In the case of default class constructors, emit the start and end
|
||||
// offsets in the source buffer as source notes so that when we
|
||||
// actually make the constructor during execution, we can give it the
|
||||
// correct toString output.
|
||||
if (!bce_->newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(*classStart),
|
||||
ptrdiff_t(*classEnd))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDerived_) {
|
||||
// [stack] HERITAGE PROTO
|
||||
if (!bce_->emitAtomOp(name_, JSOP_DERIVEDCONSTRUCTOR)) {
|
||||
// [stack] HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// [stack] HOMEOBJ
|
||||
if (!bce_->emitAtomOp(name_, JSOP_CLASSCONSTRUCTOR)) {
|
||||
// [stack] HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!initProtoAndCtor()) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::InitConstructor;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::initProtoAndCtor()
|
||||
{
|
||||
// [stack] HOMEOBJ CTOR
|
||||
|
||||
if (!bce_->emit1(JSOP_SWAP)) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_DUP2)) {
|
||||
// [stack] CTOR HOMEOBJ CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emitAtomOp(bce_->cx->names().prototype, JSOP_INITLOCKEDPROP)) {
|
||||
// [stack] CTOR HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emitAtomOp(bce_->cx->names().constructor, JSOP_INITHIDDENPROP)) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitBinding()
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
MOZ_ASSERT(classState_ == ClassState::InitConstructor ||
|
||||
classState_ == ClassState::InstanceFieldInitializersEnd ||
|
||||
classState_ == ClassState::StaticFieldInitializersEnd);
|
||||
|
||||
// [stack] CTOR HOMEOBJ
|
||||
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack] CTOR
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name_ != bce_->cx->names().empty) {
|
||||
MOZ_ASSERT(innerScope_.isSome());
|
||||
|
||||
if (!bce_->emitLexicalInitialization(name_)) {
|
||||
// [stack] CTOR
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// [stack] CTOR
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::BoundName;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassEmitter::emitEnd(Kind kind)
|
||||
{
|
||||
MOZ_ASSERT(classState_ == ClassState::BoundName);
|
||||
// [stack] CTOR
|
||||
|
||||
if (innerScope_.isSome()) {
|
||||
MOZ_ASSERT(tdzCache_.isSome());
|
||||
|
||||
if (!innerScope_->leave(bce_))
|
||||
return false;
|
||||
innerScope_.reset();
|
||||
tdzCache_.reset();
|
||||
} else {
|
||||
MOZ_ASSERT(kind == Kind::Expression);
|
||||
MOZ_ASSERT(tdzCache_.isNothing());
|
||||
}
|
||||
|
||||
if (kind == Kind::Declaration) {
|
||||
MOZ_ASSERT(name_);
|
||||
|
||||
if (!bce_->emitLexicalInitialization(name_)) {
|
||||
// [stack] CTOR
|
||||
return false;
|
||||
}
|
||||
// Only class statements make outer bindings, and they do not leave
|
||||
// themselves on the stack.
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack]
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// [stack] # class declaration
|
||||
// [stack]
|
||||
// [stack] # class expression
|
||||
// [stack] CTOR
|
||||
|
||||
strictMode_.restore();
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::End;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
859
js/src/frontend/ObjectEmitter.h
Normal file
859
js/src/frontend/ObjectEmitter.h
Normal file
|
|
@ -0,0 +1,859 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef frontend_ObjectEmitter_h
|
||||
#define frontend_ObjectEmitter_h
|
||||
|
||||
#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS, MOZ_ALWAYS_INLINE, MOZ_RAII
|
||||
#include "mozilla/Maybe.h" // Maybe
|
||||
|
||||
#include <stddef.h> // size_t, ptrdiff_t
|
||||
#include <stdint.h> // uint32_t
|
||||
|
||||
#include "jsopcode.h" // JSOp
|
||||
#include "jsfun.h" // JSFunction
|
||||
#include "jsscript.h" // FunctionAsyncKind
|
||||
|
||||
#include "frontend/EmitterScope.h" // EmitterScope
|
||||
#include "frontend/NameOpEmitter.h" // NameOpEmitter
|
||||
#include "frontend/TDZCheckCache.h" // TDZCheckCache
|
||||
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
|
||||
#include "vm/String.h" // JSAtom
|
||||
#include "vm/NativeObject.h" // PlainObject
|
||||
#include "vm/Scope.h" // LexicalScope
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace frontend {
|
||||
|
||||
struct BytecodeEmitter;
|
||||
class SharedContext;
|
||||
|
||||
// Class for emitting bytecode for object and class properties.
|
||||
// See ObjectEmitter and ClassEmitter for usage.
|
||||
class MOZ_STACK_CLASS PropertyEmitter
|
||||
{
|
||||
public:
|
||||
enum class Kind {
|
||||
// Prototype property.
|
||||
Prototype,
|
||||
|
||||
// Class static property.
|
||||
Static
|
||||
};
|
||||
|
||||
protected:
|
||||
BytecodeEmitter* bce_;
|
||||
|
||||
// True if the object is class.
|
||||
// Set by ClassEmitter.
|
||||
bool isClass_ = false;
|
||||
|
||||
// True if the property is class static method.
|
||||
bool isStatic_ = false;
|
||||
|
||||
// True if the property has computed or index key.
|
||||
bool isIndexOrComputed_ = false;
|
||||
|
||||
// An object which keeps the shape of this object literal.
|
||||
// This fields is reset to nullptr whenever the object literal turns out to
|
||||
// have at least one numeric, computed, spread or __proto__ property, or
|
||||
// the object becomes dictionary mode.
|
||||
// This field is used only in ObjectEmitter.
|
||||
JS::Rooted<PlainObject*> obj_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+
|
||||
// | Start |-+
|
||||
// +-------+ |
|
||||
// |
|
||||
// +---------+
|
||||
// |
|
||||
// | +------------------------------------------------------------+
|
||||
// | | |
|
||||
// | | [normal property/method/accessor] |
|
||||
// | v prepareForPropValue +-----------+ +------+ |
|
||||
// +->+----------------------->| PropValue |-+ +->| Init |-+
|
||||
// | +-----------+ | | +------+
|
||||
// | | |
|
||||
// | +----------------------------------+ +-----------+
|
||||
// | | |
|
||||
// | +-+---------------------------------------+ |
|
||||
// | | | |
|
||||
// | | [method with super] | |
|
||||
// | | emitInitHomeObject +-------------+ v |
|
||||
// | +--------------------->| InitHomeObj |->+ |
|
||||
// | +-------------+ | |
|
||||
// | | |
|
||||
// | +-------------------------------------- + |
|
||||
// | | |
|
||||
// | | emitInitProp |
|
||||
// | | emitInitGetter |
|
||||
// | | emitInitSetter |
|
||||
// | +------------------------------------------------------>+
|
||||
// | ^
|
||||
// | [index property/method/accessor] |
|
||||
// | prepareForIndexPropKey +----------+ |
|
||||
// +-------------------------->| IndexKey |-+ |
|
||||
// | +----------+ | |
|
||||
// | | |
|
||||
// | +-------------------------------------+ |
|
||||
// | | |
|
||||
// | | prepareForIndexPropValue +------------+ |
|
||||
// | +------------------------->| IndexValue |-+ |
|
||||
// | +------------+ | |
|
||||
// | | |
|
||||
// | +---------------------------------------+ |
|
||||
// | | |
|
||||
// | +-+--------------------------------------------------+ |
|
||||
// | | | |
|
||||
// | | [method with super] | |
|
||||
// | | emitInitHomeObject +---------------------+ v |
|
||||
// | +--------------------->| InitHomeObjForIndex |---->+ |
|
||||
// | +---------------------+ | |
|
||||
// | | |
|
||||
// | +--------------------------------------------------+ |
|
||||
// | | |
|
||||
// | | emitInitIndexProp |
|
||||
// | | emitInitIndexGetter |
|
||||
// | | emitInitIndexSetter |
|
||||
// | +---------------------------------------------------->+
|
||||
// | |
|
||||
// | [computed property/method/accessor] |
|
||||
// | prepareForComputedPropKey +-------------+ |
|
||||
// +----------------------------->| ComputedKey |-+ |
|
||||
// | +-------------+ | |
|
||||
// | | |
|
||||
// | +-------------------------------------------+ |
|
||||
// | | |
|
||||
// | | prepareForComputedPropValue +---------------+ |
|
||||
// | +---------------------------->| ComputedValue |-+ |
|
||||
// | +---------------+ | |
|
||||
// | | |
|
||||
// | +---------------------------------------------+ |
|
||||
// | | |
|
||||
// | +-+--------------------------------------------------+ |
|
||||
// | | | |
|
||||
// | | [method with super] | |
|
||||
// | | emitInitHomeObject +------------------------+ v |
|
||||
// | +--------------------->| InitHomeObjForComputed |->+ |
|
||||
// | +------------------------+ | |
|
||||
// | | |
|
||||
// | +--------------------------------------------------+ |
|
||||
// | | |
|
||||
// | | emitInitComputedProp |
|
||||
// | | emitInitComputedGetter |
|
||||
// | | emitInitComputedSetter |
|
||||
// | +---------------------------------------------------->+
|
||||
// | ^
|
||||
// | |
|
||||
// | [__proto__] |
|
||||
// | prepareForProtoValue +------------+ emitMutateProto |
|
||||
// +------------------------>| ProtoValue |-------------------->+
|
||||
// | +------------+ ^
|
||||
// | |
|
||||
// | [...prop] |
|
||||
// | prepareForSpreadOperand +---------------+ emitSpread |
|
||||
// +-------------------------->| SpreadOperand |----------------+
|
||||
// +---------------+
|
||||
enum class PropertyState {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling prepareForPropValue.
|
||||
PropValue,
|
||||
|
||||
// After calling emitInitHomeObject, from PropValue.
|
||||
InitHomeObj,
|
||||
|
||||
// After calling prepareForIndexPropKey.
|
||||
IndexKey,
|
||||
|
||||
// prepareForIndexPropValue.
|
||||
IndexValue,
|
||||
|
||||
// After calling emitInitHomeObject, from IndexValue.
|
||||
InitHomeObjForIndex,
|
||||
|
||||
// After calling prepareForComputedPropKey.
|
||||
ComputedKey,
|
||||
|
||||
// prepareForComputedPropValue.
|
||||
ComputedValue,
|
||||
|
||||
// After calling emitInitHomeObject, from ComputedValue.
|
||||
InitHomeObjForComputed,
|
||||
|
||||
// After calling prepareForProtoValue.
|
||||
ProtoValue,
|
||||
|
||||
// After calling prepareForSpreadOperand.
|
||||
SpreadOperand,
|
||||
|
||||
// After calling one of emitInitProp, emitInitGetter, emitInitSetter,
|
||||
// emitInitIndexOrComputedProp, emitInitIndexOrComputedGetter,
|
||||
// emitInitIndexOrComputedSetter, emitMutateProto, or emitSpread.
|
||||
Init,
|
||||
};
|
||||
PropertyState propertyState_ = PropertyState::Start;
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit PropertyEmitter(BytecodeEmitter* bce);
|
||||
|
||||
// Parameters are the offset in the source code for each character below:
|
||||
//
|
||||
// { __proto__: protoValue }
|
||||
// ^
|
||||
// |
|
||||
// keyPos
|
||||
MOZ_MUST_USE bool prepareForProtoValue(
|
||||
const mozilla::Maybe<uint32_t>& keyPos);
|
||||
MOZ_MUST_USE bool emitMutateProto();
|
||||
|
||||
// { ...obj }
|
||||
// ^
|
||||
// |
|
||||
// spreadPos
|
||||
MOZ_MUST_USE bool prepareForSpreadOperand(
|
||||
const mozilla::Maybe<uint32_t>& spreadPos);
|
||||
MOZ_MUST_USE bool emitSpread();
|
||||
|
||||
// { key: value }
|
||||
// ^
|
||||
// |
|
||||
// keyPos
|
||||
MOZ_MUST_USE bool prepareForPropValue(const mozilla::Maybe<uint32_t>& keyPos,
|
||||
Kind kind = Kind::Prototype);
|
||||
|
||||
// { 1: value }
|
||||
// ^
|
||||
// |
|
||||
// keyPos
|
||||
MOZ_MUST_USE bool prepareForIndexPropKey(
|
||||
const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype);
|
||||
MOZ_MUST_USE bool prepareForIndexPropValue();
|
||||
|
||||
// { [ key ]: value }
|
||||
// ^
|
||||
// |
|
||||
// keyPos
|
||||
MOZ_MUST_USE bool prepareForComputedPropKey(
|
||||
const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype);
|
||||
MOZ_MUST_USE bool prepareForComputedPropValue();
|
||||
|
||||
MOZ_MUST_USE bool emitInitHomeObject(
|
||||
FunctionAsyncKind kind = FunctionAsyncKind::SyncFunction);
|
||||
|
||||
// @param key
|
||||
// Property key
|
||||
// @param isPropertyAnonFunctionOrClass
|
||||
// True if the property value is an anonymous function or
|
||||
// an anonymous class
|
||||
// @param anonFunction
|
||||
// The anonymous function object for property value
|
||||
MOZ_MUST_USE bool emitInitProp(
|
||||
JS::Handle<JSAtom*> key, bool isPropertyAnonFunctionOrClass = false,
|
||||
JS::Handle<JSFunction*> anonFunction = nullptr);
|
||||
MOZ_MUST_USE bool emitInitGetter(JS::Handle<JSAtom*> key);
|
||||
MOZ_MUST_USE bool emitInitSetter(JS::Handle<JSAtom*> key);
|
||||
|
||||
MOZ_MUST_USE bool emitInitIndexProp(
|
||||
bool isPropertyAnonFunctionOrClass = false);
|
||||
MOZ_MUST_USE bool emitInitIndexGetter();
|
||||
MOZ_MUST_USE bool emitInitIndexSetter();
|
||||
|
||||
MOZ_MUST_USE bool emitInitComputedProp(
|
||||
bool isPropertyAnonFunctionOrClass = false);
|
||||
MOZ_MUST_USE bool emitInitComputedGetter();
|
||||
MOZ_MUST_USE bool emitInitComputedSetter();
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE MOZ_ALWAYS_INLINE bool prepareForProp(
|
||||
const mozilla::Maybe<uint32_t>& keyPos, bool isStatic, bool isComputed);
|
||||
|
||||
// @param op
|
||||
// Opcode for initializing property
|
||||
// @param prefixKind
|
||||
// None, Get, or Set
|
||||
// @param key
|
||||
// Atom of the property if the property key is not computed
|
||||
// @param isPropertyAnonFunctionOrClass
|
||||
// True if the property is either an anonymous function or an
|
||||
// anonymous class
|
||||
// @param anonFunction
|
||||
// Anonymous function object for the property
|
||||
MOZ_MUST_USE bool emitInit(JSOp op, JS::Handle<JSAtom*> key,
|
||||
bool isPropertyAnonFunctionOrClass,
|
||||
JS::Handle<JSFunction*> anonFunction);
|
||||
MOZ_MUST_USE bool emitInitIndexOrComputed(JSOp op,
|
||||
FunctionPrefixKind prefixKind,
|
||||
bool isPropertyAnonFunctionOrClass);
|
||||
|
||||
MOZ_MUST_USE bool emitPopClassConstructor();
|
||||
};
|
||||
|
||||
// Class for emitting bytecode for object literal.
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `{}`
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(0);
|
||||
// oe.emitEnd();
|
||||
//
|
||||
// `{ prop: 10 }`
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(1);
|
||||
//
|
||||
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||
// emit(10);
|
||||
// oe.emitInitProp(atom_of_prop);
|
||||
//
|
||||
// oe.emitEnd();
|
||||
//
|
||||
// `{ prop: function() {} }`, when property value is anonymous function
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(1);
|
||||
//
|
||||
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||
// emit(function);
|
||||
// oe.emitInitProp(atom_of_prop, true, function_object);
|
||||
//
|
||||
// oe.emitEnd();
|
||||
//
|
||||
// `{ get prop() { ... }, set prop(v) { ... } }`
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(2);
|
||||
//
|
||||
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||
// emit(function_for_getter);
|
||||
// oe.emitInitGetter(atom_of_prop);
|
||||
//
|
||||
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||
// emit(function_for_setter);
|
||||
// oe.emitInitSetter(atom_of_prop);
|
||||
//
|
||||
// oe.emitEnd();
|
||||
//
|
||||
// `{ 1: 10, get 2() { ... }, set 3(v) { ... } }`
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(3);
|
||||
//
|
||||
// oe.prepareForIndexPropKey(Some(offset_of_prop));
|
||||
// emit(1);
|
||||
// oe.prepareForIndexPropValue();
|
||||
// emit(10);
|
||||
// oe.emitInitIndexedProp(atom_of_prop);
|
||||
//
|
||||
// oe.prepareForIndexPropKey(Some(offset_of_opening_bracket));
|
||||
// emit(2);
|
||||
// oe.prepareForIndexPropValue();
|
||||
// emit(function_for_getter);
|
||||
// oe.emitInitIndexGetter();
|
||||
//
|
||||
// oe.prepareForIndexPropKey(Some(offset_of_opening_bracket));
|
||||
// emit(3);
|
||||
// oe.prepareForIndexPropValue();
|
||||
// emit(function_for_setter);
|
||||
// oe.emitInitIndexSetter();
|
||||
//
|
||||
// oe.emitEnd();
|
||||
//
|
||||
// `{ [prop1]: 10, get [prop2]() { ... }, set [prop3](v) { ... } }`
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(3);
|
||||
//
|
||||
// oe.prepareForComputedPropKey(Some(offset_of_opening_bracket));
|
||||
// emit(prop1);
|
||||
// oe.prepareForComputedPropValue();
|
||||
// emit(10);
|
||||
// oe.emitInitComputedProp();
|
||||
//
|
||||
// oe.prepareForComputedPropKey(Some(offset_of_opening_bracket));
|
||||
// emit(prop2);
|
||||
// oe.prepareForComputedPropValue();
|
||||
// emit(function_for_getter);
|
||||
// oe.emitInitComputedGetter();
|
||||
//
|
||||
// oe.prepareForComputedPropKey(Some(offset_of_opening_bracket));
|
||||
// emit(prop3);
|
||||
// oe.prepareForComputedPropValue();
|
||||
// emit(function_for_setter);
|
||||
// oe.emitInitComputedSetter();
|
||||
//
|
||||
// oe.emitEnd();
|
||||
//
|
||||
// `{ __proto__: obj }`
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(1);
|
||||
// oe.prepareForProtoValue(Some(offset_of___proto__));
|
||||
// emit(obj);
|
||||
// oe.emitMutateProto();
|
||||
// oe.emitEnd();
|
||||
//
|
||||
// `{ ...obj }`
|
||||
// ObjectEmitter oe(this);
|
||||
// oe.emitObject(1);
|
||||
// oe.prepareForSpreadOperand(Some(offset_of_triple_dots));
|
||||
// emit(obj);
|
||||
// oe.emitSpread();
|
||||
// oe.emitEnd();
|
||||
//
|
||||
class MOZ_STACK_CLASS ObjectEmitter : public PropertyEmitter
|
||||
{
|
||||
private:
|
||||
// The offset of JSOP_NEWINIT, which is replced by JSOP_NEWOBJECT later
|
||||
// when the object is known to have a fixed shape.
|
||||
ptrdiff_t top_ = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+ emitObject +--------+
|
||||
// | Start |----------->| Object |-+
|
||||
// +-------+ +--------+ |
|
||||
// |
|
||||
// +-----------------------------+
|
||||
// |
|
||||
// | (do PropertyEmitter operation) emitEnd +-----+
|
||||
// +-------------------------------+--------->| End |
|
||||
// +-----+
|
||||
enum class ObjectState {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling emitObject.
|
||||
Object,
|
||||
|
||||
// After calling emitEnd.
|
||||
End,
|
||||
};
|
||||
ObjectState objectState_ = ObjectState::Start;
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit ObjectEmitter(BytecodeEmitter* bce);
|
||||
|
||||
MOZ_MUST_USE bool emitObject(size_t propertyCount);
|
||||
MOZ_MUST_USE bool emitEnd();
|
||||
};
|
||||
|
||||
// Save and restore the strictness.
|
||||
// Used by class declaration/expression to temporarily enable strict mode.
|
||||
class MOZ_RAII AutoSaveLocalStrictMode
|
||||
{
|
||||
SharedContext* sc_;
|
||||
bool savedStrictness_;
|
||||
|
||||
public:
|
||||
explicit AutoSaveLocalStrictMode(SharedContext* sc);
|
||||
~AutoSaveLocalStrictMode();
|
||||
|
||||
// Force restore the strictness now.
|
||||
void restore();
|
||||
};
|
||||
|
||||
// Class for emitting bytecode for JS class.
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `class {}`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScope(scopeBindings);
|
||||
// ce.emitClass();
|
||||
//
|
||||
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
||||
// Some(offset_of_closing_bracket));
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class { constructor() { ... } }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScope(scopeBindings);
|
||||
// ce.emitClass();
|
||||
//
|
||||
// emit(function_for_constructor);
|
||||
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class X { constructor() { ... } }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScope(scopeBindings);
|
||||
// ce.emitClass(atom_of_X);
|
||||
//
|
||||
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
||||
// Some(offset_of_closing_bracket));
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class X { constructor() { ... } }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScope(scopeBindings);
|
||||
// ce.emitClass(atom_of_X);
|
||||
//
|
||||
// emit(function_for_constructor);
|
||||
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class X extends Y { constructor() { ... } }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScope(scopeBindings);
|
||||
//
|
||||
// emit(Y);
|
||||
// ce.emitDerivedClass(atom_of_X);
|
||||
//
|
||||
// emit(function_for_constructor);
|
||||
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class X extends Y { constructor() { ... super.f(); ... } }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScope(scopeBindings);
|
||||
//
|
||||
// emit(Y);
|
||||
// ce.emitDerivedClass(atom_of_X);
|
||||
//
|
||||
// emit(function_for_constructor);
|
||||
// // pass true if constructor contains super.prop access
|
||||
// ce.emitInitConstructor(/* needsHomeObject = */ true);
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class X extends Y { field0 = expr0; ... }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScope(scopeBindings);
|
||||
// emit(Y);
|
||||
// ce.emitDerivedClass(atom_of_X, nullptr, false);
|
||||
//
|
||||
// ce.prepareForFieldInitializers(fields.length());
|
||||
// for (auto field : fields) {
|
||||
// emit(field.initializer_method());
|
||||
// ce.emitStoreFieldInitializer();
|
||||
// }
|
||||
// ce.emitFieldInitializersEnd();
|
||||
//
|
||||
// emit(function_for_constructor);
|
||||
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class X { field0 = super.method(); ... }`
|
||||
// // after emitClass/emitDerivedClass
|
||||
// ce.prepareForFieldInitializers(1);
|
||||
// for (auto field : fields) {
|
||||
// emit(field.initializer_method());
|
||||
// if (field.initializer_contains_super_or_eval()) {
|
||||
// ce.emitFieldInitializerHomeObject();
|
||||
// }
|
||||
// ce.emitStoreFieldInitializer();
|
||||
// }
|
||||
// ce.emitFieldInitializersEnd();
|
||||
//
|
||||
// `m() {}` in class
|
||||
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||
// ce.prepareForPropValue(Some(offset_of_m));
|
||||
// emit(function_for_m);
|
||||
// ce.emitInitProp(atom_of_m);
|
||||
//
|
||||
// `m() { super.f(); }` in class
|
||||
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||
// ce.prepareForPropValue(Some(offset_of_m));
|
||||
// emit(function_for_m);
|
||||
// ce.emitInitHomeObject();
|
||||
// ce.emitInitProp(atom_of_m);
|
||||
//
|
||||
// `async m() { super.f(); }` in class
|
||||
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||
// ce.prepareForPropValue(Some(offset_of_m));
|
||||
// emit(function_for_m);
|
||||
// ce.emitInitHomeObject(FunctionAsyncKind::Async);
|
||||
// ce.emitInitProp(atom_of_m);
|
||||
//
|
||||
// `get p() { super.f(); }` in class
|
||||
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||
// ce.prepareForPropValue(Some(offset_of_p));
|
||||
// emit(function_for_p);
|
||||
// ce.emitInitHomeObject();
|
||||
// ce.emitInitGetter(atom_of_m);
|
||||
//
|
||||
// `static m() {}` in class
|
||||
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||
// ce.prepareForPropValue(Some(offset_of_m),
|
||||
// PropertyEmitter::Kind::Static);
|
||||
// emit(function_for_m);
|
||||
// ce.emitInitProp(atom_of_m);
|
||||
//
|
||||
// `static get [p]() { super.f(); }` in class
|
||||
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||
// ce.prepareForComputedPropValue(Some(offset_of_m),
|
||||
// PropertyEmitter::Kind::Static);
|
||||
// emit(p);
|
||||
// ce.prepareForComputedPropValue();
|
||||
// emit(function_for_m);
|
||||
// ce.emitInitHomeObject();
|
||||
// ce.emitInitComputedGetter();
|
||||
//
|
||||
class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter
|
||||
{
|
||||
public:
|
||||
enum class Kind {
|
||||
// Class expression.
|
||||
Expression,
|
||||
|
||||
// Class declaration.
|
||||
Declaration,
|
||||
};
|
||||
|
||||
private:
|
||||
// Pseudocode for class declarations:
|
||||
//
|
||||
// class extends BaseExpression {
|
||||
// constructor() { ... }
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if defined <BaseExpression> {
|
||||
// let heritage = BaseExpression;
|
||||
//
|
||||
// if (heritage !== null) {
|
||||
// funProto = heritage;
|
||||
// objProto = heritage.prototype;
|
||||
// } else {
|
||||
// funProto = %FunctionPrototype%;
|
||||
// objProto = null;
|
||||
// }
|
||||
// } else {
|
||||
// objProto = %ObjectPrototype%;
|
||||
// }
|
||||
//
|
||||
// let homeObject = ObjectCreate(objProto);
|
||||
//
|
||||
// if defined <constructor> {
|
||||
// if defined <BaseExpression> {
|
||||
// cons = DefineMethod(<constructor>, proto=homeObject,
|
||||
// funProto=funProto);
|
||||
// } else {
|
||||
// cons = DefineMethod(<constructor>, proto=homeObject);
|
||||
// }
|
||||
// } else {
|
||||
// if defined <BaseExpression> {
|
||||
// cons = DefaultDerivedConstructor(proto=homeObject,
|
||||
// funProto=funProto);
|
||||
// } else {
|
||||
// cons = DefaultConstructor(proto=homeObject);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// cons.prototype = homeObject;
|
||||
// homeObject.constructor = cons;
|
||||
//
|
||||
// EmitPropertyList(...)
|
||||
|
||||
bool isDerived_ = false;
|
||||
|
||||
mozilla::Maybe<TDZCheckCache> tdzCache_;
|
||||
mozilla::Maybe<EmitterScope> innerScope_;
|
||||
AutoSaveLocalStrictMode strictMode_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+
|
||||
// | Start |-+------------------------>+-+
|
||||
// +-------+ | ^ |
|
||||
// | [has scope] | |
|
||||
// | emitScope +-------+ | |
|
||||
// +-------------->| Scope |-+ |
|
||||
// +-------+ |
|
||||
// |
|
||||
// +-----------------------------------+
|
||||
// |
|
||||
// | emitClass +-------+
|
||||
// +-+----------------->+->| Class |-+
|
||||
// | ^ +-------+ |
|
||||
// | emitDerivedClass | |
|
||||
// +------------------+ |
|
||||
// |
|
||||
// +-------------------------------+
|
||||
// |
|
||||
// | prepareForFieldInitializers(isStatic = false)
|
||||
// +---------------+
|
||||
// | |
|
||||
// | +--------v------------------+
|
||||
// | | InstanceFieldInitializers |
|
||||
// | +---------------------------+
|
||||
// | |
|
||||
// | emitFieldInitializersEnd
|
||||
// | |
|
||||
// | +--------v---------------------+
|
||||
// | | InstanceFieldInitializersEnd |
|
||||
// | +------------------------------+
|
||||
// | |
|
||||
// +<--------------+
|
||||
// |
|
||||
// |
|
||||
// | emitInitConstructor +-----------------+
|
||||
// +-+--------------------------->+->| InitConstructor |-+
|
||||
// | ^ +-----------------+ |
|
||||
// | emitInitDefaultConstructor | |
|
||||
// +----------------------------+ |
|
||||
// |
|
||||
// +-----------------------------------------------------+
|
||||
// |
|
||||
// | prepareForFieldInitializers(isStatic = true)
|
||||
// +---------------+
|
||||
// | |
|
||||
// | +--------v----------------+
|
||||
// | | StaticFieldInitializers |
|
||||
// | +-------------------------+
|
||||
// | |
|
||||
// | | emitFieldInitializersEnd
|
||||
// | |
|
||||
// | +--------v-------------------+
|
||||
// | | StaticFieldInitializersEnd |
|
||||
// | +----------------------------+
|
||||
// | |
|
||||
// +<--------------+
|
||||
// |
|
||||
// | (do PropertyEmitter operation)
|
||||
// +--------------------------------+
|
||||
// |
|
||||
// +-------------+ emitBinding |
|
||||
// | BoundName |<-----------------+
|
||||
// +--+----------+
|
||||
// |
|
||||
// | emitEnd
|
||||
// |
|
||||
// +--v----+
|
||||
// | End |
|
||||
// +-------+
|
||||
//
|
||||
enum class ClassState {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling emitScope.
|
||||
Scope,
|
||||
|
||||
// After calling emitClass or emitDerivedClass.
|
||||
Class,
|
||||
|
||||
// After calling emitInitConstructor or emitInitDefaultConstructor.
|
||||
InitConstructor,
|
||||
|
||||
// After calling prepareForFieldInitializers(isStatic = false).
|
||||
InstanceFieldInitializers,
|
||||
|
||||
// After calling emitFieldInitializersEnd.
|
||||
InstanceFieldInitializersEnd,
|
||||
|
||||
// After calling prepareForFieldInitializers(isStatic = true).
|
||||
StaticFieldInitializers,
|
||||
|
||||
// After calling emitFieldInitializersEnd.
|
||||
StaticFieldInitializersEnd,
|
||||
|
||||
// After calling emitBinding.
|
||||
BoundName,
|
||||
|
||||
// After calling emitEnd.
|
||||
End,
|
||||
};
|
||||
ClassState classState_ = ClassState::Start;
|
||||
|
||||
// The state of the fields emitter.
|
||||
//
|
||||
// clang-format off
|
||||
//
|
||||
// +-------+
|
||||
// | Start +<-----------------------------+
|
||||
// +-------+ |
|
||||
// | |
|
||||
// | prepareForFieldInitializer | emitStoreFieldInitializer
|
||||
// v |
|
||||
// +-------------+ |
|
||||
// | Initializer +------------------------->+
|
||||
// +-------------+ |
|
||||
// | |
|
||||
// | emitFieldInitializerHomeObject |
|
||||
// v |
|
||||
// +---------------------------+ |
|
||||
// | InitializerWithHomeObject +------------+
|
||||
// +---------------------------+
|
||||
//
|
||||
// clang-format on
|
||||
enum class FieldState {
|
||||
// After calling prepareForFieldInitializers
|
||||
// and 0 or more calls to emitStoreFieldInitializer.
|
||||
Start,
|
||||
|
||||
// After calling prepareForFieldInitializer
|
||||
Initializer,
|
||||
|
||||
// After calling emitFieldInitializerHomeObject
|
||||
InitializerWithHomeObject,
|
||||
};
|
||||
FieldState fieldState_ = FieldState::Start;
|
||||
|
||||
size_t numFields_ = 0;
|
||||
#endif
|
||||
|
||||
JS::Rooted<JSAtom*> name_;
|
||||
mozilla::Maybe<NameOpEmitter> initializersAssignment_;
|
||||
size_t fieldIndex_ = 0;
|
||||
|
||||
public:
|
||||
explicit ClassEmitter(BytecodeEmitter* bce);
|
||||
|
||||
MOZ_MUST_USE bool emitScope(JS::Handle<LexicalScope::Data*> scopeBindings);
|
||||
|
||||
// @param name
|
||||
// Name of the class (nullptr if this is anonymous class)
|
||||
MOZ_MUST_USE bool emitClass(JS::Handle<JSAtom*> name);
|
||||
MOZ_MUST_USE bool emitDerivedClass(JS::Handle<JSAtom*> name);
|
||||
|
||||
// @param needsHomeObject
|
||||
// True if the constructor contains `super.foo`
|
||||
MOZ_MUST_USE bool emitInitConstructor(bool needsHomeObject);
|
||||
|
||||
// Parameters are the offset in the source code for each character below:
|
||||
//
|
||||
// class X { foo() {} }
|
||||
// ^ ^
|
||||
// | |
|
||||
// | classEnd
|
||||
// |
|
||||
// classStart
|
||||
//
|
||||
MOZ_MUST_USE bool emitInitDefaultConstructor(
|
||||
const mozilla::Maybe<uint32_t>& classStart,
|
||||
const mozilla::Maybe<uint32_t>& classEnd);
|
||||
|
||||
MOZ_MUST_USE bool prepareForFieldInitializers(size_t numFields, bool isStatic);
|
||||
MOZ_MUST_USE bool prepareForFieldInitializer();
|
||||
MOZ_MUST_USE bool emitFieldInitializerHomeObject(bool isStatic);
|
||||
MOZ_MUST_USE bool emitStoreFieldInitializer();
|
||||
MOZ_MUST_USE bool emitFieldInitializersEnd();
|
||||
|
||||
MOZ_MUST_USE bool emitBinding();
|
||||
|
||||
MOZ_MUST_USE bool emitEnd(Kind kind);
|
||||
|
||||
private:
|
||||
void setName(JS::Handle<JSAtom*> name);
|
||||
MOZ_MUST_USE bool initProtoAndCtor();
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_ObjectEmitter_h */
|
||||
|
|
@ -236,6 +236,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_PREDECREMENT:
|
||||
case PNK_POSTDECREMENT:
|
||||
case PNK_COMPUTED_NAME:
|
||||
case PNK_STATICCLASSBLOCK:
|
||||
case PNK_ARRAYPUSH:
|
||||
case PNK_SPREAD:
|
||||
case PNK_MUTATEPROTO:
|
||||
|
|
@ -257,9 +258,13 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
// Binary nodes with two non-null children.
|
||||
|
||||
// All assignment and compound assignment nodes qualify.
|
||||
case PNK_INITPROP:
|
||||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
|
|
@ -370,6 +375,14 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
return PushResult::Recyclable;
|
||||
}
|
||||
|
||||
case PNK_CLASSFIELD: {
|
||||
BinaryNode* bn = &pn->as<BinaryNode>();
|
||||
stack->push(bn->left());
|
||||
if (bn->right())
|
||||
stack->push(bn->right());
|
||||
return PushResult::Recyclable;
|
||||
}
|
||||
|
||||
// Ternary nodes with all children non-null.
|
||||
case PNK_CONDITIONAL: {
|
||||
TernaryNode* tn = &pn->as<TernaryNode>();
|
||||
|
|
@ -494,7 +507,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_IMPORT_SPEC_LIST:
|
||||
case PNK_EXPORT_SPEC_LIST:
|
||||
case PNK_PARAMSBODY:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSMEMBERLIST:
|
||||
return PushListNodeChildren(&pn->as<ListNode>(), stack);
|
||||
|
||||
// Array comprehension nodes are lists with a single child:
|
||||
|
|
@ -881,6 +894,7 @@ NameNode::dump(int indent)
|
|||
}
|
||||
|
||||
case PNK_NAME:
|
||||
case PNK_PRIVATE_NAME: // atom() already includes the '#', no need to specially include it.
|
||||
case PNK_PROPERTYNAME: {
|
||||
if (!atom()) {
|
||||
fprintf(stderr, "#<null name>");
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class ObjectBox;
|
|||
F(CALL) \
|
||||
F(ARGUMENTS) \
|
||||
F(NAME) \
|
||||
F(PRIVATE_NAME) \
|
||||
F(OBJECT_PROPERTY_NAME) \
|
||||
F(COMPUTED_NAME) \
|
||||
F(NUMBER) \
|
||||
|
|
@ -116,13 +117,16 @@ class ObjectBox;
|
|||
F(MUTATEPROTO) \
|
||||
F(CLASS) \
|
||||
F(CLASSMETHOD) \
|
||||
F(CLASSMETHODLIST) \
|
||||
F(STATICCLASSBLOCK) \
|
||||
F(CLASSFIELD) \
|
||||
F(CLASSMEMBERLIST) \
|
||||
F(CLASSNAMES) \
|
||||
F(NEWTARGET) \
|
||||
F(POSHOLDER) \
|
||||
F(SUPERBASE) \
|
||||
F(SUPERCALL) \
|
||||
F(SETTHIS) \
|
||||
F(INITPROP) \
|
||||
F(IMPORT_META) \
|
||||
F(CALL_IMPORT) \
|
||||
\
|
||||
|
|
@ -168,10 +172,13 @@ class ObjectBox;
|
|||
F(POW) \
|
||||
\
|
||||
/* Assignment operators (= += -= etc.). */ \
|
||||
/* ParseNode::isAssignment assumes all these are consecutive. */ \
|
||||
/* AssignmentNode::test assumes all these are consecutive. */ \
|
||||
F(ASSIGN) \
|
||||
F(ADDASSIGN) \
|
||||
F(SUBASSIGN) \
|
||||
F(COALESCEASSIGN) \
|
||||
F(ORASSIGN) \
|
||||
F(ANDASSIGN) \
|
||||
F(BITORASSIGN) \
|
||||
F(BITXORASSIGN) \
|
||||
F(BITANDASSIGN) \
|
||||
|
|
@ -250,20 +257,22 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_CLASS (ClassNode)
|
||||
* kid1: PNK_CLASSNAMES for class name. can be null for anonymous class.
|
||||
* kid2: expression after `extends`. null if no expression
|
||||
* kid3: either of
|
||||
* * PNK_CLASSMETHODLIST, if anonymous class
|
||||
* * PNK_LEXICALSCOPE which contains PNK_CLASSMETHODLIST as scopeBody,
|
||||
* if named class
|
||||
* kid3: PNK_LEXICALSCOPE which contains PNK_CLASSMEMBERLIST as scopeBody
|
||||
* PNK_CLASSNAMES (ClassNames)
|
||||
* left: Name node for outer binding, or null if the class is an expression
|
||||
* that doesn't create an outer binding
|
||||
* right: Name node for inner binding
|
||||
* PNK_CLASSMETHODLIST (ListNode)
|
||||
* head: list of N PNK_CLASSMETHOD nodes
|
||||
* PNK_CLASSMEMBERLIST (ListNode)
|
||||
* head: list of N PNK_CLASSMETHOD, PNK_CLASSFIELD or PNK_STATICCLASSBLOCK nodes
|
||||
* count: N >= 0
|
||||
* PNK_CLASSMETHOD (ClassMethod)
|
||||
* name: propertyName
|
||||
* method: methodDefinition
|
||||
* PNK_CLASSFIELD (ClassField)
|
||||
* name: fieldName
|
||||
* initializer: field initializer or null
|
||||
* PNK_STATICCLASSBLOCK (StaticClassBlock)
|
||||
* block: block initializer
|
||||
* PNK_MODULE (ModuleNode)
|
||||
* body: statement list of the module
|
||||
*
|
||||
|
|
@ -383,11 +392,16 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_COMMA (ListNode)
|
||||
* head: list of N comma-separated exprs
|
||||
* count: N >= 2
|
||||
* PNK_ASSIGN (BinaryNode)
|
||||
* PNK_INITPROP (BinaryNode)
|
||||
* left: target of assignment, base-class setter will not be invoked
|
||||
* right: value to assign
|
||||
* PNK_ASSIGN (AssignmentNode)
|
||||
* left: target of assignment
|
||||
* right: value to assign
|
||||
* PNK_ADDASSIGN, PNK_SUBASSIGN, PNK_BITORASSIGN, PNK_BITXORASSIGN,
|
||||
* PNK_BITANDASSIGN, PNK_LSHASSIGN, PNK_RSHASSIGN, PNK_URSHASSIGN,
|
||||
* PNK_ADDASSIGN, PNK_SUBASSIGN,
|
||||
* PNK_COALESCEASSIGN, PNK_ORASSIGN, PNK_ANDASSIGN,
|
||||
* PNK_BITORASSIGN, PNK_BITXORASSIGN, PNK_BITANDASSIGN,
|
||||
* PNK_LSHASSIGN, PNK_RSHASSIGN, PNK_URSHASSIGN,
|
||||
* PNK_MULASSIGN, PNK_DIVASSIGN, PNK_MODASSIGN, PNK_POWASSIGN (AssignmentNode)
|
||||
* left: target of assignment
|
||||
* right: value to assign
|
||||
|
|
@ -567,6 +581,8 @@ enum ParseNodeArity
|
|||
macro(AssignmentNode, AssignmentNodeType, asAssignment) \
|
||||
macro(CaseClause, CaseClauseType, asCaseClause) \
|
||||
macro(ClassMethod, ClassMethodType, asClassMethod) \
|
||||
macro(ClassField, ClassFieldType, asClassField) \
|
||||
macro(StaticClassBlock, StaticClassBlockType, asStaticClassBlock) \
|
||||
macro(ClassNames, ClassNamesType, asClassNames) \
|
||||
macro(ForNode, ForNodeType, asFor) \
|
||||
macro(PropertyAccess, PropertyAccessType, asPropertyAccess) \
|
||||
|
|
@ -618,7 +634,10 @@ enum class FunctionSyntaxKind
|
|||
Expression, // A non-arrow function expression.
|
||||
Statement, // A named function appearing as a Statement.
|
||||
Arrow,
|
||||
Method,
|
||||
Method, // Method of a class or object.
|
||||
FieldInitializer, // Field initializers desugar to methods.
|
||||
StaticClassBlock, // Mostly static class blocks act similar to field initializers, however,
|
||||
// there is some difference in static semantics.
|
||||
ClassConstructor,
|
||||
DerivedClassConstructor,
|
||||
Getter,
|
||||
|
|
@ -652,6 +671,7 @@ static inline bool
|
|||
IsMethodDefinitionKind(FunctionSyntaxKind kind)
|
||||
{
|
||||
return kind == FunctionSyntaxKind::Method ||
|
||||
kind == FunctionSyntaxKind::FieldInitializer ||
|
||||
IsConstructorKind(kind) ||
|
||||
IsGetterKind(kind) || IsSetterKind(kind);
|
||||
}
|
||||
|
|
@ -754,6 +774,7 @@ class ParseNode
|
|||
private:
|
||||
friend class BinaryNode;
|
||||
friend class ForNode;
|
||||
friend class ClassField;
|
||||
friend class ClassMethod;
|
||||
friend class PropertyAccessBase;
|
||||
friend class SwitchStatement;
|
||||
|
|
@ -761,7 +782,7 @@ class ParseNode
|
|||
ParseNode* right;
|
||||
union {
|
||||
unsigned iflags; /* JSITER_* flags for PNK_{COMPREHENSION,}FOR node */
|
||||
bool isStatic; /* only for PNK_CLASSMETHOD */
|
||||
bool isStatic; /* only for PNK_CLASSMETHOD and PNK_CLASSFIELD */
|
||||
bool hasDefault; /* only for PNK_SWITCH */
|
||||
};
|
||||
} binary;
|
||||
|
|
@ -1233,7 +1254,7 @@ class ListNode : public ParseNode
|
|||
MOZ_MUST_USE bool hasNonConstInitializer() const {
|
||||
MOZ_ASSERT(isKind(PNK_ARRAY) ||
|
||||
isKind(PNK_OBJECT) ||
|
||||
isKind(PNK_CLASSMETHODLIST));
|
||||
isKind(PNK_CLASSMEMBERLIST));
|
||||
return pn_u.list.xflags & hasNonConstInitializerBit;
|
||||
}
|
||||
|
||||
|
|
@ -1250,7 +1271,7 @@ class ListNode : public ParseNode
|
|||
void setHasNonConstInitializer() {
|
||||
MOZ_ASSERT(isKind(PNK_ARRAY) ||
|
||||
isKind(PNK_OBJECT) ||
|
||||
isKind(PNK_CLASSMETHODLIST));
|
||||
isKind(PNK_CLASSMEMBERLIST));
|
||||
pn_u.list.xflags |= hasNonConstInitializerBit;
|
||||
}
|
||||
|
||||
|
|
@ -1874,9 +1895,9 @@ class NullLiteral : public NullaryNode
|
|||
}
|
||||
};
|
||||
|
||||
// This is only used internally, currently just for tagged templates.
|
||||
// It represents the value 'undefined' (aka `void 0`), like NullLiteral
|
||||
// represents the value 'null'.
|
||||
// This is only used internally, currently just for tagged templates and the
|
||||
// initial value of fields without initializers. It represents the value
|
||||
// 'undefined' (aka `void 0`), like NullLiteral represents the value 'null'.
|
||||
class RawUndefinedLiteral : public NullaryNode
|
||||
{
|
||||
public:
|
||||
|
|
@ -2124,6 +2145,55 @@ class ClassMethod : public BinaryNode
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
class ClassField : public BinaryNode
|
||||
{
|
||||
public:
|
||||
ClassField(ParseNode* name, ParseNode* initializer, bool isStatic)
|
||||
: BinaryNode(PNK_CLASSFIELD, JSOP_NOP,
|
||||
TokenPos::box(name->pn_pos, initializer->pn_pos),
|
||||
name, initializer)
|
||||
{
|
||||
pn_u.binary.isStatic = isStatic;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
bool match = node.isKind(PNK_CLASSFIELD);
|
||||
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
|
||||
return match;
|
||||
}
|
||||
|
||||
ParseNode& name() const { return *left(); }
|
||||
|
||||
FunctionNode* initializer() const { return &right()->as<FunctionNode>(); }
|
||||
|
||||
bool isStatic() const {
|
||||
return pn_u.binary.isStatic;
|
||||
}
|
||||
};
|
||||
|
||||
// Hold onto the function generated for a class static block like
|
||||
//
|
||||
// class A {
|
||||
// static { /* this static block */ }
|
||||
// }
|
||||
//
|
||||
class StaticClassBlock : public UnaryNode
|
||||
{
|
||||
public:
|
||||
explicit StaticClassBlock(FunctionNode* function)
|
||||
: UnaryNode(PNK_STATICCLASSBLOCK, JSOP_NOP, function->pn_pos, function) {
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
bool match = node.isKind(PNK_STATICCLASSBLOCK);
|
||||
MOZ_ASSERT_IF(match, node.is<UnaryNode>());
|
||||
return match;
|
||||
}
|
||||
FunctionNode* function() const { return &kid()->as<FunctionNode>(); }
|
||||
};
|
||||
|
||||
|
||||
class SwitchStatement : public BinaryNode
|
||||
{
|
||||
public:
|
||||
|
|
@ -2207,13 +2277,11 @@ class ClassNames : public BinaryNode
|
|||
class ClassNode : public TernaryNode
|
||||
{
|
||||
public:
|
||||
ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock,
|
||||
ClassNode(ParseNode* names, ParseNode* heritage, LexicalScopeNode* memberBlock,
|
||||
const TokenPos& pos)
|
||||
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock, pos)
|
||||
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, memberBlock, pos)
|
||||
{
|
||||
MOZ_ASSERT_IF(names, names->is<ClassNames>());
|
||||
MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() ||
|
||||
methodsOrBlock->isKind(PNK_CLASSMETHODLIST));
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
|
|
@ -2228,18 +2296,14 @@ class ClassNode : public TernaryNode
|
|||
ParseNode* heritage() const {
|
||||
return kid2();
|
||||
}
|
||||
ListNode* methodList() const {
|
||||
ParseNode* methodsOrBlock = kid3();
|
||||
if (methodsOrBlock->isKind(PNK_CLASSMETHODLIST))
|
||||
return &methodsOrBlock->as<ListNode>();
|
||||
|
||||
ListNode* list = &methodsOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
|
||||
MOZ_ASSERT(list->isKind(PNK_CLASSMETHODLIST));
|
||||
ListNode* memberList() const {
|
||||
ListNode* list = &kid3()->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
|
||||
MOZ_ASSERT(list->isKind(PNK_CLASSMEMBERLIST));
|
||||
return list;
|
||||
}
|
||||
Handle<LexicalScope::Data*> scopeBindings() const {
|
||||
ParseNode* scope = kid3();
|
||||
return scope->as<LexicalScopeNode>().scopeBindings();
|
||||
LexicalScopeNode* scopeBindings() const {
|
||||
LexicalScopeNode* scope = &kid3()->as<LexicalScopeNode>();
|
||||
return scope->isEmptyScope() ? nullptr : scope;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -526,6 +526,10 @@ class ParseContext : public Nestable<ParseContext>
|
|||
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isMethod();
|
||||
}
|
||||
|
||||
bool allowReturn() const {
|
||||
return sc_->isFunctionBox() && sc_->asFunctionBox()->allowReturn();
|
||||
}
|
||||
|
||||
uint32_t scriptId() const {
|
||||
return scriptId_;
|
||||
}
|
||||
|
|
@ -583,15 +587,16 @@ enum class PropertyType {
|
|||
AsyncMethod,
|
||||
AsyncGeneratorMethod,
|
||||
Constructor,
|
||||
DerivedConstructor
|
||||
DerivedConstructor,
|
||||
Field,
|
||||
};
|
||||
|
||||
// Specify a value for an ES6 grammar parametrization. We have no enum for
|
||||
// [Return] because its behavior is exactly equivalent to checking whether
|
||||
// [Return] because its behavior is almost exactly equivalent to checking whether
|
||||
// we're in a function box -- easier and simpler than passing an extra
|
||||
// parameter everywhere.
|
||||
enum YieldHandling { YieldIsName, YieldIsKeyword };
|
||||
enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword };
|
||||
enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword, AwaitIsDisallowed };
|
||||
enum InHandling { InAllowed, InProhibited };
|
||||
enum DefaultHandling { NameRequired, AllowDefaultName };
|
||||
enum TripledotHandling { TripledotAllowed, TripledotProhibited };
|
||||
|
|
@ -720,6 +725,15 @@ class UsedNameTracker
|
|||
MOZ_MUST_USE bool noteUse(ExclusiveContext* cx, JSAtom* name,
|
||||
uint32_t scriptId, uint32_t scopeId);
|
||||
|
||||
MOZ_MUST_USE bool markAsAlwaysClosedOver(ExclusiveContext* cx, JSAtom* name,
|
||||
uint32_t scriptId, uint32_t scopeId) {
|
||||
// This marks a variable as always closed over:
|
||||
// UsedNameInfo::noteBoundInScope only checks if scriptId and scopeId are
|
||||
// greater than the current scriptId/scopeId, so do a simple increment to
|
||||
// make that so.
|
||||
return noteUse(cx, name, scriptId + 1, scopeId + 1);
|
||||
}
|
||||
|
||||
struct RewindToken
|
||||
{
|
||||
private:
|
||||
|
|
@ -807,7 +821,10 @@ class ParserBase : public StrictModeGetter
|
|||
|
||||
public:
|
||||
bool awaitIsKeyword() const {
|
||||
return awaitHandling_ != AwaitIsName;
|
||||
return awaitHandling_ == AwaitIsKeyword || awaitHandling_ == AwaitIsModuleKeyword;
|
||||
}
|
||||
bool awaitIsDisallowed() const {
|
||||
return awaitHandling_ == AwaitIsDisallowed;
|
||||
}
|
||||
|
||||
ParseGoal parseGoal() const {
|
||||
|
|
@ -1171,7 +1188,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
*/
|
||||
JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
||||
HandleObject proto);
|
||||
HandleObject proto = nullptr);
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
|
|
@ -1440,6 +1457,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
|
||||
// Parse a function body. Pass StatementListBody if the body is a list of
|
||||
// statements; pass ExpressionBody if the body is a single expression.
|
||||
//
|
||||
// Don't include opening LeftCurly token when invoking.
|
||||
enum FunctionBodyType { StatementListBody, ExpressionBody };
|
||||
LexicalScopeNodeType functionBody(InHandling inHandling, YieldHandling yieldHandling,
|
||||
FunctionSyntaxKind kind, FunctionBodyType type);
|
||||
|
|
@ -1478,18 +1497,53 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
enum ClassContext { ClassStatement, ClassExpression };
|
||||
ClassNodeType classDefinition(YieldHandling yieldHandling, ClassContext classContext,
|
||||
DefaultHandling defaultHandling);
|
||||
struct ClassFields {
|
||||
// The number of instance class fields.
|
||||
size_t instanceFields = 0;
|
||||
|
||||
bool checkLabelOrIdentifierReference(HandlePropertyName ident,
|
||||
// The number of instance class fields with computed property names.
|
||||
size_t instanceFieldKeys = 0;
|
||||
|
||||
// The number of static class fields.
|
||||
size_t staticFields = 0;
|
||||
|
||||
// The number of static blocks
|
||||
size_t staticBlocks = 0;
|
||||
|
||||
// The number of static class fields with computed property names.
|
||||
size_t staticFieldKeys = 0;
|
||||
};
|
||||
MOZ_MUST_USE bool classMember(YieldHandling yieldHandling,
|
||||
const ParseContext::ClassStatement& classStmt,
|
||||
HandlePropertyName className,
|
||||
uint32_t classStartOffset, bool hasHeritage,
|
||||
ClassFields& classFields,
|
||||
ListNodeType& classMembers, bool* done);
|
||||
MOZ_MUST_USE bool finishClassConstructor(
|
||||
const ParseContext::ClassStatement& classStmt,
|
||||
HandlePropertyName className, bool hasHeritage,
|
||||
uint32_t classStartOffset, uint32_t classEndOffset,
|
||||
const ClassFields& classFields, ListNodeType& classMembers);
|
||||
|
||||
FunctionNodeType fieldInitializerOpt(HandleAtom atom, ClassFields& classFields, bool isStatic);
|
||||
FunctionNodeType staticClassBlock(ClassFields& classFields);
|
||||
FunctionNodeType synthesizeConstructor(HandleAtom className,
|
||||
uint32_t classNameOffset,
|
||||
bool hasHeritage);
|
||||
|
||||
bool checkLabelOrIdentifierReference(PropertyName* ident,
|
||||
uint32_t offset,
|
||||
YieldHandling yieldHandling);
|
||||
YieldHandling yieldHandling,
|
||||
TokenKind hint = TOK_LIMIT);
|
||||
|
||||
bool checkLocalExportName(HandlePropertyName ident, uint32_t offset) {
|
||||
bool checkLocalExportName(PropertyName* ident, uint32_t offset) {
|
||||
return checkLabelOrIdentifierReference(ident, offset, YieldIsName);
|
||||
}
|
||||
|
||||
bool checkBindingIdentifier(HandlePropertyName ident,
|
||||
bool checkBindingIdentifier(PropertyName* ident,
|
||||
uint32_t offset,
|
||||
YieldHandling yieldHandling);
|
||||
YieldHandling yieldHandling,
|
||||
TokenKind hint = TOK_LIMIT);
|
||||
|
||||
PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
|
||||
|
||||
|
|
@ -1520,8 +1574,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
bool matchInOrOf(bool* isForInp, bool* isForOfp);
|
||||
|
||||
bool hasUsedFunctionSpecialName(HandlePropertyName name);
|
||||
bool declareFunctionArgumentsObject();
|
||||
bool declareFunctionThis();
|
||||
bool declareFunctionArgumentsObject(bool canSkipLazyClosedOverBindings);
|
||||
bool declareFunctionThis(bool canSkipLazyClosedOverBindings);
|
||||
NameNodeType newInternalDotName(HandlePropertyName name);
|
||||
NameNodeType newThisName();
|
||||
NameNodeType newDotGeneratorName();
|
||||
|
|
@ -1594,9 +1648,15 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
|
||||
LexicalScopeNodeType finishLexicalScope(ParseContext::Scope& scope, Node body);
|
||||
|
||||
enum PropertyNameContext { PropertyNameInLiteral, PropertyNameInPattern, PropertyNameInClass };
|
||||
Node propertyName(YieldHandling yieldHandling,
|
||||
PropertyNameContext propertyNameContext,
|
||||
const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType propList,
|
||||
PropertyType* propType, MutableHandleAtom propAtom);
|
||||
MutableHandleAtom propAtom);
|
||||
Node propertyOrMethodName(YieldHandling yieldHandling,
|
||||
PropertyNameContext propertyNameContext,
|
||||
const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType propList,
|
||||
PropertyType* propType, MutableHandleAtom propAtom);
|
||||
UnaryNodeType computedPropertyName(YieldHandling yieldHandling,
|
||||
const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType literal);
|
||||
ListNodeType arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
|
||||
|
|
|
|||
|
|
@ -110,11 +110,11 @@ PropOpEmitter::emitGet(JSAtom* prop)
|
|||
bool
|
||||
PropOpEmitter::prepareForRhs()
|
||||
{
|
||||
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
|
||||
MOZ_ASSERT_IF(isSimpleAssignment(), state_ == State::Obj);
|
||||
MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
|
||||
MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Obj);
|
||||
MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
|
||||
|
||||
if (isSimpleAssignment()) {
|
||||
if (isSimpleAssignment() || isPropInit()) {
|
||||
// For CompoundAssignment, SUPERBASE is already emitted by emitGet.
|
||||
if (isSuper()) {
|
||||
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS SUPERBASE
|
||||
|
|
@ -133,7 +133,7 @@ bool
|
|||
PropOpEmitter::skipObjAndRhs()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start);
|
||||
MOZ_ASSERT(isSimpleAssignment());
|
||||
MOZ_ASSERT(isSimpleAssignment() || isPropInit());
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Rhs;
|
||||
|
|
@ -182,18 +182,20 @@ PropOpEmitter::emitDelete(JSAtom* prop)
|
|||
bool
|
||||
PropOpEmitter::emitAssignment(JSAtom* prop)
|
||||
{
|
||||
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
|
||||
MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
|
||||
MOZ_ASSERT(state_ == State::Rhs);
|
||||
|
||||
if (isSimpleAssignment()) {
|
||||
if (isSimpleAssignment() || isPropInit()) {
|
||||
if (!prepareAtomIndex(prop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JSOp setOp = isSuper()
|
||||
? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
|
||||
: bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
MOZ_ASSERT_IF(isPropInit(), !isSuper());
|
||||
JSOp setOp = isPropInit() ? JSOP_INITPROP
|
||||
: isSuper()
|
||||
? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
|
||||
: bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
if (!bce_->emitAtomOp(propAtomIndex_, setOp)) { // VAL
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ class MOZ_STACK_CLASS PropOpEmitter
|
|||
PostDecrement,
|
||||
PreDecrement,
|
||||
SimpleAssignment,
|
||||
PropInit,
|
||||
CompoundAssignment
|
||||
};
|
||||
enum class ObjKind {
|
||||
|
|
@ -167,6 +168,7 @@ class MOZ_STACK_CLASS PropOpEmitter
|
|||
// | +--------+ |
|
||||
// | |
|
||||
// | [SimpleAssignment] |
|
||||
// | [PropInit] |
|
||||
// | prepareForRhs | +-----+
|
||||
// +--------------------->+-------------->+->| Rhs |-+
|
||||
// | ^ +-----+ |
|
||||
|
|
@ -217,6 +219,10 @@ class MOZ_STACK_CLASS PropOpEmitter
|
|||
return kind_ == Kind::SimpleAssignment;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool isPropInit() const {
|
||||
return kind_ == Kind::PropInit;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool isDelete() const {
|
||||
return kind_ == Kind::Delete;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ class FunctionContextFlags
|
|||
|
||||
bool needsHomeObject:1;
|
||||
bool isDerivedClassConstructor:1;
|
||||
bool isFieldInitializer:1;
|
||||
|
||||
// Whether this function has a .this binding. If true, we need to emit
|
||||
// JSOP_FUNCTIONTHIS in the prologue to initialize it.
|
||||
|
|
@ -170,6 +171,7 @@ class FunctionContextFlags
|
|||
definitelyNeedsArgsObj(false),
|
||||
needsHomeObject(false),
|
||||
isDerivedClassConstructor(false),
|
||||
isFieldInitializer(false),
|
||||
hasThisBinding(false),
|
||||
hasInnerFunctions(false)
|
||||
{ }
|
||||
|
|
@ -243,6 +245,7 @@ class SharedContext
|
|||
bool allowNewTarget_;
|
||||
bool allowSuperProperty_;
|
||||
bool allowSuperCall_;
|
||||
bool allowArguments_;
|
||||
bool inWith_;
|
||||
bool needsThisTDZChecks_;
|
||||
|
||||
|
|
@ -262,6 +265,7 @@ class SharedContext
|
|||
allowNewTarget_(false),
|
||||
allowSuperProperty_(false),
|
||||
allowSuperCall_(false),
|
||||
allowArguments_(true),
|
||||
inWith_(false),
|
||||
needsThisTDZChecks_(false)
|
||||
{ }
|
||||
|
|
@ -286,6 +290,7 @@ class SharedContext
|
|||
bool allowNewTarget() const { return allowNewTarget_; }
|
||||
bool allowSuperProperty() const { return allowSuperProperty_; }
|
||||
bool allowSuperCall() const { return allowSuperCall_; }
|
||||
bool allowArguments() const { return allowArguments_; }
|
||||
bool inWith() const { return inWith_; }
|
||||
bool needsThisTDZChecks() const { return needsThisTDZChecks_; }
|
||||
|
||||
|
|
@ -427,6 +432,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
bool isExprBody_:1; /* arrow function with expression
|
||||
* body or expression closure:
|
||||
* function(x) x*x */
|
||||
bool allowReturn_ : 1; /* Used to issue an early error in static class blocks. */
|
||||
|
||||
|
||||
FunctionContextFlags funCxFlags;
|
||||
|
||||
|
|
@ -451,7 +458,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
|
||||
void initFromLazyFunction();
|
||||
void initStandaloneFunction(Scope* enclosingScope);
|
||||
void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
|
||||
void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
|
||||
|
||||
ObjectBox* toObjectBox() override { return this; }
|
||||
JSFunction* function() const { return &object->as<JSFunction>(); }
|
||||
|
|
@ -518,6 +525,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
isExprBody_ = true;
|
||||
}
|
||||
|
||||
bool allowReturn() const { return allowReturn_; }
|
||||
|
||||
void setGeneratorKind(GeneratorKind kind) {
|
||||
// A generator kind can be set at initialization, or when "yield" is
|
||||
// first seen. In both cases the transition can only happen from
|
||||
|
|
@ -533,6 +542,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
bool needsHomeObject() const { return funCxFlags.needsHomeObject; }
|
||||
bool isDerivedClassConstructor() const { return funCxFlags.isDerivedClassConstructor; }
|
||||
bool hasInnerFunctions() const { return funCxFlags.hasInnerFunctions; }
|
||||
bool isFieldInitializer() const { return funCxFlags.isFieldInitializer; }
|
||||
|
||||
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
|
||||
void setHasThisBinding() { funCxFlags.hasThisBinding = true; }
|
||||
|
|
@ -544,6 +554,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
void setDerivedClassConstructor() { MOZ_ASSERT(function()->isClassConstructor());
|
||||
funCxFlags.isDerivedClassConstructor = true; }
|
||||
void setHasInnerFunctions() { funCxFlags.hasInnerFunctions = true; }
|
||||
void setFieldInitializer() { MOZ_ASSERT(function()->isMethod());
|
||||
funCxFlags.isFieldInitializer = true; }
|
||||
|
||||
bool hasSimpleParameterList() const {
|
||||
return !hasRest() && !hasParameterExprs && !hasDestructuringArgs;
|
||||
|
|
@ -563,7 +575,11 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
}
|
||||
|
||||
void setStart(const TokenStream& tokenStream) {
|
||||
bufStart = tokenStream.currentToken().pos.begin;
|
||||
setStart(tokenStream, tokenStream.currentToken().pos);
|
||||
}
|
||||
|
||||
void setStart(const TokenStream& tokenStream, const TokenPos& tokenPos) {
|
||||
bufStart = tokenPos.begin;
|
||||
tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
Node newGenExp(Node callee, Node args) { return NodeGeneric; }
|
||||
|
||||
ListNodeType newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
|
||||
ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
|
||||
ListNodeType newClassMemberList(uint32_t begin) { return NodeGeneric; }
|
||||
ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
|
||||
ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
|
||||
|
||||
|
|
@ -331,7 +331,10 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
MOZ_MUST_USE bool addShorthand(ListNodeType literal, NameNodeType name, NameNodeType expr) { return true; }
|
||||
MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { return true; }
|
||||
MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node name, FunctionNodeType funNode, JSOp op) { return true; }
|
||||
MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType literal, Node name, FunctionNodeType funNode, JSOp op, bool isStatic) { return true; }
|
||||
MOZ_MUST_USE Node newClassMethodDefinition(Node key, FunctionNodeType funNode, JSOp op, bool isStatic) { return NodeGeneric; }
|
||||
MOZ_MUST_USE Node newClassFieldDefinition(Node name, FunctionNodeType initializer, bool isStatic) { return NodeGeneric; }
|
||||
MOZ_MUST_USE Node newStaticClassBlock(FunctionNodeType block) { return NodeGeneric; }
|
||||
MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList, Node member) { return true; }
|
||||
UnaryNodeType newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
|
||||
UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
|
||||
UnaryNodeType newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
|
||||
|
|
@ -417,7 +420,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
|
||||
void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
|
||||
|
||||
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind) { return NodeFunctionDefinition; }
|
||||
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) { return NodeFunctionDefinition; }
|
||||
|
||||
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) { return true; }
|
||||
void setFunctionFormalParametersAndBody(FunctionNodeType funNode, ListNodeType paramsBody) {}
|
||||
|
|
@ -468,7 +471,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
MOZ_ASSERT(kind != PNK_CONST);
|
||||
return NodeGeneric;
|
||||
}
|
||||
ListNodeType newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) {
|
||||
ListNodeType newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) {
|
||||
return newList(kind, op);
|
||||
}
|
||||
ListNodeType newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
macro(LP, "'('") \
|
||||
macro(RP, "')'") \
|
||||
macro(NAME, "identifier") \
|
||||
macro(PRIVATE_NAME, "private identifier") \
|
||||
macro(NUMBER, "numeric literal") \
|
||||
macro(STRING, "string literal") \
|
||||
\
|
||||
|
|
@ -218,6 +219,9 @@
|
|||
range(ASSIGNMENT_START, ASSIGN) \
|
||||
macro(ADDASSIGN, "'+='") \
|
||||
macro(SUBASSIGN, "'-='") \
|
||||
macro(COALESCEASSIGN, "'\?\?='") /* avoid trigraphs warning */ \
|
||||
macro(ORASSIGN, "'||='") \
|
||||
macro(ANDASSIGN, "'&&='") \
|
||||
macro(BITORASSIGN, "'|='") \
|
||||
macro(BITXORASSIGN, "'^='") \
|
||||
macro(BITANDASSIGN, "'&='") \
|
||||
|
|
@ -322,6 +326,7 @@ inline MOZ_MUST_USE bool
|
|||
TokenKindIsPossibleIdentifier(TokenKind tt)
|
||||
{
|
||||
return tt == TOK_NAME ||
|
||||
tt == TOK_PRIVATE_NAME ||
|
||||
TokenKindIsContextualKeyword(tt) ||
|
||||
TokenKindIsStrictReservedWord(tt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,18 +93,35 @@ FindReservedWord(const CharT* s, size_t length)
|
|||
}
|
||||
|
||||
static const ReservedWordInfo*
|
||||
FindReservedWord(JSLinearString* str)
|
||||
FindReservedWord(JSLinearString* str, js::frontend::NameVisibility* visibility)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return str->hasLatin1Chars()
|
||||
? FindReservedWord(str->latin1Chars(nogc), str->length())
|
||||
: FindReservedWord(str->twoByteChars(nogc), str->length());
|
||||
if (str->hasLatin1Chars()) {
|
||||
const JS::Latin1Char* chars = str->latin1Chars(nogc);
|
||||
size_t length = str->length();
|
||||
if (length > 0 && chars[0] == '#') {
|
||||
*visibility = js::frontend::NameVisibility::Private;
|
||||
return nullptr;
|
||||
}
|
||||
*visibility = js::frontend::NameVisibility::Public;
|
||||
return FindReservedWord(chars, length);
|
||||
}
|
||||
|
||||
const char16_t* chars = str->twoByteChars(nogc);
|
||||
size_t length = str->length();
|
||||
if (length > 0 && chars[0] == '#') {
|
||||
*visibility = js::frontend::NameVisibility::Private;
|
||||
return nullptr;
|
||||
}
|
||||
*visibility = js::frontend::NameVisibility::Public;
|
||||
return FindReservedWord(chars, length);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static bool
|
||||
IsIdentifier(const CharT* chars, size_t length)
|
||||
{
|
||||
// Generic version for latin1 in char* and UCS-2 in char16_t*
|
||||
if (length == 0)
|
||||
return false;
|
||||
|
||||
|
|
@ -138,14 +155,52 @@ GetSingleCodePoint(const char16_t** p, const char16_t* end)
|
|||
return codePoint;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace frontend {
|
||||
|
||||
// Latin1 Variants
|
||||
|
||||
bool
|
||||
IsIdentifier(const Latin1Char* chars, size_t length)
|
||||
{
|
||||
return ::IsIdentifier(chars, length);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsIdentifierNameOrPrivateName(const Latin1Char* chars, size_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return false;
|
||||
|
||||
if (char16_t(*chars) == '#') {
|
||||
++chars;
|
||||
--length;
|
||||
}
|
||||
|
||||
return IsIdentifier(chars, length);
|
||||
}
|
||||
|
||||
// UTF-16 Versions
|
||||
|
||||
bool
|
||||
IsIdentifier(const char16_t* chars, size_t length)
|
||||
{
|
||||
return ::IsIdentifier(chars, length);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
|
||||
{
|
||||
if (IsIdentifier(chars, length))
|
||||
return true;
|
||||
|
||||
if (length == 0)
|
||||
return false;
|
||||
// XXX Revisit if this is still faster.
|
||||
// Assumption is that iterating the string twice in the rare worst case (not a valid UCS-2
|
||||
// identifier, but valid in UTF-16) is on average better than parsing UTF-16 code points
|
||||
// individually for every input.
|
||||
if (IsIdentifier(chars, length)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char16_t* p = chars;
|
||||
const char16_t* end = chars + length;
|
||||
|
|
@ -164,71 +219,78 @@ IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsIdentifierNameOrPrivateNameMaybeNonBMP(const char16_t* chars, size_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return false;
|
||||
|
||||
// '#' is always just one character in either UCS-2 or UTF-16, so compare it directly.
|
||||
if (char16_t(*chars) == '#') {
|
||||
++chars;
|
||||
--length;
|
||||
}
|
||||
|
||||
return IsIdentifierMaybeNonBMP(chars, length);
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsIdentifier(JSLinearString* str)
|
||||
IsIdentifier(JSLinearString* str)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return str->hasLatin1Chars()
|
||||
? ::IsIdentifier(str->latin1Chars(nogc), str->length())
|
||||
: ::IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length());
|
||||
if (str->hasLatin1Chars()) {
|
||||
return IsIdentifier(str->latin1Chars(nogc), str->length());
|
||||
|
||||
}
|
||||
return IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length());
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsIdentifier(const char* chars, size_t length)
|
||||
IsIdentifierNameOrPrivateName(JSLinearString* str)
|
||||
{
|
||||
return ::IsIdentifier(chars, length);
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
if (str->hasLatin1Chars()) {
|
||||
return IsIdentifierNameOrPrivateName(str->latin1Chars(nogc), str->length());
|
||||
|
||||
}
|
||||
return IsIdentifierNameOrPrivateNameMaybeNonBMP(str->twoByteChars(nogc), str->length());
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsIdentifier(const char16_t* chars, size_t length)
|
||||
IsKeyword(JSLinearString* str)
|
||||
{
|
||||
return ::IsIdentifier(chars, length);
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsKeyword(JSLinearString* str)
|
||||
{
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(str))
|
||||
NameVisibility visibility;
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(str, &visibility))
|
||||
return TokenKindIsKeyword(rw->tokentype);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsFutureReservedWord(JSLinearString* str)
|
||||
TokenKind
|
||||
ReservedWordTokenKind(PropertyName* str)
|
||||
{
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(str))
|
||||
return TokenKindIsFutureReservedWord(rw->tokentype);
|
||||
NameVisibility visibility;
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(str, &visibility))
|
||||
return rw->tokentype;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsStrictReservedWord(JSLinearString* str)
|
||||
{
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(str))
|
||||
return TokenKindIsStrictReservedWord(rw->tokentype);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsReservedWordLiteral(JSLinearString* str)
|
||||
{
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(str))
|
||||
return TokenKindIsReservedWordLiteral(rw->tokentype);
|
||||
|
||||
return false;
|
||||
return visibility == NameVisibility::Private ? TOK_PRIVATE_NAME : TOK_NAME;
|
||||
}
|
||||
|
||||
const char*
|
||||
frontend::ReservedWordToCharZ(PropertyName* str)
|
||||
ReservedWordToCharZ(PropertyName* str)
|
||||
{
|
||||
const ReservedWordInfo* rw = FindReservedWord(str);
|
||||
if (rw == nullptr)
|
||||
return nullptr;
|
||||
NameVisibility visibility;
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(str, &visibility))
|
||||
return ReservedWordToCharZ(rw->tokentype);
|
||||
|
||||
switch (rw->tokentype) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char*
|
||||
ReservedWordToCharZ(TokenKind tt)
|
||||
{
|
||||
MOZ_ASSERT(tt != TOK_NAME);
|
||||
switch (tt) {
|
||||
#define EMIT_CASE(word, name, type) case type: return js_##word##_str;
|
||||
FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
|
||||
#undef EMIT_CASE
|
||||
|
|
@ -238,6 +300,10 @@ frontend::ReservedWordToCharZ(PropertyName* str)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
|
||||
} // namespace js
|
||||
|
||||
PropertyName*
|
||||
TokenStream::reservedWordToPropertyName(TokenKind tt) const
|
||||
{
|
||||
|
|
@ -604,7 +670,7 @@ TokenStream::TokenBuf::findEOLMax(size_t start, size_t max)
|
|||
if (n >= max)
|
||||
break;
|
||||
n++;
|
||||
|
||||
|
||||
// This stops at U+2028 LINE SEPARATOR or U+2029 PARAGRAPH SEPARATOR in
|
||||
// string and template literals. These code points do affect line and
|
||||
// column coordinates, even as they encode their literal values.
|
||||
|
|
@ -1314,6 +1380,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
bool hasExp;
|
||||
DecimalPoint decimalPoint;
|
||||
const char16_t* identStart;
|
||||
NameVisibility identVisibility;
|
||||
bool hadUnicodeEscape;
|
||||
|
||||
// Check if in the middle of a template string. Have to get this out of
|
||||
|
|
@ -1358,6 +1425,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
if (unicode::IsUnicodeIDStart(char16_t(c))) {
|
||||
identStart = userbuf.addressOfNextRawChar() - 1;
|
||||
hadUnicodeEscape = false;
|
||||
identVisibility = NameVisibility::Public;
|
||||
goto identifier;
|
||||
}
|
||||
|
||||
|
|
@ -1368,6 +1436,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
{
|
||||
identStart = userbuf.addressOfNextRawChar() - 2;
|
||||
hadUnicodeEscape = false;
|
||||
identVisibility = NameVisibility::Public;
|
||||
goto identifier;
|
||||
}
|
||||
}
|
||||
|
|
@ -1416,6 +1485,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
tp = newToken(-1);
|
||||
identStart = userbuf.addressOfNextRawChar() - 1;
|
||||
hadUnicodeEscape = false;
|
||||
identVisibility = NameVisibility::Public;
|
||||
|
||||
identifier:
|
||||
for (;;) {
|
||||
|
|
@ -1457,11 +1527,14 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
length = userbuf.addressOfNextRawChar() - identStart;
|
||||
}
|
||||
|
||||
// Represent reserved words as reserved word tokens.
|
||||
if (!hadUnicodeEscape) {
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) {
|
||||
tp->type = rw->tokentype;
|
||||
goto out;
|
||||
// Private identifiers start with a '#', and so cannot be reserved words.
|
||||
if (identVisibility == NameVisibility::Public) {
|
||||
// Represent reserved words as reserved word tokens.
|
||||
if (!hadUnicodeEscape) {
|
||||
if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) {
|
||||
tp->type = rw->tokentype;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1469,7 +1542,12 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
if (!atom) {
|
||||
goto error;
|
||||
}
|
||||
tp->type = TOK_NAME;
|
||||
if (identVisibility == NameVisibility::Private) {
|
||||
MOZ_ASSERT(identStart[0] == '#', "Private identifier starts with #");
|
||||
tp->type = TOK_PRIVATE_NAME;
|
||||
} else {
|
||||
tp->type = TOK_NAME;
|
||||
}
|
||||
tp->setName(atom->asPropertyName());
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1774,14 +1852,31 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
if (escapeLength > 0) {
|
||||
identStart = userbuf.addressOfNextRawChar() - escapeLength - 1;
|
||||
hadUnicodeEscape = true;
|
||||
identVisibility = NameVisibility::Public;
|
||||
goto identifier;
|
||||
}
|
||||
goto badchar;
|
||||
}
|
||||
|
||||
case '#': {
|
||||
// TODO: This does not handle escaped private property names due to being extremely difficult
|
||||
// in the current state of the tokenizer. If #1351107 is ported, it becomes straightforward.
|
||||
c = getCharIgnoreEOL();
|
||||
// '$' and '_' are not in IsUnicodeIDStart
|
||||
c1kind = FirstCharKind(firstCharKinds[c]);
|
||||
if (c1kind == Ident || unicode::IsUnicodeIDStart(char16_t(c))) {
|
||||
identStart = userbuf.addressOfNextRawChar() - 2;
|
||||
hadUnicodeEscape = false;
|
||||
identVisibility = NameVisibility::Private;
|
||||
goto identifier;
|
||||
}
|
||||
ungetCharIgnoreEOL(c);
|
||||
goto badchar;
|
||||
}
|
||||
|
||||
case '|':
|
||||
if (matchChar('|'))
|
||||
tp->type = TOK_OR;
|
||||
tp->type = matchChar('=') ? TOK_ORASSIGN : TOK_OR;
|
||||
else
|
||||
tp->type = matchChar('=') ? TOK_BITORASSIGN : TOK_BITOR;
|
||||
goto out;
|
||||
|
|
@ -1792,7 +1887,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
|
||||
case '&':
|
||||
if (matchChar('&'))
|
||||
tp->type = TOK_AND;
|
||||
tp->type = matchChar('=') ? TOK_ANDASSIGN : TOK_AND;
|
||||
else
|
||||
tp->type = matchChar('=') ? TOK_BITANDASSIGN : TOK_BITAND;
|
||||
goto out;
|
||||
|
|
@ -1811,8 +1906,10 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
ungetCharIgnoreEOL(c);
|
||||
tp->type = TOK_OPTCHAIN;
|
||||
}
|
||||
} else {
|
||||
tp->type = matchChar('?') ? TOK_COALESCE : TOK_HOOK;
|
||||
} else if (matchChar('?')) {
|
||||
tp->type = matchChar('=') ? TOK_COALESCEASSIGN : TOK_COALESCE;
|
||||
} else {
|
||||
tp->type = TOK_HOOK;
|
||||
}
|
||||
goto out;
|
||||
|
||||
|
|
@ -2247,7 +2344,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
|||
updateFlagsForEOL();
|
||||
} else if (c == LINE_SEPARATOR || c == PARA_SEPARATOR) {
|
||||
// U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR encode
|
||||
// their literal values in template literals and (as of the
|
||||
// their literal values in template literals and (as of the
|
||||
// JSON superset proposal) string literals, but they still count
|
||||
// as line terminators when computing line/column coordinates.
|
||||
updateLineInfoForEOL();
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ enum class InvalidEscapeType {
|
|||
Octal
|
||||
};
|
||||
|
||||
enum class NameVisibility { Public, Private };
|
||||
|
||||
class TokenStream;
|
||||
|
||||
struct Token
|
||||
|
|
@ -185,7 +187,7 @@ struct Token
|
|||
// Mutators
|
||||
|
||||
void setName(PropertyName* name) {
|
||||
MOZ_ASSERT(type == TOK_NAME);
|
||||
MOZ_ASSERT(type == TOK_NAME || type == TOK_PRIVATE_NAME);
|
||||
u.name = name;
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +213,7 @@ struct Token
|
|||
// Type-safe accessors
|
||||
|
||||
PropertyName* name() const {
|
||||
MOZ_ASSERT(type == TOK_NAME);
|
||||
MOZ_ASSERT(type == TOK_NAME || type == TOK_PRIVATE_NAME);
|
||||
return u.name->JSAtom::asPropertyName(); // poor-man's type verification
|
||||
}
|
||||
|
||||
|
|
@ -244,17 +246,14 @@ class CompileError : public JSErrorReport {
|
|||
void throwError(JSContext* cx);
|
||||
};
|
||||
|
||||
extern TokenKind
|
||||
ReservedWordTokenKind(PropertyName* str);
|
||||
|
||||
extern const char*
|
||||
ReservedWordToCharZ(PropertyName* str);
|
||||
|
||||
extern MOZ_MUST_USE bool
|
||||
IsFutureReservedWord(JSLinearString* str);
|
||||
|
||||
extern MOZ_MUST_USE bool
|
||||
IsReservedWordLiteral(JSLinearString* str);
|
||||
|
||||
extern MOZ_MUST_USE bool
|
||||
IsStrictReservedWord(JSLinearString* str);
|
||||
extern const char*
|
||||
ReservedWordToCharZ(TokenKind tt);
|
||||
|
||||
// Ideally, tokenizing would be entirely independent of context. But the
|
||||
// strict mode flag, which is in SharedContext, affects tokenizing, and
|
||||
|
|
@ -347,7 +346,7 @@ class MOZ_STACK_CLASS TokenStream
|
|||
|
||||
public:
|
||||
PropertyName* currentName() const {
|
||||
if (isCurrentTokenType(TOK_NAME)) {
|
||||
if (isCurrentTokenType(TOK_NAME) || isCurrentTokenType(TOK_PRIVATE_NAME)) {
|
||||
return currentToken().name();
|
||||
}
|
||||
|
||||
|
|
@ -355,6 +354,16 @@ class MOZ_STACK_CLASS TokenStream
|
|||
return reservedWordToPropertyName(currentToken().type);
|
||||
}
|
||||
|
||||
bool currentNameHasEscapes() const {
|
||||
if (isCurrentTokenType(TOK_NAME) || isCurrentTokenType(TOK_PRIVATE_NAME)) {
|
||||
TokenPos pos = currentToken().pos;
|
||||
return (pos.end - pos.begin) != currentToken().name()->length();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type));
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyName* nextName() const {
|
||||
if (nextToken().type != TOK_NAME) {
|
||||
return nextToken().name();
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch state
|
|||
MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
|
||||
MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
|
||||
MSG_DEF(JSMSG_BAD_SUPERCALL, 0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
|
||||
MSG_DEF(JSMSG_BAD_ARGUMENTS, 0, JSEXN_SYNTAXERR, "arguments is not valid in fields")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
|
||||
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
|
||||
|
|
@ -264,6 +265,7 @@ MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'fr
|
|||
MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
|
||||
MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
|
||||
MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
|
||||
MSG_DEF(JSMSG_MISSING_PRIVATE_NAME, 0, JSEXN_SYNTAXERR, "'#' not followed by identifier")
|
||||
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character")
|
||||
MSG_DEF(JSMSG_IMPORT_META_OUTSIDE_MODULE, 0, JSEXN_SYNTAXERR, "import.meta may only appear in a module")
|
||||
MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
|
||||
|
|
@ -345,6 +347,7 @@ MSG_DEF(JSMSG_UNNAMED_CLASS_STMT, 0, JSEXN_SYNTAXERR, "class statement requ
|
|||
MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 0, JSEXN_SYNTAXERR, "unterminated comment")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_STATIC_CLASS_BLOCK, 0, JSEXN_SYNTAXERR, "unterminated static class block")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_STRING, 0, JSEXN_SYNTAXERR, "unterminated string literal")
|
||||
MSG_DEF(JSMSG_USELESS_EXPR, 0, JSEXN_TYPEERR, "useless expression")
|
||||
MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 0, JSEXN_SYNTAXERR, "\"use asm\" is only meaningful in the Directive Prologue of a function body")
|
||||
|
|
@ -360,6 +363,7 @@ MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allo
|
|||
MSG_DEF(JSMSG_BAD_NEW_OPTIONAL, 0, JSEXN_SYNTAXERR, "new keyword cannot be used with an optional chain")
|
||||
MSG_DEF(JSMSG_BAD_OPTIONAL_TEMPLATE, 0, JSEXN_SYNTAXERR, "tagged template cannot be used with optional chain")
|
||||
MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
|
||||
MSG_DEF(JSMSG_FIELDS_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "fields are not currently supported")
|
||||
|
||||
// asm.js
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
|
||||
|
|
|
|||
|
|
@ -4429,7 +4429,7 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
|
|||
return false;
|
||||
|
||||
// If name is not valid identifier
|
||||
if (!js::frontend::IsIdentifier(name, nameLen))
|
||||
if (!js::frontend::IsIdentifier(reinterpret_cast<const Latin1Char*>(name), nameLen))
|
||||
isInvalidName = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,4 +86,6 @@ ASTDEF(AST_COMPUTED_NAME, "ComputedName", "computedNam
|
|||
|
||||
ASTDEF(AST_CLASS_STMT, "ClassStatement", "classStatement")
|
||||
ASTDEF(AST_CLASS_METHOD, "ClassMethod", "classMethod")
|
||||
ASTDEF(AST_CLASS_FIELD, "ClassField", "classField")
|
||||
ASTDEF(AST_STATIC_CLASS_BLOCK, "StaticClassBlock", "staticClassBlock")
|
||||
/* AST_LIMIT = last + 1 */
|
||||
|
|
|
|||
|
|
@ -1286,6 +1286,19 @@ JSFunction::isDerivedClassConstructor()
|
|||
return derived;
|
||||
}
|
||||
|
||||
bool
|
||||
JSFunction::isFieldInitializer() const
|
||||
{
|
||||
bool init;
|
||||
if (isInterpretedLazy()) {
|
||||
init = lazyScript()->isFieldInitializer();
|
||||
} else {
|
||||
init = nonLazyScript()->isFieldInitializer();
|
||||
}
|
||||
MOZ_ASSERT_IF(init, isMethod());
|
||||
return init;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
JSFunction::getLength(JSContext* cx, HandleFunction fun, uint16_t* length)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -595,6 +595,7 @@ class JSFunction : public js::NativeObject
|
|||
}
|
||||
|
||||
bool isDerivedClassConstructor();
|
||||
bool isFieldInitializer() const;
|
||||
|
||||
static unsigned offsetOfNativeOrScript() {
|
||||
static_assert(offsetof(U, n.native) == offsetof(U, i.s.script_),
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
|||
uint32_t toStringEnd = script->toStringEnd();
|
||||
uint32_t lineno = script->lineno();
|
||||
uint32_t column = script->column();
|
||||
uint32_t numFieldInitializers;
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
packedFields = lazy->packedFields();
|
||||
|
|
@ -251,10 +252,17 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
|||
// relazify scripts with inner functions. See
|
||||
// JSFunction::createScriptForLazilyInterpretedFunction.
|
||||
MOZ_ASSERT(lazy->numInnerFunctions() == 0);
|
||||
if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
|
||||
numFieldInitializers = (uint32_t)lazy->getFieldInitializers().numFieldInitializers;
|
||||
} else {
|
||||
numFieldInitializers = UINT32_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (!xdr->codeUint64(&packedFields))
|
||||
return false;
|
||||
if (!xdr->codeUint32(&numFieldInitializers))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
RootedScriptSource sourceObject(cx, &script->scriptSourceUnwrap());
|
||||
|
|
@ -265,6 +273,9 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
|||
return false;
|
||||
|
||||
lazy->setToStringEnd(toStringEnd);
|
||||
if (numFieldInitializers != UINT32_MAX) {
|
||||
lazy->setFieldInitializers(FieldInitializers((size_t)numFieldInitializers));
|
||||
}
|
||||
|
||||
// As opposed to XDRLazyScript, we need to restore the runtime bits
|
||||
// of the script, as we are trying to match the fact this function
|
||||
|
|
@ -339,6 +350,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
NeedsHomeObject,
|
||||
IsDerivedClassConstructor,
|
||||
IsDefaultClassConstructor,
|
||||
IsFieldInitializer,
|
||||
};
|
||||
|
||||
uint32_t length, lineno, column, nfixed, nslots;
|
||||
|
|
@ -463,6 +475,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
scriptBits |= (1 << IsDerivedClassConstructor);
|
||||
if (script->isDefaultClassConstructor())
|
||||
scriptBits |= (1 << IsDefaultClassConstructor);
|
||||
if (script->isFieldInitializer())
|
||||
scriptBits |= (1 << IsFieldInitializer);
|
||||
}
|
||||
|
||||
if (!xdr->codeUint32(&prologueLength))
|
||||
|
|
@ -609,6 +623,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
script->isDerivedClassConstructor_ = true;
|
||||
if (scriptBits & (1 << IsDefaultClassConstructor))
|
||||
script->isDefaultClassConstructor_ = true;
|
||||
if (scriptBits & (1 << IsFieldInitializer))
|
||||
script->isFieldInitializer_ = true;
|
||||
|
||||
if (scriptBits & (1 << IsLegacyGenerator)) {
|
||||
MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
|
||||
|
|
@ -975,6 +991,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
uint32_t lineno;
|
||||
uint32_t column;
|
||||
uint64_t packedFields;
|
||||
uint32_t numFieldInitializers;
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
// Note: it's possible the LazyScript has a non-null script_ pointer
|
||||
|
|
@ -990,13 +1007,19 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
lineno = lazy->lineno();
|
||||
column = lazy->column();
|
||||
packedFields = lazy->packedFields();
|
||||
if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
|
||||
numFieldInitializers = (uint32_t)lazy->getFieldInitializers().numFieldInitializers;
|
||||
} else {
|
||||
numFieldInitializers = UINT32_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
|
||||
!xdr->codeUint32(&toStringStart) ||
|
||||
!xdr->codeUint32(&toStringEnd) ||
|
||||
!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
|
||||
!xdr->codeUint64(&packedFields))
|
||||
!xdr->codeUint64(&packedFields) ||
|
||||
!xdr->codeUint32(&numFieldInitializers))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1007,6 +1030,9 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
if (!lazy)
|
||||
return false;
|
||||
lazy->setToStringEnd(toStringEnd);
|
||||
if (numFieldInitializers != UINT32_MAX) {
|
||||
lazy->setFieldInitializers(FieldInitializers((size_t)numFieldInitializers));
|
||||
}
|
||||
fun->initLazyScript(lazy);
|
||||
}
|
||||
}
|
||||
|
|
@ -2786,6 +2812,7 @@ JSScript::initFromFunctionBox(ExclusiveContext* cx, HandleScript script,
|
|||
script->funHasExtensibleScope_ = funbox->hasExtensibleScope();
|
||||
script->needsHomeObject_ = funbox->needsHomeObject();
|
||||
script->isDerivedClassConstructor_ = funbox->isDerivedClassConstructor();
|
||||
script->isFieldInitializer_ = funbox->isFieldInitializer();
|
||||
|
||||
if (funbox->argumentsHasLocalBinding()) {
|
||||
script->setArgumentsHasVarBinding();
|
||||
|
|
@ -3464,6 +3491,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
|||
dst->isGeneratorExp_ = src->isGeneratorExp();
|
||||
dst->setGeneratorKind(src->generatorKind());
|
||||
dst->isDerivedClassConstructor_ = src->isDerivedClassConstructor();
|
||||
dst->isFieldInitializer_ = src->isFieldInitializer();
|
||||
dst->needsHomeObject_ = src->needsHomeObject();
|
||||
dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor();
|
||||
dst->isAsync_ = src->asyncKind() == AsyncFunction;
|
||||
|
|
@ -4110,6 +4138,7 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
|
|||
sourceObject_(nullptr),
|
||||
table_(table),
|
||||
packedFields_(packedFields),
|
||||
fieldInitializers_(FieldInitializers::Invalid()),
|
||||
begin_(begin),
|
||||
end_(end),
|
||||
toStringStart_(toStringStart),
|
||||
|
|
|
|||
|
|
@ -704,6 +704,35 @@ class ScriptSourceObject : public NativeObject
|
|||
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
|
||||
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
|
||||
GeneratorKindAsBits(GeneratorKind generatorKind) {
|
||||
return static_cast<unsigned>(generatorKind);
|
||||
|
|
@ -855,6 +884,8 @@ class JSScript : public js::gc::TenuredCell
|
|||
|
||||
private:
|
||||
js::SharedScriptData* scriptData_;
|
||||
|
||||
js::FieldInitializers fieldInitializers_ = js::FieldInitializers::Invalid();
|
||||
public:
|
||||
uint8_t* data; /* pointer to variable-length data array (see
|
||||
comment above Create() for details) */
|
||||
|
|
@ -1089,6 +1120,8 @@ class JSScript : public js::gc::TenuredCell
|
|||
bool isDerivedClassConstructor_:1;
|
||||
bool isDefaultClassConstructor_:1;
|
||||
|
||||
bool isFieldInitializer_:1;
|
||||
|
||||
bool isAsync_:1;
|
||||
|
||||
bool hasRest_:1;
|
||||
|
|
@ -1098,7 +1131,10 @@ class JSScript : public js::gc::TenuredCell
|
|||
// instead of private to suppress -Wunused-private-field compiler warnings.
|
||||
protected:
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
// Currently no padding is needed.
|
||||
# ifndef DEBUG
|
||||
// DEBUG is currently 4 bytes larger and doesn't need padding to gc::CellSize
|
||||
uint32_t padding_;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//
|
||||
|
|
@ -1427,6 +1463,10 @@ class JSScript : public js::gc::TenuredCell
|
|||
return isDerivedClassConstructor_;
|
||||
}
|
||||
|
||||
bool isFieldInitializer() const {
|
||||
return isFieldInitializer_;
|
||||
}
|
||||
|
||||
/*
|
||||
* As an optimization, even when argsHasLocalBinding, the function prologue
|
||||
* may not need to create an arguments object. This is determined by
|
||||
|
|
@ -1454,6 +1494,11 @@ class JSScript : public js::gc::TenuredCell
|
|||
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
|
||||
* location for the argument. If an arguments object exists AND it's mapped
|
||||
|
|
@ -2003,7 +2048,6 @@ namespace js {
|
|||
// bytecode from its source.
|
||||
class LazyScript : public gc::TenuredCell
|
||||
{
|
||||
private:
|
||||
// If non-nullptr, the script has been compiled and this is a forwarding
|
||||
// pointer to the result. This is a weak pointer: after relazification, we
|
||||
// can collect the script if there are no other pointers to it.
|
||||
|
|
@ -2030,7 +2074,6 @@ class LazyScript : public gc::TenuredCell
|
|||
uint32_t padding;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static const uint32_t NumClosedOverBindingsBits = 20;
|
||||
static const uint32_t NumInnerFunctionsBits = 20;
|
||||
|
||||
|
|
@ -2062,6 +2105,7 @@ class LazyScript : public gc::TenuredCell
|
|||
uint32_t hasBeenCloned : 1;
|
||||
uint32_t treatAsRunOnce : 1;
|
||||
uint32_t isDerivedClassConstructor : 1;
|
||||
uint32_t isFieldInitializer : 1;
|
||||
uint32_t needsHomeObject : 1;
|
||||
uint32_t hasRest : 1;
|
||||
uint32_t parseGoal : 1;
|
||||
|
|
@ -2072,6 +2116,8 @@ class LazyScript : public gc::TenuredCell
|
|||
uint64_t packedFields_;
|
||||
};
|
||||
|
||||
FieldInitializers fieldInitializers_;
|
||||
|
||||
// Source location for the script.
|
||||
// See the comment in JSScript for the details.
|
||||
uint32_t begin_;
|
||||
|
|
@ -2276,6 +2322,13 @@ class LazyScript : public gc::TenuredCell
|
|||
p_.isDerivedClassConstructor = true;
|
||||
}
|
||||
|
||||
bool isFieldInitializer() const {
|
||||
return p_.isFieldInitializer;
|
||||
}
|
||||
void setIsFieldInitializer() {
|
||||
p_.isFieldInitializer = true;
|
||||
}
|
||||
|
||||
bool needsHomeObject() const {
|
||||
return p_.needsHomeObject;
|
||||
}
|
||||
|
|
@ -2297,6 +2350,12 @@ class LazyScript : public gc::TenuredCell
|
|||
p_.hasThisBinding = true;
|
||||
}
|
||||
|
||||
void setFieldInitializers(FieldInitializers fieldInitializers) {
|
||||
fieldInitializers_ = fieldInitializers;
|
||||
}
|
||||
|
||||
const FieldInitializers& getFieldInitializers() const { return fieldInitializers_; }
|
||||
|
||||
const char* filename() const {
|
||||
return scriptSource()->filename();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,14 +140,18 @@ main_deunified_sources = [
|
|||
'frontend/BytecodeControlStructures.cpp',
|
||||
'frontend/BytecodeEmitter.cpp',
|
||||
'frontend/CallOrNewEmitter.cpp',
|
||||
'frontend/DefaultEmitter.cpp',
|
||||
'frontend/ElemOpEmitter.cpp',
|
||||
'frontend/EmitterScope.cpp',
|
||||
'frontend/FoldConstants.cpp',
|
||||
'frontend/ForOfLoopControl.cpp',
|
||||
'frontend/FunctionEmitter.cpp',
|
||||
'frontend/IfEmitter.cpp',
|
||||
'frontend/JumpList.cpp',
|
||||
'frontend/LexicalScopeEmitter.cpp',
|
||||
'frontend/NameFunctions.cpp',
|
||||
'frontend/NameOpEmitter.cpp',
|
||||
'frontend/ObjectEmitter.cpp',
|
||||
'frontend/ParseNode.cpp',
|
||||
'frontend/PropOpEmitter.cpp',
|
||||
'frontend/SwitchEmitter.cpp',
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
macro(anonymous, anonymous, "anonymous") \
|
||||
macro(Any, Any, "Any") \
|
||||
macro(apply, apply, "apply") \
|
||||
macro(args, args, "args") \
|
||||
macro(arguments, arguments, "arguments") \
|
||||
macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \
|
||||
macro(ArrayIterator, ArrayIterator, "Array Iterator") \
|
||||
|
|
@ -102,6 +103,10 @@
|
|||
macro(dotAll, dotAll, "dotAll") \
|
||||
macro(dotGenerator, dotGenerator, ".generator") \
|
||||
macro(dotThis, dotThis, ".this") \
|
||||
macro(dotInitializers, dotInitializers, ".initializers") \
|
||||
macro(dotFieldKeys, dotFieldKeys, ".fieldKeys") \
|
||||
macro(dotStaticInitializers, dotStaticInitializers, ".staticInitializers") \
|
||||
macro(dotStaticFieldKeys, dotStaticFieldKeys, ".staticFieldKeys") \
|
||||
macro(each, each, "each") \
|
||||
macro(elementType, elementType, "elementType") \
|
||||
macro(else, else_, "else") \
|
||||
|
|
|
|||
|
|
@ -1890,6 +1890,7 @@
|
|||
macro(JSOP_UNPICK, 183,"unpick", NULL, 2, 0, 0, JOF_UINT8) \
|
||||
/*
|
||||
* Pops the top of stack value, pushes property of it onto the stack.
|
||||
* Requires the value under 'obj' to be the receiver of the following call.
|
||||
*
|
||||
* Like JSOP_GETPROP but for call context.
|
||||
* Category: Literals
|
||||
|
|
@ -1974,7 +1975,8 @@
|
|||
\
|
||||
/*
|
||||
* Pops the top two values on the stack as 'propval' and 'obj', pushes
|
||||
* 'propval' property of 'obj' onto the stack.
|
||||
* 'propval' property of 'obj' onto the stack. Requires the value under
|
||||
* 'obj' to be the receiver of the following call.
|
||||
*
|
||||
* Like JSOP_GETELEM but for call context.
|
||||
* Category: Literals
|
||||
|
|
|
|||
|
|
@ -761,8 +761,8 @@ FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosin
|
|||
MOZ_ASSERT(!data->nextFrameSlot);
|
||||
}
|
||||
|
||||
scope.set(createWithData(cx, &uniqueData.ref(), hasParameterExprs, needsEnvironment, fun,
|
||||
enclosing));
|
||||
scope.set(createWithData(cx, &uniqueData.ref(), hasParameterExprs,
|
||||
needsEnvironment, fun, enclosing));
|
||||
if (!scope)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -548,8 +548,9 @@ class FunctionScope : public Scope
|
|||
|
||||
private:
|
||||
static FunctionScope* createWithData(ExclusiveContext* cx, MutableHandle<UniquePtr<Data>> data,
|
||||
bool hasParameterExprs, bool needsEnvironment,
|
||||
HandleFunction fun, HandleScope enclosing);
|
||||
bool hasParameterExprs,
|
||||
bool needsEnvironment, HandleFunction fun,
|
||||
HandleScope enclosing);
|
||||
|
||||
Data& data() {
|
||||
return *reinterpret_cast<Data*>(data_);
|
||||
|
|
|
|||
|
|
@ -7045,7 +7045,7 @@ ParseFunction(ModuleValidator& m, FunctionNode** funNodeOut, unsigned* line)
|
|||
return false;
|
||||
|
||||
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
|
||||
FunctionNode* funNode = m.parser().handler.newFunction(syntaxKind);
|
||||
FunctionNode* funNode = m.parser().handler.newFunction(syntaxKind, m.parser().pos());
|
||||
if (!funNode)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue