Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-05-01 14:44:54 +08:00
commit a248550543
45 changed files with 6846 additions and 1402 deletions

View file

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

View file

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

View file

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

View file

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

View 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;
}

View 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 */

View 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 */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View 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 */

View 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;
}

View 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 */

View file

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

View 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;
}

View 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 */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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_),

View file

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

View file

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

View file

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

View file

@ -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") \

View file

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

View file

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

View file

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

View file

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