mirror of
https://github.com/roytam1/basilisk55.git
synced 2026-05-26 15:02:46 +00:00
import from UXP: Issue #2142 - Add PropertyEmitter, ObjectEmitter, ClassEmitter, LexicalScopeEmitter, DefaultEmitter (1b89be6d)
This commit is contained in:
+335
-291
@@ -31,11 +31,14 @@
|
||||
#include "ds/Nestable.h"
|
||||
#include "frontend/BytecodeControlStructures.h"
|
||||
#include "frontend/CallOrNewEmitter.h"
|
||||
#include "frontend/DefaultEmitter.h" // DefaultEmitter
|
||||
#include "frontend/ElemOpEmitter.h"
|
||||
#include "frontend/EmitterScope.h"
|
||||
#include "frontend/ForOfLoopControl.h"
|
||||
#include "frontend/IfEmitter.h"
|
||||
#include "frontend/LexicalScopeEmitter.h" // LexicalScopeEmitter
|
||||
#include "frontend/NameOpEmitter.h"
|
||||
#include "frontend/ObjectEmitter.h" // PropertyEmitter, ObjectEmitter, ClassEmitter
|
||||
#include "frontend/Parser.h"
|
||||
#include "frontend/PropOpEmitter.h"
|
||||
#include "frontend/SwitchEmitter.h"
|
||||
@@ -3010,62 +3013,70 @@ BytecodeEmitter::wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth, In
|
||||
bool
|
||||
BytecodeEmitter::emitDefault(ParseNode* defaultExpr, ParseNode* pattern)
|
||||
{
|
||||
if (!emit1(JSOP_DUP)) // VALUE VALUE
|
||||
// [stack] VALUE
|
||||
|
||||
DefaultEmitter de(this);
|
||||
if (!de.prepareForDefault()) {
|
||||
// [stack]
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // VALUE VALUE UNDEFINED
|
||||
}
|
||||
if (!emitInitializer(defaultExpr, pattern)) {
|
||||
// [stack] DEFAULTVALUE
|
||||
return false;
|
||||
if (!emit1(JSOP_STRICTEQ)) // VALUE EQL?
|
||||
return false;
|
||||
// Emit source note to enable ion compilation.
|
||||
if (!newSrcNote(SRC_IF))
|
||||
return false;
|
||||
JumpList jump;
|
||||
if (!emitJump(JSOP_IFEQ, &jump)) // VALUE
|
||||
return false;
|
||||
if (!emit1(JSOP_POP)) // .
|
||||
return false;
|
||||
if (!emitInitializerInBranch(defaultExpr, pattern)) // DEFAULTVALUE
|
||||
return false;
|
||||
if (!emitJumpTargetAndPatch(jump))
|
||||
}
|
||||
if (!de.emitEnd()) {
|
||||
// [stack] VALUE/DEFAULTVALUE
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name,
|
||||
FunctionPrefixKind prefixKind)
|
||||
BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name)
|
||||
{
|
||||
if (maybeFun->is<FunctionNode>()) {
|
||||
// Function doesn't have 'name' property at this point.
|
||||
// Set function's name at compile time.
|
||||
RootedFunction fun(cx, maybeFun->as<FunctionNode>().funbox()->function());
|
||||
return setFunName(maybeFun->as<FunctionNode>().funbox()->function(), name);
|
||||
}
|
||||
|
||||
// Single node can be emitted multiple times if it appears in
|
||||
// array destructuring default. If function already has a name,
|
||||
// just return.
|
||||
if (fun->hasCompileTimeName()) {
|
||||
MOZ_ASSERT(maybeFun->isKind(PNK_CLASS));
|
||||
|
||||
return emitSetClassConstructorName(name);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::setFunName(JSFunction* fun, JSAtom* name)
|
||||
{
|
||||
// Single node can be emitted multiple times if it appears in
|
||||
// array destructuring default. If function already has a name,
|
||||
// just return.
|
||||
if (fun->hasCompileTimeName()) {
|
||||
#ifdef DEBUG
|
||||
RootedAtom funName(cx, NameToFunctionName(cx, name, prefixKind));
|
||||
if (!funName)
|
||||
return false;
|
||||
MOZ_ASSERT(funName == fun->compileTimeName());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedAtom funName(cx, NameToFunctionName(cx, name, prefixKind));
|
||||
RootedAtom funName(cx, name);
|
||||
if (!funName)
|
||||
return false;
|
||||
fun->setCompileTimeName(name);
|
||||
MOZ_ASSERT(funName == fun->compileTimeName());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedAtom funName(cx, name);
|
||||
if (!funName)
|
||||
return false;
|
||||
fun->setCompileTimeName(funName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSetClassConstructorName(JSAtom* name)
|
||||
{
|
||||
uint32_t nameIndex;
|
||||
if (!makeAtomIndex(name, &nameIndex))
|
||||
return false;
|
||||
if (!emitIndexOp(JSOP_STRING, nameIndex)) // FUN NAME
|
||||
return false;
|
||||
uint8_t kind = uint8_t(prefixKind);
|
||||
uint8_t kind = uint8_t(FunctionPrefixKind::None);
|
||||
if (!emit2(JSOP_SETFUNNAME, kind)) // FUN
|
||||
return false;
|
||||
return true;
|
||||
@@ -3088,13 +3099,6 @@ BytecodeEmitter::emitInitializer(ParseNode* initializer, ParseNode* pattern)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern)
|
||||
{
|
||||
TDZCheckCache tdzCache(this);
|
||||
return emitInitializer(initializer, pattern);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, DestructuringFlavor flav)
|
||||
{
|
||||
@@ -4230,7 +4234,7 @@ BytecodeEmitter::emitCatch(TernaryNode* catchNode)
|
||||
break;
|
||||
|
||||
case PNK_NAME:
|
||||
if (!emitLexicalInitialization(pn2))
|
||||
if (!emitLexicalInitialization(&pn2->as<NameNode>()))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
@@ -4454,11 +4458,21 @@ BytecodeEmitter::emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLi
|
||||
MOZ_NEVER_INLINE bool
|
||||
BytecodeEmitter::emitLexicalScope(LexicalScopeNode* lexicalScope)
|
||||
{
|
||||
TDZCheckCache tdzCache(this);
|
||||
LexicalScopeEmitter lse(this);
|
||||
|
||||
ParseNode* body = lexicalScope->scopeBody();
|
||||
if (lexicalScope->isEmptyScope())
|
||||
return emitLexicalScopeBody(body);
|
||||
if (lexicalScope->isEmptyScope()) {
|
||||
if (!lse.emitEmptyScope())
|
||||
return false;
|
||||
|
||||
if (!emitLexicalScopeBody(body))
|
||||
return false;
|
||||
|
||||
if (!lse.emitEnd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update line number notes before emitting TDZ poison in
|
||||
// EmitterScope::enterLexical to avoid spurious pausing on seemingly
|
||||
@@ -4484,7 +4498,6 @@ BytecodeEmitter::emitLexicalScope(LexicalScopeNode* lexicalScope)
|
||||
return false;
|
||||
}
|
||||
|
||||
EmitterScope emitterScope(this);
|
||||
ScopeKind kind;
|
||||
if (body->isKind(PNK_CATCH)) {
|
||||
TernaryNode* catchNode = &body->as<TernaryNode>();
|
||||
@@ -4494,21 +4507,21 @@ BytecodeEmitter::emitLexicalScope(LexicalScopeNode* lexicalScope)
|
||||
} else
|
||||
kind = ScopeKind::Lexical;
|
||||
|
||||
if (!emitterScope.enterLexical(this, kind, lexicalScope->scopeBindings()))
|
||||
if (!lse.emitScope(kind, lexicalScope->scopeBindings()))
|
||||
return false;
|
||||
|
||||
if (body->isKind(PNK_FOR)) {
|
||||
// for loops need to emit {FRESHEN,RECREATE}LEXICALENV if there are
|
||||
// lexical declarations in the head. Signal this by passing a
|
||||
// non-nullptr lexical scope.
|
||||
if (!emitFor(&body->as<ForNode>(), &emitterScope))
|
||||
if (!emitFor(&body->as<ForNode>(), &lse.emitterScope()))
|
||||
return false;
|
||||
} else {
|
||||
if (!emitLexicalScopeBody(body, SUPPRESS_LINENOTE))
|
||||
return false;
|
||||
}
|
||||
|
||||
return emitterScope.leave(this);
|
||||
return lse.emitEnd();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -4834,7 +4847,7 @@ BytecodeEmitter::emitInitializeForInOrOfTarget(TernaryNode* forHead)
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitForOf(ForNode* forNode, EmitterScope* headLexicalEmitterScope)
|
||||
BytecodeEmitter::emitForOf(ForNode* forNode, const EmitterScope* headLexicalEmitterScope)
|
||||
{
|
||||
MOZ_ASSERT(forNode->isKind(PNK_FOR));
|
||||
|
||||
@@ -5007,7 +5020,7 @@ BytecodeEmitter::emitForOf(ForNode* forNode, EmitterScope* headLexicalEmitterSco
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitForIn(ForNode* forNode, EmitterScope* headLexicalEmitterScope)
|
||||
BytecodeEmitter::emitForIn(ForNode* forNode, const EmitterScope* headLexicalEmitterScope)
|
||||
{
|
||||
MOZ_ASSERT(forNode->isKind(PNK_FOR));
|
||||
MOZ_ASSERT(forNode->isOp(JSOP_ITER));
|
||||
@@ -5156,7 +5169,7 @@ BytecodeEmitter::emitForIn(ForNode* forNode, EmitterScope* headLexicalEmitterSco
|
||||
|
||||
/* C-style `for (init; cond; update) ...` loop. */
|
||||
bool
|
||||
BytecodeEmitter::emitCStyleFor(ForNode* forNode, EmitterScope* headLexicalEmitterScope)
|
||||
BytecodeEmitter::emitCStyleFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope)
|
||||
{
|
||||
LoopControl loopInfo(this, StatementKind::ForLoop);
|
||||
|
||||
@@ -5342,7 +5355,7 @@ BytecodeEmitter::emitCStyleFor(ForNode* forNode, EmitterScope* headLexicalEmitte
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitFor(ForNode* forNode, EmitterScope* headLexicalEmitterScope)
|
||||
BytecodeEmitter::emitFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope)
|
||||
{
|
||||
MOZ_ASSERT(forNode->isKind(PNK_FOR));
|
||||
|
||||
@@ -7792,8 +7805,10 @@ BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitPropertyList(ListNode* obj, MutableHandlePlainObject objp, PropListType type)
|
||||
BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe, PropListType type)
|
||||
{
|
||||
// [stack] CTOR? OBJ
|
||||
|
||||
for (ParseNode* propdef : obj->contents()) {
|
||||
if (propdef->is<ClassField>()) {
|
||||
// Skip over class fields and emit them at the end. This is needed
|
||||
@@ -7801,163 +7816,235 @@ BytecodeEmitter::emitPropertyList(ListNode* obj, MutableHandlePlainObject objp,
|
||||
// into a local variable
|
||||
continue;
|
||||
}
|
||||
if (!updateSourceCoordNotes(propdef->pn_pos.begin))
|
||||
return false;
|
||||
|
||||
// Handle __proto__: v specially because *only* this form, and no other
|
||||
// involving "__proto__", performs [[Prototype]] mutation.
|
||||
if (propdef->isKind(PNK_MUTATEPROTO)) {
|
||||
// [stack] OBJ
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
if (!emitTree(propdef->as<UnaryNode>().kid()))
|
||||
if (!pe.prepareForProtoValue(Some(propdef->pn_pos.begin))) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
objp.set(nullptr);
|
||||
if (!emit1(JSOP_MUTATEPROTO))
|
||||
}
|
||||
if (!emitTree(propdef->as<UnaryNode>().kid())) {
|
||||
// [stack] OBJ PROTO
|
||||
return false;
|
||||
}
|
||||
if (!pe.emitMutateProto()) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propdef->isKind(PNK_SPREAD)) {
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
|
||||
if (!emit1(JSOP_DUP))
|
||||
// [stack] OBJ
|
||||
if (!pe.prepareForSpreadOperand(Some(propdef->pn_pos.begin))) {
|
||||
// [stack] OBJ OBJ
|
||||
return false;
|
||||
|
||||
if (!emitTree(propdef->as<UnaryNode>().kid()))
|
||||
}
|
||||
if (!emitTree(propdef->as<UnaryNode>().kid())) {
|
||||
// [stack] OBJ OBJ VAL
|
||||
return false;
|
||||
|
||||
if (!emitCopyDataProperties(CopyOption::Unfiltered))
|
||||
}
|
||||
if (!pe.emitSpread()) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
|
||||
objp.set(nullptr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool extraPop = false;
|
||||
if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) {
|
||||
extraPop = true;
|
||||
if (!emit1(JSOP_DUP2))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
|
||||
ParseNode* key = propdef->as<BinaryNode>().left();
|
||||
bool isIndex = false;
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
if (!emitNumberOp(key->as<NumericLiteral>().value()))
|
||||
return false;
|
||||
isIndex = true;
|
||||
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
|
||||
// EmitClass took care of constructor already.
|
||||
if (type == ClassBody && key->as<NameNode>().atom() == cx->names().constructor &&
|
||||
!propdef->as<ClassMethod>().isStatic())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(key->isKind(PNK_COMPUTED_NAME));
|
||||
if (!emitComputedPropertyName(&key->as<UnaryNode>()))
|
||||
return false;
|
||||
isIndex = true;
|
||||
}
|
||||
|
||||
/* Emit code for the property initializer. */
|
||||
ParseNode* propVal = propdef->as<BinaryNode>().right();
|
||||
if (!emitTree(propVal))
|
||||
return false;
|
||||
BinaryNode* prop = &propdef->as<BinaryNode>();
|
||||
|
||||
ParseNode* key = prop->left();
|
||||
ParseNode* propVal = prop->right();
|
||||
bool isPropertyAnonFunctionOrClass = propVal->isDirectRHSAnonFunction();
|
||||
JSOp op = propdef->getOp();
|
||||
MOZ_ASSERT(op == JSOP_INITPROP ||
|
||||
op == JSOP_INITPROP_GETTER ||
|
||||
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITPROP_GETTER ||
|
||||
op == JSOP_INITPROP_SETTER);
|
||||
|
||||
FunctionPrefixKind prefixKind = op == JSOP_INITPROP_GETTER ? FunctionPrefixKind::Get
|
||||
: op == JSOP_INITPROP_SETTER ? FunctionPrefixKind::Set
|
||||
: FunctionPrefixKind::None;
|
||||
auto emitValue = [this, &propVal, &pe]() {
|
||||
// [stack] CTOR? OBJ CTOR? KEY?
|
||||
|
||||
if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
|
||||
objp.set(nullptr);
|
||||
|
||||
if (propVal->is<FunctionNode>() &&
|
||||
propVal->as<FunctionNode>().funbox()->needsHomeObject()) {
|
||||
FunctionBox* funbox = propVal->as<FunctionNode>().funbox();
|
||||
MOZ_ASSERT(funbox->function()->allowSuperProperty());
|
||||
bool isAsync = funbox->isAsync();
|
||||
if (isAsync) {
|
||||
if (!emit1(JSOP_SWAP))
|
||||
return false;
|
||||
}
|
||||
if (!emit2(JSOP_INITHOMEOBJECT, isIndex + isAsync))
|
||||
if (!emitTree(propVal)) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY? VAL
|
||||
return false;
|
||||
if (isAsync) {
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Class methods are not enumerable.
|
||||
if (type == ClassBody) {
|
||||
switch (op) {
|
||||
case JSOP_INITPROP: op = JSOP_INITHIDDENPROP; break;
|
||||
case JSOP_INITPROP_GETTER: op = JSOP_INITHIDDENPROP_GETTER; break;
|
||||
case JSOP_INITPROP_SETTER: op = JSOP_INITHIDDENPROP_SETTER; break;
|
||||
default: MOZ_CRASH("Invalid op");
|
||||
}
|
||||
}
|
||||
if (propVal->is<FunctionNode>() &&
|
||||
propVal->as<FunctionNode>().funbox()->needsHomeObject()) {
|
||||
FunctionBox* funbox = propVal->as<FunctionNode>().funbox();
|
||||
MOZ_ASSERT(funbox->function()->allowSuperProperty());
|
||||
|
||||
if (isIndex) {
|
||||
objp.set(nullptr);
|
||||
switch (op) {
|
||||
case JSOP_INITPROP: op = JSOP_INITELEM; break;
|
||||
case JSOP_INITHIDDENPROP: op = JSOP_INITHIDDENELEM; break;
|
||||
case JSOP_INITPROP_GETTER: op = JSOP_INITELEM_GETTER; break;
|
||||
case JSOP_INITHIDDENPROP_GETTER: op = JSOP_INITHIDDENELEM_GETTER; break;
|
||||
case JSOP_INITPROP_SETTER: op = JSOP_INITELEM_SETTER; break;
|
||||
case JSOP_INITHIDDENPROP_SETTER: op = JSOP_INITHIDDENELEM_SETTER; break;
|
||||
default: MOZ_CRASH("Invalid op");
|
||||
}
|
||||
if (propVal->isDirectRHSAnonFunction()) {
|
||||
if (!emitDupAt(1))
|
||||
return false;
|
||||
if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind)))
|
||||
return false;
|
||||
}
|
||||
if (!emit1(op))
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING));
|
||||
|
||||
uint32_t index;
|
||||
if (!makeAtomIndex(key->as<NameNode>().atom(), &index))
|
||||
return false;
|
||||
|
||||
if (objp) {
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
MOZ_ASSERT(!IsHiddenInitOp(op));
|
||||
MOZ_ASSERT(!objp->inDictionaryMode());
|
||||
Rooted<jsid> id(cx, AtomToId(key->as<NameNode>().atom()));
|
||||
if (!NativeDefineProperty(cx, objp, id, UndefinedHandleValue, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
{
|
||||
if (!pe.emitInitHomeObject(funbox->asyncKind())) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY? FUN
|
||||
return false;
|
||||
}
|
||||
if (objp->inDictionaryMode())
|
||||
objp.set(nullptr);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (propVal->isDirectRHSAnonFunction()) {
|
||||
RootedAtom keyName(cx, key->as<NameNode>().atom());
|
||||
if (!setOrEmitSetFunName(propVal, keyName, prefixKind))
|
||||
return false;
|
||||
}
|
||||
if (!emitIndex32(op, index))
|
||||
PropertyEmitter::Kind kind =
|
||||
(type == ClassBody && propdef->as<ClassMethod>().isStatic())
|
||||
? PropertyEmitter::Kind::Static
|
||||
: PropertyEmitter::Kind::Prototype;
|
||||
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
// [stack] CTOR? OBJ
|
||||
if (!pe.prepareForIndexPropKey(Some(propdef->pn_pos.begin), kind)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
if (!emitNumberOp(key->as<NumericLiteral>().value())) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY
|
||||
return false;
|
||||
}
|
||||
if (!pe.prepareForIndexPropValue()) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY
|
||||
return false;
|
||||
}
|
||||
if (!emitValue()) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY VAL
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case JSOP_INITPROP:
|
||||
if (!pe.emitInitIndexProp(isPropertyAnonFunctionOrClass)) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case JSOP_INITPROP_GETTER:
|
||||
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||
if (!pe.emitInitIndexGetter()) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case JSOP_INITPROP_SETTER:
|
||||
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||
if (!pe.emitInitIndexSetter()) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid op");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extraPop) {
|
||||
if (!emit1(JSOP_POP))
|
||||
if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
|
||||
// EmitClass took care of constructor already.
|
||||
if (type == ClassBody && key->as<NameNode>().atom() == cx->names().constructor &&
|
||||
!propdef->as<ClassMethod>().isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pe.prepareForPropValue(Some(propdef->pn_pos.begin), kind)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
if (!emitValue()) {
|
||||
// [stack] CTOR? OBJ CTOR? VAL
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedFunction anonFunction(cx);
|
||||
if (isPropertyAnonFunctionOrClass) {
|
||||
MOZ_ASSERT(op == JSOP_INITPROP);
|
||||
|
||||
if (propVal->is<FunctionNode>()) {
|
||||
// When the value is function, we set the function's name
|
||||
// at the compile-time, instead of emitting SETFUNNAME.
|
||||
FunctionBox* funbox = propVal->as<FunctionNode>().funbox();
|
||||
anonFunction = funbox->function();
|
||||
} else {
|
||||
// Only object literal can have a property where key is
|
||||
// name and value is an anonymous class.
|
||||
//
|
||||
// ({ foo: class {} });
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
MOZ_ASSERT(propVal->isKind(PNK_CLASS));
|
||||
}
|
||||
}
|
||||
|
||||
RootedAtom keyAtom(cx, key->as<NameNode>().atom());
|
||||
switch (op) {
|
||||
case JSOP_INITPROP:
|
||||
if (!pe.emitInitProp(keyAtom, isPropertyAnonFunctionOrClass,
|
||||
anonFunction)) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case JSOP_INITPROP_GETTER:
|
||||
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||
if (!pe.emitInitGetter(keyAtom)) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case JSOP_INITPROP_SETTER:
|
||||
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||
if (!pe.emitInitSetter(keyAtom)) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default: MOZ_CRASH("Invalid op");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(key->isKind(PNK_COMPUTED_NAME));
|
||||
|
||||
// [stack] CTOR? OBJ
|
||||
|
||||
if (!pe.prepareForComputedPropKey(Some(propdef->pn_pos.begin), kind)) {
|
||||
// [stack] CTOR? OBJ CTOR?
|
||||
return false;
|
||||
}
|
||||
if (!emitTree(key->as<UnaryNode>().kid())) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY
|
||||
return false;
|
||||
}
|
||||
if (!pe.prepareForComputedPropValue()) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY
|
||||
return false;
|
||||
}
|
||||
if (!emitValue()) {
|
||||
// [stack] CTOR? OBJ CTOR? KEY VAL
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case JSOP_INITPROP:
|
||||
if (!pe.emitInitComputedProp(isPropertyAnonFunctionOrClass)) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case JSOP_INITPROP_GETTER:
|
||||
MOZ_ASSERT(isPropertyAnonFunctionOrClass);
|
||||
if (!pe.emitInitComputedGetter()) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case JSOP_INITPROP_SETTER:
|
||||
MOZ_ASSERT(isPropertyAnonFunctionOrClass);
|
||||
if (!pe.emitInitComputedSetter()) {
|
||||
// [stack] CTOR? OBJ
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid op");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8148,38 +8235,21 @@ BytecodeEmitter::emitObject(ListNode* objNode)
|
||||
if (!objNode->hasNonConstInitializer() && objNode->head() && checkSingletonContext())
|
||||
return emitSingletonInitialiser(objNode);
|
||||
|
||||
/*
|
||||
* 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__).
|
||||
*/
|
||||
ptrdiff_t offset = this->offset();
|
||||
if (!emitNewInit(JSProto_Object))
|
||||
// [stack]
|
||||
|
||||
ObjectEmitter oe(this);
|
||||
if (!oe.emitObject(objNode->count())) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
if (!emitPropertyList(objNode, oe, ObjectLiteral)) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
if (!oe.emitEnd()) {
|
||||
// [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(objNode->count());
|
||||
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
if (!emitPropertyList(objNode, &obj, ObjectLiteral))
|
||||
return false;
|
||||
|
||||
if (obj) {
|
||||
// The object survived and has a predictable shape: update the original
|
||||
// bytecode.
|
||||
if (!replaceNewInitWithNewObject(obj, offset))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8546,6 +8616,7 @@ BytecodeEmitter::emitFunctionFormalParameters(ListNode* paramsBody)
|
||||
// If we have an initializer, emit the initializer and assign it
|
||||
// to the argument slot. TDZ is taken care of afterwards.
|
||||
MOZ_ASSERT(hasParameterExprs);
|
||||
IfEmitter ifUndefined(this);
|
||||
if (!emitArgOp(JSOP_GETARG, argSlot))
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP))
|
||||
@@ -8554,17 +8625,13 @@ BytecodeEmitter::emitFunctionFormalParameters(ListNode* paramsBody)
|
||||
return false;
|
||||
if (!emit1(JSOP_STRICTEQ))
|
||||
return false;
|
||||
// Emit source note to enable Ion compilation.
|
||||
if (!newSrcNote(SRC_IF))
|
||||
return false;
|
||||
JumpList jump;
|
||||
if (!emitJump(JSOP_IFEQ, &jump))
|
||||
if (!ifUndefined.emitThen())
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
if (!emitInitializerInBranch(initializer, bindingElement))
|
||||
if (!emitInitializer(initializer, bindingElement))
|
||||
return false;
|
||||
if (!emitJumpTargetAndPatch(jump))
|
||||
if (!ifUndefined.emitEnd())
|
||||
return false;
|
||||
} else if (isRest) {
|
||||
if (!emit1(JSOP_REST))
|
||||
@@ -8730,9 +8797,15 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody)
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitLexicalInitialization(ParseNode* pn)
|
||||
BytecodeEmitter::emitLexicalInitialization(NameNode* pn)
|
||||
{
|
||||
NameOpEmitter noe(this, pn->name(), NameOpEmitter::Kind::Initialize);
|
||||
return emitLexicalInitialization(pn->name());
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitLexicalInitialization(JSAtom* name)
|
||||
{
|
||||
NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize);
|
||||
if (!noe.prepareForRhs()) {
|
||||
return false;
|
||||
}
|
||||
@@ -8748,7 +8821,6 @@ BytecodeEmitter::emitLexicalInitialization(ParseNode* pn)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class AutoResetFieldInitializers
|
||||
{
|
||||
BytecodeEmitter* bce;
|
||||
@@ -8791,106 +8863,78 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
|
||||
// set this->fieldInitializers_
|
||||
AutoResetFieldInitializers _innermostClassAutoReset(this, setupFieldInitializers(classMembers));
|
||||
|
||||
bool savedStrictness = sc->setLocalStrictMode(true);
|
||||
// [stack]
|
||||
|
||||
ClassEmitter ce(this);
|
||||
RootedAtom innerName(cx);
|
||||
ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
|
||||
|
||||
Maybe<TDZCheckCache> tdzCache;
|
||||
Maybe<EmitterScope> emitterScope;
|
||||
if (names) {
|
||||
tdzCache.emplace(this);
|
||||
emitterScope.emplace(this);
|
||||
if (!emitterScope->enterLexical(this, ScopeKind::Lexical, classNode->scopeBindings()))
|
||||
innerName = names->innerBinding()->as<NameNode>().atom();
|
||||
MOZ_ASSERT(innerName);
|
||||
|
||||
if (names->outerBinding()) {
|
||||
MOZ_ASSERT(names->outerBinding()->as<NameNode>().atom());
|
||||
MOZ_ASSERT(names->outerBinding()->as<NameNode>().atom() == innerName);
|
||||
kind = ClassEmitter::Kind::Declaration;
|
||||
}
|
||||
|
||||
if (!ce.emitScopeForNamedClass(classNode->scopeBindings())) {
|
||||
// [stack]
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This is kind of silly. In order to the get the home object defined on
|
||||
// the constructor, we have to make it second, but we want the prototype
|
||||
// on top for EmitPropertyList, because we expect static properties to be
|
||||
// rarer. The result is a few more swaps than we would like. Such is life.
|
||||
if (heritageExpression) {
|
||||
if (!emitTree(heritageExpression))
|
||||
bool isDerived = !!heritageExpression;
|
||||
if (isDerived) {
|
||||
if (!emitTree(heritageExpression)) {
|
||||
// [stack] HERITAGE
|
||||
return false;
|
||||
if (!emit1(JSOP_CLASSHERITAGE))
|
||||
return false;
|
||||
if (!emit1(JSOP_OBJWITHPROTO))
|
||||
return false;
|
||||
|
||||
// JSOP_CLASSHERITAGE leaves both protos on the stack. After
|
||||
// creating the prototype, swap it to the bottom to make the
|
||||
// constructor.
|
||||
if (!emit1(JSOP_SWAP))
|
||||
}
|
||||
if (!ce.emitDerivedClass(innerName)) {
|
||||
// [stack] HERITAGE HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitNewInit(JSProto_Object))
|
||||
if (!ce.emitClass(innerName)) {
|
||||
// [stack] HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (constructor) {
|
||||
if (!emitFunction(constructor, !!heritageExpression))
|
||||
bool needsHomeObject = constructor->funbox()->needsHomeObject();
|
||||
// HERITAGE is consumed inside emitFunction.
|
||||
if (!emitFunction(constructor, isDerived)) {
|
||||
// [stack] HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
if (!ce.emitInitConstructor(needsHomeObject)) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
if (constructor->funbox()->needsHomeObject()) {
|
||||
if (!emit2(JSOP_INITHOMEOBJECT, 0))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 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.
|
||||
ptrdiff_t classStart = ptrdiff_t(classNode->pn_pos.begin);
|
||||
ptrdiff_t classEnd = ptrdiff_t(classNode->pn_pos.end);
|
||||
if (!newSrcNote3(SRC_CLASS_SPAN, classStart, classEnd))
|
||||
if (!ce.emitInitDefaultConstructor(Some(classNode->pn_pos.begin),
|
||||
Some(classNode->pn_pos.end))) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
|
||||
JSAtom *name = names ? names->innerBinding()->as<NameNode>().atom() : cx->names().empty;
|
||||
if (heritageExpression) {
|
||||
if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR))
|
||||
return false;
|
||||
} else {
|
||||
if (!emitAtomOp(name, JSOP_CLASSCONSTRUCTOR))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_SWAP))
|
||||
if (!emitPropertyList(classMembers, ce, ClassBody)) {
|
||||
// [stack] CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
if (!ce.emitEnd(kind)) {
|
||||
// [stack] # class declaration
|
||||
// [stack]
|
||||
// [stack] # class expression
|
||||
// [stack] CTOR
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_DUP2))
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP))
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().constructor, JSOP_INITHIDDENPROP))
|
||||
return false;
|
||||
|
||||
RootedPlainObject obj(cx);
|
||||
if (!emitPropertyList(classMembers, &obj, ClassBody))
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
|
||||
if (names) {
|
||||
ParseNode* innerName = names->innerBinding();
|
||||
if (!emitLexicalInitialization(innerName))
|
||||
return false;
|
||||
|
||||
// Pop the inner scope.
|
||||
if (!emitterScope->leave(this))
|
||||
return false;
|
||||
emitterScope.reset();
|
||||
|
||||
ParseNode* outerName = names->outerBinding();
|
||||
if (outerName) {
|
||||
if (!emitLexicalInitialization(outerName))
|
||||
return false;
|
||||
// Only class statements make outer bindings, and they do not leave
|
||||
// themselves on the stack.
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(sc->setLocalStrictMode(savedStrictness));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9240,7 +9284,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
||||
if (!emitTree(ed->left()))
|
||||
return false;
|
||||
if (ed->right()) {
|
||||
if (!emitLexicalInitialization(ed->right()))
|
||||
if (!emitLexicalInitialization(&ed->right()->as<NameNode>()))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
|
||||
@@ -125,6 +125,7 @@ class CallOrNewEmitter;
|
||||
class ElemOpEmitter;
|
||||
class EmitterScope;
|
||||
class NestableControl;
|
||||
class PropertyEmitter;
|
||||
class PropOpEmitter;
|
||||
class TDZCheckCache;
|
||||
|
||||
@@ -522,7 +523,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||
|
||||
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);
|
||||
|
||||
FieldInitializers setupFieldInitializers(ListNode* classMembers);
|
||||
@@ -696,11 +697,11 @@ 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 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);
|
||||
@@ -784,10 +785,10 @@ 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);
|
||||
|
||||
@@ -798,7 +799,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||
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.
|
||||
//
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -370,7 +370,7 @@ EmitterScope::appendScopeNote(BytecodeEmitter* bce)
|
||||
}
|
||||
|
||||
bool
|
||||
EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart, uint32_t slotEnd)
|
||||
EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart, uint32_t slotEnd) const
|
||||
{
|
||||
// Lexical bindings throw ReferenceErrors if they are used before
|
||||
// initialization. See ES6 8.1.1.1.6.
|
||||
@@ -993,7 +993,7 @@ EmitterScope::enterWith(BytecodeEmitter* bce)
|
||||
}
|
||||
|
||||
bool
|
||||
EmitterScope::deadZoneFrameSlots(BytecodeEmitter* bce)
|
||||
EmitterScope::deadZoneFrameSlots(BytecodeEmitter* bce) const
|
||||
{
|
||||
return deadZoneFrameSlotRange(bce, frameSlotStart(), frameSlotEnd());
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class EmitterScope : public Nestable<EmitterScope>
|
||||
MOZ_MUST_USE bool appendScopeNote(BytecodeEmitter* bce);
|
||||
|
||||
MOZ_MUST_USE bool deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart,
|
||||
uint32_t slotEnd);
|
||||
uint32_t slotEnd) const;
|
||||
|
||||
public:
|
||||
explicit EmitterScope(BytecodeEmitter* bce);
|
||||
@@ -105,7 +105,7 @@ class EmitterScope : public Nestable<EmitterScope>
|
||||
MOZ_MUST_USE bool enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc);
|
||||
MOZ_MUST_USE bool enterModule(BytecodeEmitter* module, ModuleSharedContext* modulesc);
|
||||
MOZ_MUST_USE bool enterWith(BytecodeEmitter* bce);
|
||||
MOZ_MUST_USE bool deadZoneFrameSlots(BytecodeEmitter* bce);
|
||||
MOZ_MUST_USE bool deadZoneFrameSlots(BytecodeEmitter* bce) const;
|
||||
|
||||
MOZ_MUST_USE bool leave(BytecodeEmitter* bce, bool nonLocal = false);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,762 @@
|
||||
/* -*- 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 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::emitScopeForNamedClass(JS::Handle<LexicalScope::Data*> scopeBindings)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(classState_ == ClassState::Start);
|
||||
|
||||
tdzCacheForInnerName_.emplace(bce_);
|
||||
innerNameScope_.emplace(bce_);
|
||||
if (!innerNameScope_->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);
|
||||
|
||||
// [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::emitEnd(Kind kind)
|
||||
{
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||
propertyState_ == PropertyState::Init);
|
||||
MOZ_ASSERT(classState_ == ClassState::InitConstructor);
|
||||
|
||||
// [stack] CTOR HOMEOBJ
|
||||
|
||||
if (!bce_->emit1(JSOP_POP)) {
|
||||
// [stack] CTOR
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name_ != bce_->cx->names().empty) {
|
||||
MOZ_ASSERT(tdzCacheForInnerName_.isSome());
|
||||
MOZ_ASSERT(innerNameScope_.isSome());
|
||||
|
||||
if (!bce_->emitLexicalInitialization(name_)) {
|
||||
// [stack] CTOR
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pop the inner scope.
|
||||
if (!innerNameScope_->leave(bce_))
|
||||
return false;
|
||||
innerNameScope_.reset();
|
||||
|
||||
if (kind == Kind::Declaration) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
tdzCacheForInnerName_.reset();
|
||||
} else {
|
||||
// [stack] CTOR
|
||||
|
||||
MOZ_ASSERT(tdzCacheForInnerName_.isNothing());
|
||||
}
|
||||
|
||||
// [stack] # class declaration
|
||||
// [stack]
|
||||
// [stack] # class expression
|
||||
// [stack] CTOR
|
||||
|
||||
strictMode_.restore();
|
||||
|
||||
#ifdef DEBUG
|
||||
classState_ = ClassState::End;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,727 @@
|
||||
/* -*- 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/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.emitClass();
|
||||
//
|
||||
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
||||
// Some(offset_of_closing_bracket));
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class { constructor() { ... } }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitClass();
|
||||
//
|
||||
// emit(function_for_constructor);
|
||||
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||
//
|
||||
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||
//
|
||||
// `class X { constructor() { ... } }`
|
||||
// ClassEmitter ce(this);
|
||||
// ce.emitScopeForNamedClass(scopeBindingForName);
|
||||
// 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.emitScopeForNamedClass(scopeBindingForName);
|
||||
// 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.emitScopeForNamedClass(scopeBindingForName);
|
||||
//
|
||||
// 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.emitScopeForNamedClass(scopeBindingForName);
|
||||
//
|
||||
// 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);
|
||||
//
|
||||
// `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> tdzCacheForInnerName_;
|
||||
mozilla::Maybe<EmitterScope> innerNameScope_;
|
||||
AutoSaveLocalStrictMode strictMode_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+
|
||||
// | Start |-+------------------------------------>+-+
|
||||
// +-------+ | ^ |
|
||||
// | [named class] | |
|
||||
// | emitScopeForNamedClass +-------+ | |
|
||||
// +-------------------------->| Scope |-+ |
|
||||
// +-------+ |
|
||||
// |
|
||||
// +-----------------------------------------------+
|
||||
// |
|
||||
// | emitClass +-------+
|
||||
// +-+----------------->+->| Class |-+
|
||||
// | ^ +-------+ |
|
||||
// | emitDerivedClass | |
|
||||
// +------------------+ |
|
||||
// |
|
||||
// +-------------------------------+
|
||||
// |
|
||||
// |
|
||||
// | emitInitConstructor +-----------------+
|
||||
// +-+--------------------------->+->| InitConstructor |-+
|
||||
// | ^ +-----------------+ |
|
||||
// | emitInitDefaultConstructor | |
|
||||
// +----------------------------+ |
|
||||
// |
|
||||
// +---------------------------------------------------+
|
||||
// |
|
||||
// | (do PropertyEmitter operation) emitEnd +-----+
|
||||
// +-------------------------------+--------->| End |
|
||||
// +-----+
|
||||
enum class ClassState {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling emitScopeForNamedClass.
|
||||
Scope,
|
||||
|
||||
// After calling emitClass or emitDerivedClass.
|
||||
Class,
|
||||
|
||||
// After calling emitInitConstructor or emitInitDefaultConstructor.
|
||||
InitConstructor,
|
||||
|
||||
// After calling emitEnd.
|
||||
End,
|
||||
};
|
||||
ClassState classState_ = ClassState::Start;
|
||||
#endif
|
||||
|
||||
JS::Rooted<JSAtom*> name_;
|
||||
|
||||
public:
|
||||
explicit ClassEmitter(BytecodeEmitter* bce);
|
||||
|
||||
MOZ_MUST_USE bool emitScopeForNamedClass(
|
||||
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 emitEnd(Kind kind);
|
||||
|
||||
private:
|
||||
void setName(JS::Handle<JSAtom*> name);
|
||||
MOZ_MUST_USE bool initProtoAndCtor();
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_ObjectEmitter_h */
|
||||
@@ -186,14 +186,17 @@ 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/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',
|
||||
|
||||
Reference in New Issue
Block a user