import from UXP: Issue #2142 - Add PropertyEmitter, ObjectEmitter, ClassEmitter, LexicalScopeEmitter, DefaultEmitter (1b89be6d)

This commit is contained in:
2023-05-01 13:30:57 +08:00
parent 9ba8f749e8
commit 356f75e7a4
11 changed files with 2139 additions and 304 deletions
+335 -291
View File
@@ -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;
+11 -9
View File
@@ -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.
//
+73
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;
}
+65
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 */
+2 -2
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());
}
+2 -2
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);
+60
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;
}
+99
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 */
+762
View File
@@ -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;
}
+727
View File
@@ -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 */
+3
View File
@@ -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',