зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1793961 - Implement code generation for accessor decorators; r=arai
Differential Revision: https://phabricator.services.mozilla.com/D182084
This commit is contained in:
Родитель
89384c4be2
Коммит
20a0efd9ae
|
@ -9678,14 +9678,31 @@ static FunctionNode* GetInitializer(ParseNode* node, bool isStaticContext) {
|
|||
: node->as<StaticClassBlock>().function();
|
||||
}
|
||||
|
||||
static bool IsPrivateInstanceAccessor(const ClassMethod* classMethod) {
|
||||
return !classMethod->isStatic() &&
|
||||
classMethod->name().isKind(ParseNodeKind::PrivateName) &&
|
||||
classMethod->accessorType() != AccessorType::None;
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitCreateMemberInitializers(ClassEmitter& ce,
|
||||
ListNode* obj,
|
||||
FieldPlacement placement) {
|
||||
// FieldPlacement::Instance
|
||||
// [stack] HOMEOBJ HERITAGE?
|
||||
FieldPlacement placement
|
||||
#ifdef ENABLE_DECORATORS
|
||||
,
|
||||
bool hasHeritage
|
||||
#endif
|
||||
) {
|
||||
// FieldPlacement::Instance, hasHeritage == false
|
||||
// [stack] HOMEOBJ
|
||||
//
|
||||
// FieldPlacement::Instance, hasHeritage == true
|
||||
// [stack] HOMEOBJ HERITAGE
|
||||
//
|
||||
// FieldPlacement::Static
|
||||
// [stack] CTOR HOMEOBJ
|
||||
#ifdef ENABLE_DECORATORS
|
||||
MOZ_ASSERT_IF(placement == FieldPlacement::Static, !hasHeritage);
|
||||
#endif
|
||||
mozilla::Maybe<MemberInitializers> memberInitializers =
|
||||
setupMemberInitializers(obj, placement);
|
||||
if (!memberInitializers) {
|
||||
|
@ -9769,18 +9786,252 @@ bool BytecodeEmitter::emitCreateMemberInitializers(ClassEmitter& ce,
|
|||
}
|
||||
if (field->decorators() && !field->decorators()->empty()) {
|
||||
DecoratorEmitter de(this);
|
||||
if (!de.emitApplyDecoratorsToFieldDefinition(
|
||||
&field->name(), field->decorators(), field->isStatic())) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITIALIZERS
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITIALIZERS
|
||||
return false;
|
||||
}
|
||||
if (!field->hasAccessor()) {
|
||||
if (!de.emitApplyDecoratorsToFieldDefinition(
|
||||
&field->name(), field->decorators(), field->isStatic())) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITS
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITS
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ClassMethod* accessorGetterNode = field->accessorGetterNode();
|
||||
auto accessorGetterKeyAtom =
|
||||
accessorGetterNode->left()->as<NameNode>().atom();
|
||||
ClassMethod* accessorSetterNode = field->accessorSetterNode();
|
||||
auto accessorSetterKeyAtom =
|
||||
accessorSetterNode->left()->as<NameNode>().atom();
|
||||
if (!IsPrivateInstanceAccessor(accessorGetterNode)) {
|
||||
if (!emitTree(&accessorGetterNode->method())) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX GETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX GETTER
|
||||
return false;
|
||||
}
|
||||
if (!emitTree(&accessorSetterNode->method())) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX GETTER SETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX GETTER SETTER
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(IsPrivateInstanceAccessor(accessorSetterNode));
|
||||
auto getAccessor = [this](
|
||||
ClassMethod* classMethod,
|
||||
TaggedParserAtomIndex& updatedAtom) -> bool {
|
||||
// [stack]
|
||||
|
||||
// Synthesize a name for the lexical variable that will store the
|
||||
// private method body.
|
||||
TaggedParserAtomIndex name =
|
||||
classMethod->name().as<NameNode>().atom();
|
||||
AccessorType accessorType = classMethod->accessorType();
|
||||
StringBuffer storedMethodName(fc);
|
||||
if (!storedMethodName.append(parserAtoms(), name)) {
|
||||
return false;
|
||||
}
|
||||
if (!storedMethodName.append(accessorType == AccessorType::Getter
|
||||
? ".getter"
|
||||
: ".setter")) {
|
||||
return false;
|
||||
}
|
||||
updatedAtom = storedMethodName.finishParserAtom(parserAtoms(), fc);
|
||||
if (!updatedAtom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return emitGetName(updatedAtom);
|
||||
// [stack] ACCESSOR
|
||||
};
|
||||
|
||||
if (!getAccessor(accessorGetterNode, accessorGetterKeyAtom)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX GETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX GETTER
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!getAccessor(accessorSetterNode, accessorSetterKeyAtom)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX GETTER SETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX GETTER SETTER
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
if (!de.emitApplyDecoratorsToAccessorDefinition(
|
||||
&field->name(), field->decorators(), field->isStatic())) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX GETTER SETTER INITS
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX GETTER SETTER INITS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitUnpickN(2)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITS GETTER SETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITS GETTER SETTER
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsPrivateInstanceAccessor(accessorGetterNode)) {
|
||||
if (!isStatic) {
|
||||
if (!emitPickN(hasHeritage ? 6 : 5)) {
|
||||
// [stack] HERITAGE? ARRAY INDEX INITS GETTER SETTER HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitPickN(6)) {
|
||||
// [stack] HOMEOBJ ARRAY INDEX INITS GETTER SETTER CTOR
|
||||
return false;
|
||||
}
|
||||
if (!emitPickN(6)) {
|
||||
// [stack] ARRAY INDEX INITS GETTER SETTER CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyEmitter::Kind kind = field->isStatic()
|
||||
? PropertyEmitter::Kind::Static
|
||||
: PropertyEmitter::Kind::Prototype;
|
||||
if (!accessorGetterNode->name().isKind(ParseNodeKind::PrivateName)) {
|
||||
MOZ_ASSERT(
|
||||
!accessorSetterNode->name().isKind(ParseNodeKind::PrivateName));
|
||||
|
||||
if (!ce.prepareForPropValue(propdef->pn_pos.begin, kind)) {
|
||||
// [stack] HERITAGE? ARRAY INDEX INITS GETTER SETTER HOMEOBJ
|
||||
// or:
|
||||
// [stack] ARRAY INDEX INITS GETTER SETTER CTOR HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
if (!emitPickN(isStatic ? 3 : 1)) {
|
||||
// [stack] HERITAGE? ARRAY INDEX INITS GETTER HOMEOBJ SETTER
|
||||
// or:
|
||||
// [stack] ARRAY INDEX INITS GETTER CTOR HOMEOBJ CTOR SETTER
|
||||
return false;
|
||||
}
|
||||
if (!ce.emitInit(AccessorType::Setter, accessorSetterKeyAtom)) {
|
||||
// [stack] HERITAGE? ARRAY INDEX INITS GETTER HOMEOBJ
|
||||
// or:
|
||||
// [stack] ARRAY INDEX INITS GETTER CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ce.prepareForPropValue(propdef->pn_pos.begin, kind)) {
|
||||
// [stack] HERITAGE? ARRAY INDEX INITS GETTER HOMEOBJ
|
||||
// or:
|
||||
// [stack] ARRAY INDEX INITS GETTER CTOR HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
if (!emitPickN(isStatic ? 3 : 1)) {
|
||||
// [stack] HERITAGE? ARRAY INDEX INITS HOMEOBJ GETTER
|
||||
// or:
|
||||
// [stack] ARRAY INDEX INITS CTOR HOMEOBJ CTOR GETTER
|
||||
return false;
|
||||
}
|
||||
if (!ce.emitInit(AccessorType::Getter, accessorGetterKeyAtom)) {
|
||||
// [stack] HERITAGE? ARRAY INDEX INITS HOMEOBJ
|
||||
// or:
|
||||
// [stack] ARRAY INDEX INITS CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(isStatic);
|
||||
// The getter and setter share the same name.
|
||||
if (!emitNewPrivateName(accessorSetterKeyAtom,
|
||||
accessorSetterKeyAtom)) {
|
||||
return false;
|
||||
}
|
||||
if (!ce.prepareForPrivateStaticMethod(propdef->pn_pos.begin)) {
|
||||
// [stack] ARRAY INDEX INITS GETTER SETTER CTOR HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
if (!emitGetPrivateName(
|
||||
&accessorSetterNode->name().as<NameNode>())) {
|
||||
// [stack] ARRAY INDEX INITS GETTER SETTER CTOR HOMEOBJ CTOR
|
||||
// KEY
|
||||
return false;
|
||||
}
|
||||
if (!emitPickN(4)) {
|
||||
// [stack] ARRAY INDEX INITS GETTER CTOR HOMEOBJ CTOR KEY
|
||||
// SETTER
|
||||
return false;
|
||||
}
|
||||
if (!ce.emitPrivateStaticMethod(AccessorType::Setter)) {
|
||||
// [stack] ARRAY INDEX INITS GETTER CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ce.prepareForPrivateStaticMethod(propdef->pn_pos.begin)) {
|
||||
// [stack] ARRAY INDEX INITS GETTER CTOR HOMEOBJ CTOR
|
||||
return false;
|
||||
}
|
||||
if (!emitGetPrivateName(
|
||||
&accessorGetterNode->name().as<NameNode>())) {
|
||||
// [stack] ARRAY INDEX INITS GETTER CTOR HOMEOBJ CTOR KEY
|
||||
return false;
|
||||
}
|
||||
if (!emitPickN(4)) {
|
||||
// [stack] ARRAY INDEX INITS CTOR HOMEOBJ CTOR KEY GETTER
|
||||
return false;
|
||||
}
|
||||
if (!ce.emitPrivateStaticMethod(AccessorType::Getter)) {
|
||||
// [stack] ARRAY INDEX INITS CTOR HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isStatic) {
|
||||
if (!emitUnpickN(hasHeritage ? 4 : 3)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITS
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitUnpickN(4)) {
|
||||
// [stack] HOMEOBJ ARRAY INDEX INITS CTOR
|
||||
return false;
|
||||
}
|
||||
if (!emitUnpickN(4)) {
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITS
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(IsPrivateInstanceAccessor(accessorSetterNode));
|
||||
|
||||
if (!emitLexicalInitialization(accessorSetterKeyAtom)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITS GETTER SETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITS GETTER SETTER
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitPopN(1)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITS GETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITS GETTER
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitLexicalInitialization(accessorGetterKeyAtom)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITS GETTER
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITS GETTER
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitPopN(1)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX INITS
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX INITS
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!emit1(JSOp::InitElemInc)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY INDEX
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX
|
||||
// [stack] CTOR HOMEOBJ ARRAY INDEX
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -9788,9 +10039,9 @@ bool BytecodeEmitter::emitCreateMemberInitializers(ClassEmitter& ce,
|
|||
|
||||
// Pop INDEX
|
||||
if (!emitPopN(1)) {
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY
|
||||
// [stack] HOMEOBJ HERITAGE? ARRAY
|
||||
// or:
|
||||
// [stack] CTOR HOMEOBJ ARRAY
|
||||
// [stack] CTOR HOMEOBJ ARRAY
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
@ -9805,12 +10056,6 @@ bool BytecodeEmitter::emitCreateMemberInitializers(ClassEmitter& ce,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool IsPrivateInstanceAccessor(const ClassMethod* classMethod) {
|
||||
return !classMethod->isStatic() &&
|
||||
classMethod->name().isKind(ParseNodeKind::PrivateName) &&
|
||||
classMethod->accessorType() != AccessorType::None;
|
||||
}
|
||||
|
||||
bool BytecodeEmitter::emitPrivateMethodInitializers(ClassEmitter& ce,
|
||||
ListNode* obj) {
|
||||
for (ParseNode* propdef : obj->contents()) {
|
||||
|
@ -11345,7 +11590,12 @@ bool BytecodeEmitter::emitClass(
|
|||
|
||||
// Any class with field initializers will have a constructor
|
||||
if (!emitCreateMemberInitializers(ce, classMembers,
|
||||
FieldPlacement::Instance)) {
|
||||
FieldPlacement::Instance
|
||||
#ifdef ENABLE_DECORATORS
|
||||
,
|
||||
isDerived
|
||||
#endif
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -11383,7 +11633,12 @@ bool BytecodeEmitter::emitClass(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!emitCreateMemberInitializers(ce, classMembers, FieldPlacement::Static)) {
|
||||
if (!emitCreateMemberInitializers(ce, classMembers, FieldPlacement::Static
|
||||
#ifdef ENABLE_DECORATORS
|
||||
,
|
||||
false
|
||||
#endif
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -657,7 +657,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
|||
FieldPlacement placement);
|
||||
[[nodiscard]] bool emitCreateMemberInitializers(ClassEmitter& ce,
|
||||
ListNode* obj,
|
||||
FieldPlacement placement);
|
||||
FieldPlacement placement
|
||||
#ifdef ENABLE_DECORATORS
|
||||
,
|
||||
bool hasHeritage
|
||||
#endif
|
||||
);
|
||||
const MemberInitializers& findMemberInitializersForCall();
|
||||
[[nodiscard]] bool emitInitializeInstanceMembers(
|
||||
bool isDerivedClassConstructor);
|
||||
|
|
|
@ -22,7 +22,7 @@ DecoratorEmitter::DecoratorEmitter(BytecodeEmitter* bce) : bce_(bce) {}
|
|||
bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
|
||||
DecoratorEmitter::Kind kind, ParseNode* key, ListNode* decorators,
|
||||
bool isStatic) {
|
||||
MOZ_ASSERT(kind != Kind::Field);
|
||||
MOZ_ASSERT(kind != Kind::Field && kind != Kind::Accessor);
|
||||
|
||||
// The DecoratorEmitter expects the value to be decorated to be at the top
|
||||
// of the stack prior to this call. It will apply the decorators to this
|
||||
|
@ -31,16 +31,16 @@ bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
|
|||
|
||||
// Decorators Proposal
|
||||
// https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
|
||||
// 1. Let decorators be elementRecord.[[Decorators]].
|
||||
// 2. If decorators is empty, return unused.
|
||||
// Step 1. Let decorators be elementRecord.[[Decorators]].
|
||||
// Step 2. If decorators is empty, return unused.
|
||||
// This is checked by the caller.
|
||||
MOZ_ASSERT(!decorators->empty());
|
||||
|
||||
// 3. Let key be elementRecord.[[Key]].
|
||||
// 4. Let kind be elementRecord.[[Kind]].
|
||||
// 5. For each element decorator of decorators, do
|
||||
// Step 3. Let key be elementRecord.[[Key]].
|
||||
// Step 4. Let kind be elementRecord.[[Kind]].
|
||||
// Step 5. For each element decorator of decorators, do
|
||||
for (ParseNode* decorator : decorators->contents()) {
|
||||
// 5.a. Let decorationState be the Record { [[Finished]]: false }.
|
||||
// Step 5.a. Let decorationState be the Record { [[Finished]]: false }.
|
||||
if (!emitDecorationState()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
|
|||
return false;
|
||||
}
|
||||
|
||||
// 5.i. Set decorationState.[[Finished]] to true.
|
||||
// Step 5.i. Set decorationState.[[Finished]] to true.
|
||||
if (!emitUpdateDecorationState()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
|
|||
return false;
|
||||
}
|
||||
|
||||
// 5.l.i. If IsCallable(newValue) is true, then
|
||||
// Step 5.l.i. If IsCallable(newValue) is true, then
|
||||
if (!emitCheckIsCallable()) {
|
||||
// [stack] VAL RETVAL ISCALLABLE_RESULT
|
||||
return false;
|
||||
|
@ -92,25 +92,8 @@ bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
|
|||
// [stack] VAL RETVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
// 5.k. Else if kind is accessor, then
|
||||
// 5.k.i. If newValue is an Object, then
|
||||
// 5.k.i.1. Let newGetter be ? Get(newValue, "get").
|
||||
// 5.k.i.2. If IsCallable(newGetter) is true, set elementRecord.[[Get]] to newGetter.
|
||||
// 5.k.i.3. Else if newGetter is not undefined, throw a TypeError exception.
|
||||
// 5.k.i.4. Let newSetter be ? Get(newValue, "set").
|
||||
// 5.k.i.5. If IsCallable(newSetter) is true, set elementRecord.[[Set]] to newSetter.
|
||||
// 5.k.i.6. Else if newSetter is not undefined, throw a TypeError exception.
|
||||
// 5.k.i.7. Let initializer be ? Get(newValue, "init").
|
||||
// 5.k.i.8. If IsCallable(initializer) is true, append initializer to elementRecord.[[Initializers]].
|
||||
// 5.k.i.9. Else if initializer is not undefined, throw a TypeError exception.
|
||||
// 5.k.ii. Else if newValue is not undefined, throw a TypeError exception.
|
||||
// TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1793961.
|
||||
// clang-format on
|
||||
// 5.l. Else,
|
||||
|
||||
// 5.l.i.1. Perform MakeMethod(newValue, homeObject).
|
||||
// Step 5.l. Else,
|
||||
// Step 5.l.i.1. Perform MakeMethod(newValue, homeObject).
|
||||
// MakeMethod occurs in the caller, here we just drop the original method
|
||||
// which was an argument to the decorator, and leave the new method
|
||||
// returned by the decorator on the stack.
|
||||
|
@ -122,9 +105,10 @@ bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
|
|||
// [stack] RETVAL
|
||||
return false;
|
||||
}
|
||||
// 5.j.ii. Else if initializer is not undefined, throw a TypeError
|
||||
// exception. 5.l.ii. Else if newValue is not undefined, throw a
|
||||
// TypeError exception.
|
||||
// Step 5.j.ii. Else if initializer is not undefined, throw a TypeError
|
||||
// exception.
|
||||
// Step 5.l.ii. Else if newValue is not undefined, throw a
|
||||
// TypeError exception.
|
||||
if (!ie.emitElse()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -157,8 +141,8 @@ bool DecoratorEmitter::emitApplyDecoratorsToFieldDefinition(
|
|||
|
||||
// Decorators Proposal
|
||||
// https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
|
||||
// 1. Let decorators be elementRecord.[[Decorators]].
|
||||
// 2. If decorators is empty, return unused.
|
||||
// Step 1. Let decorators be elementRecord.[[Decorators]].
|
||||
// Step 2. If decorators is empty, return unused.
|
||||
// This is checked by the caller.
|
||||
MOZ_ASSERT(!decorators->empty());
|
||||
|
||||
|
@ -184,11 +168,11 @@ bool DecoratorEmitter::emitApplyDecoratorsToFieldDefinition(
|
|||
return false;
|
||||
}
|
||||
|
||||
// 3. Let key be elementRecord.[[Key]].
|
||||
// 4. Let kind be elementRecord.[[Kind]].
|
||||
// 5. For each element decorator of decorators, do
|
||||
// Step 3. Let key be elementRecord.[[Key]].
|
||||
// Step 4. Let kind be elementRecord.[[Kind]].
|
||||
// Step 5. For each element decorator of decorators, do
|
||||
for (ParseNode* decorator : decorators->contents()) {
|
||||
// 5.a. Let decorationState be the Record { [[Finished]]: false }.
|
||||
// Step 5.a. Let decorationState be the Record { [[Finished]]: false }.
|
||||
if (!emitDecorationState()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -198,7 +182,7 @@ bool DecoratorEmitter::emitApplyDecoratorsToFieldDefinition(
|
|||
return false;
|
||||
}
|
||||
|
||||
// 5.i. Set decorationState.[[Finished]] to true.
|
||||
// Step 5.i. Set decorationState.[[Finished]] to true.
|
||||
if (!emitUpdateDecorationState()) {
|
||||
// [stack] ARRAY INDEX RETVAL
|
||||
return false;
|
||||
|
@ -232,7 +216,7 @@ bool DecoratorEmitter::emitApplyDecoratorsToFieldDefinition(
|
|||
return false;
|
||||
}
|
||||
|
||||
// 5.l.i. If IsCallable(newValue) is true, then
|
||||
// Step 5.l.i. If IsCallable(newValue) is true, then
|
||||
if (!emitCheckIsCallable()) {
|
||||
// [stack] ARRAY INDEX RETVAL ISCALLABLE_RESULT
|
||||
return false;
|
||||
|
@ -243,17 +227,18 @@ bool DecoratorEmitter::emitApplyDecoratorsToFieldDefinition(
|
|||
return false;
|
||||
}
|
||||
|
||||
// 5.j. If kind is field, then
|
||||
// 5.j.i. If IsCallable(initializer) is true, append initializer
|
||||
// to elementRecord.[[Initializers]].
|
||||
// Step 5.j. If kind is field, then
|
||||
// Step 5.j.i. If IsCallable(initializer) is true, append initializer to
|
||||
// elementRecord.[[Initializers]].
|
||||
if (!bce_->emit1(JSOp::InitElemInc)) {
|
||||
// [stack] ARRAY INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5.j.ii. Else if initializer is not undefined, throw a TypeError
|
||||
// exception. 5.l.ii. Else if newValue is not undefined, throw a
|
||||
// TypeError exception.
|
||||
// Step 5.j.ii. Else if initializer is not undefined, throw a TypeError
|
||||
// exception.
|
||||
// Step 5.l.ii. Else if newValue is not undefined, throw a
|
||||
// TypeError exception.
|
||||
if (!ie.emitElse()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -278,16 +263,204 @@ bool DecoratorEmitter::emitApplyDecoratorsToFieldDefinition(
|
|||
// [stack] ARRAY
|
||||
}
|
||||
|
||||
bool DecoratorEmitter::emitApplyDecoratorsToAccessorDefinition(
|
||||
ParseNode* key, ListNode* decorators, bool isStatic) {
|
||||
// This method creates a new array to contain initializers added by decorators
|
||||
// to the stack. start:
|
||||
// [stack] GETTER SETTER
|
||||
// end:
|
||||
// [stack] GETTER SETTER ARRAY
|
||||
MOZ_ASSERT(key->is<NameNode>());
|
||||
|
||||
// Decorators Proposal
|
||||
// https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
|
||||
// Step 1. Let decorators be elementRecord.[[Decorators]].
|
||||
// Step 2. If decorators is empty, return unused.
|
||||
// This is checked by the caller.
|
||||
MOZ_ASSERT(!decorators->empty());
|
||||
|
||||
// If we're applying decorators to a field, we'll push a new array to the
|
||||
// stack to hold newly created initializers.
|
||||
if (!bce_->emitUint32Operand(JSOp::NewArray, 1)) {
|
||||
// [stack] GETTER SETTER ARRAY
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emitGetPrivateName(&key->as<NameNode>())) {
|
||||
// [stack] GETTER SETTER ARRAY NAME
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emitUint32Operand(JSOp::InitElemArray, 0)) {
|
||||
// [stack] GETTER SETTER ARRAY
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emit1(JSOp::One)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3. Let key be elementRecord.[[Key]].
|
||||
// Step 4. Let kind be elementRecord.[[Kind]].
|
||||
// Step 5. For each element decorator of decorators, do
|
||||
for (ParseNode* decorator : decorators->contents()) {
|
||||
// 5.a. Let decorationState be the Record { [[Finished]]: false }.
|
||||
if (!emitDecorationState()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.g.i. Set value to OrdinaryObjectCreate(%Object.prototype%).
|
||||
ObjectEmitter oe(bce_);
|
||||
if (!oe.emitObject(2)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.g.ii. Perform ! CreateDataPropertyOrThrow(value, "get",
|
||||
// elementRecord.[[Get]]).
|
||||
if (!oe.prepareForPropValue(decorator->pn_pos.begin,
|
||||
PropertyEmitter::Kind::Prototype)) {
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emitDupAt(4)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX VALUE GETTER
|
||||
return false;
|
||||
}
|
||||
if (!oe.emitInit(frontend::AccessorType::None,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::get())) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.g.iii. Perform ! CreateDataPropertyOrThrow(value, "set",
|
||||
// elementRecord.[[Set]]).
|
||||
if (!oe.prepareForPropValue(decorator->pn_pos.begin,
|
||||
PropertyEmitter::Kind::Prototype)) {
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emitDupAt(3)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX VALUE SETTER
|
||||
return false;
|
||||
}
|
||||
if (!oe.emitInit(frontend::AccessorType::None,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::set())) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!oe.emitEnd()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.j. Let newValue be ? Call(decorator, decoratorReceiver,
|
||||
// « value, context »).
|
||||
if (!emitCallDecorator(Kind::Accessor, key, isStatic, decorator)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.k. Set decorationState.[[Finished]] to true.
|
||||
if (!emitUpdateDecorationState()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to check if the decorator returned undefined, a callable value,
|
||||
// or any other value.
|
||||
IfEmitter ie(bce_);
|
||||
if (!ie.emitIf(mozilla::Nothing())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitCheckIsUndefined()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL ISUNDEFINED
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ie.emitThenElse()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pop the undefined RETVAL from the stack, leaving the original values in
|
||||
// place.
|
||||
if (!bce_->emitPopN(1)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ie.emitElse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.k. Else if kind is accessor, then
|
||||
// Step 5.k.ii. Else if newValue is not undefined, throw a TypeError
|
||||
// exception. (Reordered)
|
||||
if (!bce_->emit2(JSOp::CheckIsObj,
|
||||
uint8_t(CheckIsObjectKind::DecoratorReturn))) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.k.i. If newValue is an Object, then
|
||||
// Step 5.k.i.1. Let newGetter be ? Get(newValue, "get").
|
||||
// Step 5.k.i.2. If IsCallable(newGetter) is true, set
|
||||
// elementRecord.[[Get]] to newGetter.
|
||||
// Step 5.k.i.3. Else if newGetter is not undefined, throw a
|
||||
// TypeError exception.
|
||||
if (!emitHandleNewValueField(
|
||||
frontend::TaggedParserAtomIndex::WellKnown::get(), 5)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.k.i.4. Let newSetter be ? Get(newValue, "set").
|
||||
// Step 5.k.i.5. If IsCallable(newSetter) is true, set
|
||||
// elementRecord.[[Set]] to newSetter.
|
||||
// Step 5.k.i.6. Else if newSetter is not undefined, throw a
|
||||
// TypeError exception.
|
||||
if (!emitHandleNewValueField(
|
||||
frontend::TaggedParserAtomIndex::WellKnown::set(), 4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.k.i.7. Let initializer be ? Get(newValue, "init").
|
||||
// Step 5.k.i.8. If IsCallable(initializer) is true, append
|
||||
// initializer to elementRecord.[[Initializers]].
|
||||
// Step 5.k.i.9. Else if initializer is not undefined, throw a
|
||||
// TypeError exception.
|
||||
if (!emitHandleNewValueField(
|
||||
frontend::TaggedParserAtomIndex::WellKnown::init(), 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pop RETVAL from stack
|
||||
if (!bce_->emitPopN(1)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ie.emitEnd()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Pop INDEX
|
||||
return bce_->emitPopN(1);
|
||||
// [stack] GETTER SETTER ARRAY
|
||||
}
|
||||
|
||||
bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
||||
// [stack] THIS INITIALIZERS
|
||||
|
||||
// Decorators Proposal
|
||||
// https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
|
||||
//
|
||||
// 1. Assert: elementRecord.[[Kind]] is field or accessor.
|
||||
// 2. If elementRecord.[[BackingStorageKey]] is present, let fieldName be
|
||||
// elementRecord.[[BackingStorageKey]].
|
||||
// 3. Else, let fieldName be elementRecord.[[Key]].
|
||||
// Step 1. Assert: elementRecord.[[Kind]] is field or accessor.
|
||||
// Step 2. If elementRecord.[[BackingStorageKey]] is present, let fieldName be
|
||||
// elementRecord.[[BackingStorageKey]].
|
||||
// Step 3. Else, let fieldName be elementRecord.[[Key]].
|
||||
// We've stored the fieldname in the first element of the initializers array.
|
||||
if (!bce_->emit1(JSOp::Dup)) {
|
||||
// [stack] THIS INITIALIZERS INITIALIZERS
|
||||
|
@ -320,7 +493,7 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 4. Let initValue be undefined.
|
||||
// Step 4. Let initValue be undefined.
|
||||
// TODO: (See Bug 1817993) At the moment, we're applying the initialization
|
||||
// logic in two steps. The pre-decorator initialization code runs, stores
|
||||
// the initial value, and then we retrieve it here and apply the initializers
|
||||
|
@ -331,7 +504,7 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
}
|
||||
|
||||
if (!bce_->emit2(JSOp::Pick, 2)) {
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -352,7 +525,7 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 5. For each element initializer of elementRecord.[[Initializers]], do
|
||||
// Step 5. For each element initializer of elementRecord.[[Initializers]], do
|
||||
WhileEmitter wh(bce_);
|
||||
// At this point, we have no context to determine offsets in the
|
||||
// code for this while statement. Ideally, it would correspond to
|
||||
|
@ -369,7 +542,7 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
|
||||
if (!bce_->emitDupAt(2)) {
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX INDEX
|
||||
// LENGTH
|
||||
// LENGTH
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -378,7 +551,7 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// a. Set initValue to ? Call(initializer, receiver, « initValue »).
|
||||
// Step 5.a. Set initValue to ? Call(initializer, receiver, « initValue»).
|
||||
if (!wh.emitBody()) {
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
|
||||
return false;
|
||||
|
@ -386,13 +559,13 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
|
||||
if (!bce_->emitDupAt(2)) {
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
|
||||
// INITIALIZERS
|
||||
// INITIALIZERS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emitDupAt(1)) {
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
|
||||
// INITIALIZERS INDEX
|
||||
// INITIALIZERS INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -402,27 +575,25 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
}
|
||||
|
||||
if (!bce_->emitDupAt(6)) {
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX FUNC
|
||||
// THIS
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX FUNC THIS
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass value in as argument to the initializer
|
||||
if (!bce_->emit2(JSOp::Pick, 5)) {
|
||||
// [stack] THIS FIELDNAME INITIALIZERS LENGTH INDEX FUNC THIS
|
||||
// VALUE
|
||||
// [stack] THIS FIELDNAME INITIALIZERS LENGTH INDEX FUNC THIS VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callee is always internal function.
|
||||
if (!bce_->emitCall(JSOp::Call, 1)) {
|
||||
// [stack] THIS FIELDNAME INITIALIZERS LENGTH INDEX RVAL
|
||||
// [stack] THIS FIELDNAME INITIALIZERS LENGTH INDEX RVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store returned value for next iteration
|
||||
if (!bce_->emit2(JSOp::Unpick, 3)) {
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
|
||||
// [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -436,12 +607,12 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 6. If fieldName is a Private Name, then
|
||||
// 6.a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).
|
||||
// 7. Else,
|
||||
// 6.a. Assert: IsPropertyKey(fieldName) is true.
|
||||
// 6.b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName,
|
||||
// initValue).
|
||||
// Step 6. If fieldName is a Private Name, then
|
||||
// Step 6.a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).
|
||||
// Step 7. Else,
|
||||
// Step 7.a. Assert: IsPropertyKey(fieldName) is true.
|
||||
// Step 7.b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName,
|
||||
// initValue).
|
||||
// TODO: (See Bug 1817993) Because the field already exists, we just store the
|
||||
// updated value here.
|
||||
if (!bce_->emitPopN(3)) {
|
||||
|
@ -454,7 +625,7 @@ bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 8. Return unused.
|
||||
// Step 8. Return unused.
|
||||
return bce_->emitPopN(1);
|
||||
// [stack]
|
||||
}
|
||||
|
@ -507,6 +678,11 @@ bool DecoratorEmitter::emitUpdateDecorationState() {
|
|||
ParseNode* key,
|
||||
bool isStatic,
|
||||
ParseNode* decorator) {
|
||||
// Except for fields, this method expects the value to be passed
|
||||
// to the decorator to be on top of the stack. For methods, getters and
|
||||
// setters this is the method itself. For accessors it is an object
|
||||
// containing the getter and setter associated with the accessor.
|
||||
// [stack] VAL?
|
||||
// Prepare to call decorator
|
||||
CallOrNewEmitter cone(bce_, JSOp::Call,
|
||||
CallOrNewEmitter::ArgumentsKind::Other,
|
||||
|
@ -580,41 +756,42 @@ bool DecoratorEmitter::emitUpdateDecorationState() {
|
|||
}
|
||||
|
||||
if (kind == Kind::Field) {
|
||||
// 5.c. Let value be undefined.
|
||||
// Step 5.c. Let value be undefined.
|
||||
if (!bce_->emit1(JSOp::Undefined)) {
|
||||
// [stack] VAL? CALLEE THIS undefined
|
||||
// [stack] CALLEE THIS undefined
|
||||
return false;
|
||||
}
|
||||
} else if (kind == Kind::Getter || kind == Kind::Method ||
|
||||
kind == Kind::Setter) {
|
||||
// 5.d. If kind is method, set value to elementRecord.[[Value]].
|
||||
// 5.e. Else if kind is getter, set value to elementRecord.[[Get]].
|
||||
// 5.f. Else if kind is setter, set value to elementRecord.[[Set]].
|
||||
// Step 5.d. If kind is method, set value to elementRecord.[[Value]].
|
||||
// Step 5.e. Else if kind is getter, set value to elementRecord.[[Get]].
|
||||
// Step 5.f. Else if kind is setter, set value to elementRecord.[[Set]].
|
||||
// The DecoratorEmitter expects the method to already be on the stack.
|
||||
// We dup the value here so we can use it as an argument to the decorator.
|
||||
if (!bce_->emitDupAt(2)) {
|
||||
// [stack] VAL CALLEE THIS VAL
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Step 5.g. Else if kind is accessor, then
|
||||
// Step 5.g.i. Set value to OrdinaryObjectCreate(%Object.prototype%).
|
||||
// For accessor decorators, we've already created the value object prior
|
||||
// to calling this method.
|
||||
MOZ_ASSERT(kind == Kind::Accessor);
|
||||
if (!bce_->emitPickN(2)) {
|
||||
// [stack] CALLEE THIS VAL
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 5.g. Else if kind is accessor, then
|
||||
// 5.g.i. Set value to OrdinaryObjectCreate(%Object.prototype%).
|
||||
// 5.g.ii. Perform ! CreateDataPropertyOrThrow(accessor, "get",
|
||||
// elementRecord.[[Get]]). 5.g.iii. Perform
|
||||
// ! CreateDataPropertyOrThrow(accessor, "set",
|
||||
// elementRecord.[[Set]]).
|
||||
// TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1793961.
|
||||
// 5.b. Let context be CreateDecoratorContextObject(kind, key,
|
||||
// extraInitializers, decorationState, isStatic).
|
||||
// Step 5.b. Let context be CreateDecoratorContextObject(kind, key,
|
||||
// extraInitializers, decorationState, isStatic).
|
||||
if (!emitCreateDecoratorContextObject(kind, key, isStatic,
|
||||
decorator->pn_pos)) {
|
||||
// [stack] VAL? CALLEE THIS VAL
|
||||
// context
|
||||
// [stack] VAL? CALLEE THIS VAL context
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5.h. Let newValue be ? Call(decorator, undefined, « value, context
|
||||
// »).
|
||||
// Step 5.h. Let newValue be ? Call(decorator, undefined, « value, context»).
|
||||
return cone.emitEnd(2, decorator->pn_pos.begin);
|
||||
// [stack] VAL? RETVAL
|
||||
}
|
||||
|
@ -678,7 +855,7 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
ParseNode* key,
|
||||
bool isStatic,
|
||||
TokenPos pos) {
|
||||
// 1. Let contextObj be OrdinaryObjectCreate(%Object.prototype%).
|
||||
// Step 1. Let contextObj be OrdinaryObjectCreate(%Object.prototype%).
|
||||
ObjectEmitter oe(bce_);
|
||||
if (!oe.emitObject(/* propertyCount */ 6)) {
|
||||
// [stack] context
|
||||
|
@ -689,7 +866,7 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
}
|
||||
|
||||
if (kind == Kind::Method) {
|
||||
// 2. If kind is method, let kindStr be "method".
|
||||
// Step 2. If kind is method, let kindStr be "method".
|
||||
if (!bce_->emitStringOp(
|
||||
JSOp::String,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::method())) {
|
||||
|
@ -697,7 +874,7 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
return false;
|
||||
}
|
||||
} else if (kind == Kind::Getter) {
|
||||
// 3. Else if kind is getter, let kindStr be "getter".
|
||||
// Step 3. Else if kind is getter, let kindStr be "getter".
|
||||
if (!bce_->emitStringOp(
|
||||
JSOp::String,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::getter())) {
|
||||
|
@ -705,15 +882,23 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
return false;
|
||||
}
|
||||
} else if (kind == Kind::Setter) {
|
||||
// 4. Else if kind is setter, let kindStr be "setter".
|
||||
// Step 4. Else if kind is setter, let kindStr be "setter".
|
||||
if (!bce_->emitStringOp(
|
||||
JSOp::String,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::setter())) {
|
||||
// [stack] context "setter"
|
||||
return false;
|
||||
}
|
||||
} else if (kind == Kind::Accessor) {
|
||||
// Step 5. Else if kind is accessor, let kindStr be "accessor".
|
||||
if (!bce_->emitStringOp(
|
||||
JSOp::String,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::accessor())) {
|
||||
// [stack] context "accessor"
|
||||
return false;
|
||||
}
|
||||
} else if (kind == Kind::Field) {
|
||||
// 6. Else if kind is field, let kindStr be "field".
|
||||
// Step 6. Else if kind is field, let kindStr be "field".
|
||||
if (!bce_->emitStringOp(
|
||||
JSOp::String,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::field())) {
|
||||
|
@ -722,29 +907,24 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
}
|
||||
} else {
|
||||
// clang-format off
|
||||
|
||||
|
||||
// TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1793960.
|
||||
// 5. Else if kind is accessor, let kindStr be "accessor".
|
||||
// TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1793961.
|
||||
// 7. Else,
|
||||
// a. Assert: kind is class.
|
||||
// b. Let kindStr be "class".
|
||||
// Step 7. Else,
|
||||
// Step 7.a. Assert: kind is class.
|
||||
// Step 7.b. Let kindStr be "class".
|
||||
// TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1793963.
|
||||
// clang-format on
|
||||
return false;
|
||||
}
|
||||
|
||||
// 8. Perform ! CreateDataPropertyOrThrow(contextObj, "kind", kindStr).
|
||||
// Step 8. Perform ! CreateDataPropertyOrThrow(contextObj, "kind", kindStr).
|
||||
if (!oe.emitInit(frontend::AccessorType::None,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::kind())) {
|
||||
// [stack] context
|
||||
return false;
|
||||
}
|
||||
// 9. If kind is not class, then
|
||||
// Step 9. If kind is not class, then
|
||||
if (kind != Kind::Class) {
|
||||
// 9.a. Perform ! CreateDataPropertyOrThrow(contextObj, "access",
|
||||
// CreateDecoratorAccessObject(kind, name)).
|
||||
// Step 9.a. Perform ! CreateDataPropertyOrThrow(contextObj, "access",
|
||||
// CreateDecoratorAccessObject(kind, name)).
|
||||
if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -756,8 +936,8 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
// [stack] context
|
||||
return false;
|
||||
}
|
||||
// 9.b. If isStatic is present, perform
|
||||
// ! CreateDataPropertyOrThrow(contextObj, "static", isStatic).
|
||||
// Step 9.b. If isStatic is present, perform
|
||||
// ! CreateDataPropertyOrThrow(contextObj, "static", isStatic).
|
||||
if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -770,12 +950,11 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
// [stack] context
|
||||
return false;
|
||||
}
|
||||
// 9.c. If name is a Private Name, then
|
||||
// 9.c.i. Perform ! CreateDataPropertyOrThrow(contextObj, "private",
|
||||
// true).
|
||||
// 9.d. Else,
|
||||
// 9.d.i. Perform ! CreateDataPropertyOrThrow(contextObj, "private",
|
||||
// false).
|
||||
// Step 9.c. If name is a Private Name, then
|
||||
// Step 9.c.i. Perform ! CreateDataPropertyOrThrow(contextObj, "private",
|
||||
// true).
|
||||
// Step 9.d. Else, Step 9.d.i. Perform
|
||||
// ! CreateDataPropertyOrThrow(contextObj, "private", false).
|
||||
if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -789,11 +968,11 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
// [stack] context
|
||||
return false;
|
||||
}
|
||||
// 9.c.ii. Perform ! CreateDataPropertyOrThrow(contextObj, "name",
|
||||
// name.[[Description]]).
|
||||
// Step 9.c.ii. Perform ! CreateDataPropertyOrThrow(contextObj,
|
||||
// "name", name.[[Description]]).
|
||||
//
|
||||
// 9.d.ii. Perform
|
||||
// ! CreateDataPropertyOrThrow(contextObj, "name", name).)
|
||||
// Step 9.d.ii. Perform ! CreateDataPropertyOrThrow(contextObj,
|
||||
// "name", name.[[Description]]).)
|
||||
if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -812,13 +991,13 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
// 10. Else,
|
||||
// 10.a. Perform ! CreateDataPropertyOrThrow(contextObj, "name", name).
|
||||
// Steo 10. Else,
|
||||
// Step 10.a. Perform ! CreateDataPropertyOrThrow(contextObj, "name", name).
|
||||
// TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1793963
|
||||
return false;
|
||||
}
|
||||
// 11. Let addInitializer be CreateAddInitializerFunction(initializers,
|
||||
// decorationState).
|
||||
// Step 11. Let addInitializer be CreateAddInitializerFunction(initializers,
|
||||
// decorationState).
|
||||
if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -826,14 +1005,131 @@ bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
|
|||
// [stack] context addInitializer
|
||||
return false;
|
||||
}
|
||||
// 12. Perform ! CreateDataPropertyOrThrow(contextObj, "addInitializer",
|
||||
// addInitializer).
|
||||
// Step 12. Perform ! CreateDataPropertyOrThrow(contextObj, "addInitializer",
|
||||
// addInitializer).
|
||||
if (!oe.emitInit(
|
||||
frontend::AccessorType::None,
|
||||
frontend::TaggedParserAtomIndex::WellKnown::addInitializer())) {
|
||||
// [stack] context
|
||||
return false;
|
||||
}
|
||||
// 13. Return contextObj.
|
||||
// Step 13. Return contextObj.
|
||||
return oe.emitEnd();
|
||||
}
|
||||
|
||||
bool DecoratorEmitter::emitHandleNewValueField(TaggedParserAtomIndex atom,
|
||||
int8_t offset) {
|
||||
// This function handles retrieving the new value from a field in the RETVAL
|
||||
// object returned by the decorator. The `atom` is the atom of the field to be
|
||||
// examined. The offset is the offset of the existing value on the stack,
|
||||
// which will be replaced by the new value. If the offset is zero, we're
|
||||
// handling the initializer which will be added to the array of initializers
|
||||
// already on the stack.
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
|
||||
if (!bce_->emit1(JSOp::Dup)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL RETVAL
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emitStringOp(JSOp::String, atom)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL RETVAL ATOM
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOp::GetElem)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
// NEW_VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
IfEmitter ifCallable(bce_);
|
||||
if (!ifCallable.emitIf(mozilla::Nothing())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitCheckIsUndefined()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
// NEW_VALUE ISUNDEFINED
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ifCallable.emitThenElse()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
// NEW_VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pop the undefined getter or setter from the stack, leaving the original
|
||||
// values in place.
|
||||
if (!bce_->emitPopN(1)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ifCallable.emitElseIf(mozilla::Nothing())) {
|
||||
return false;
|
||||
}
|
||||
if (!emitCheckIsCallable()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
// NEW_VALUE ISCALLABLE_RESULT
|
||||
return false;
|
||||
}
|
||||
if (!ifCallable.emitThenElse()) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
// NEW_VALUE
|
||||
return false;
|
||||
}
|
||||
if (offset != 0) {
|
||||
if (!bce_->emitPickN(offset)) {
|
||||
// [stack] GETTER? SETTER? ARRAY INDEX RETVAL
|
||||
// NEW_VALUE GETTER_OR_SETTER
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emitPopN(1)) {
|
||||
// [stack] GETTER? SETTER? ARRAY INDEX RETVAL
|
||||
// NEW_VALUE
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emitUnpickN(offset - 1)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Offset == 0 means we're retrieving the initializer, this is
|
||||
// stored in the initializer array on the stack.
|
||||
if (!bce_->emit1(JSOp::Swap)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX NEW_VALUE RETVAL
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emitUnpickN(3)) {
|
||||
// [stack] GETTER SETTER RETVAL ARRAY INDEX NEW_VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emit1(JSOp::InitElemInc)) {
|
||||
// [stack] GETTER SETTER RETVAL ARRAY INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emitPickN(2)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX RETVAL
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ifCallable.emitElse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emitPopN(1)) {
|
||||
// [stack] GETTER SETTER ARRAY INDEX
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bce_->emit2(JSOp::ThrowMsg,
|
||||
uint8_t(ThrowMsgKind::DecoratorInvalidReturnType))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ifCallable.emitEnd();
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@ class MOZ_STACK_CLASS DecoratorEmitter {
|
|||
ListNode* decorators,
|
||||
bool isStatic);
|
||||
|
||||
[[nodiscard]] bool emitApplyDecoratorsToAccessorDefinition(
|
||||
ParseNode* key, ListNode* decorators, bool isStatic);
|
||||
|
||||
[[nodiscard]] bool emitInitializeFieldOrAccessor();
|
||||
|
||||
private:
|
||||
|
@ -52,6 +55,9 @@ class MOZ_STACK_CLASS DecoratorEmitter {
|
|||
[[nodiscard]] bool emitCreateDecoratorContextObject(Kind kind, ParseNode* key,
|
||||
bool isStatic,
|
||||
TokenPos pos);
|
||||
|
||||
[[nodiscard]] bool emitHandleNewValueField(TaggedParserAtomIndex atom,
|
||||
int8_t offset);
|
||||
};
|
||||
|
||||
} /* namespace js::frontend */
|
||||
|
|
|
@ -624,7 +624,6 @@ bool ClassEmitter::emitDerivedClass(TaggedParserAtomIndex name,
|
|||
}
|
||||
|
||||
bool ClassEmitter::emitInitConstructor(bool needsHomeObject) {
|
||||
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||
MOZ_ASSERT(classState_ == ClassState::Class ||
|
||||
classState_ == ClassState::InstanceMemberInitializersEnd);
|
||||
|
||||
|
|
|
@ -2299,6 +2299,16 @@ class ClassMethod : public BinaryNode {
|
|||
class ClassField : public BinaryNode {
|
||||
bool isStatic_;
|
||||
#ifdef ENABLE_DECORATORS
|
||||
// The accessorGetterNode_ and accessorSetterNode_ are used to store the
|
||||
// getter and setter synthesized by the `accessor` keyword when they are
|
||||
// decorated. Otherwise, they are null.
|
||||
//
|
||||
// In most cases, the accessors are not added to the class members, and the
|
||||
// code generation occurs immediately prior to the decorator running. For
|
||||
// non-static private methods, the accessors are added to the class members
|
||||
// which causes them to be stored in lexical variables. The references here
|
||||
// are used to store the names of the accessors to look up the values of these
|
||||
// variables during bytecode generation.
|
||||
ClassMethod* accessorGetterNode_;
|
||||
ClassMethod* accessorSetterNode_;
|
||||
ListNode* decorators_;
|
||||
|
@ -2325,7 +2335,6 @@ class ClassField : public BinaryNode {
|
|||
#ifdef ENABLE_DECORATORS
|
||||
MOZ_ASSERT((accessorGetterNode_ == nullptr) ==
|
||||
(accessorSetterNode_ == nullptr));
|
||||
MOZ_ASSERT_IF(!decorators_, !accessorGetterNode_);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -807,9 +807,13 @@ bool GeneralParser<ParseHandler, Unit>::noteDeclaredPrivateName(
|
|||
PrivateNameKind kind;
|
||||
switch (propType) {
|
||||
case PropertyType::Field:
|
||||
case PropertyType::FieldWithAccessor:
|
||||
kind = PrivateNameKind::Field;
|
||||
break;
|
||||
case PropertyType::FieldWithAccessor:
|
||||
// In this case, we create a new private field for the underlying storage,
|
||||
// and use the current name for the getter and setter.
|
||||
kind = PrivateNameKind::GetterSetter;
|
||||
break;
|
||||
case PropertyType::Method:
|
||||
case PropertyType::GeneratorMethod:
|
||||
case PropertyType::AsyncMethod:
|
||||
|
@ -7849,14 +7853,21 @@ bool GeneralParser<ParseHandler, Unit>::classMember(
|
|||
if (!accessorGetterNode) {
|
||||
return false;
|
||||
}
|
||||
if (!decorators) {
|
||||
// If the accessor is not decorated, add it to the class here.
|
||||
// Otherwise, we'll handle this when the decorators are called.
|
||||
// If the accessor is not decorated or is a non-static private field,
|
||||
// add it to the class here. Otherwise, we'll handle this when the
|
||||
// decorators are called. We don't need to keep a reference to the node
|
||||
// after this except for non-static private accessors. Please see the
|
||||
// comment in the definition of ClassField for details.
|
||||
bool addAccessorImmediately =
|
||||
!decorators || (!isStatic && handler_.isPrivateName(propName));
|
||||
if (addAccessorImmediately) {
|
||||
if (!handler_.addClassMemberDefinition(classMembers,
|
||||
accessorGetterNode)) {
|
||||
return false;
|
||||
}
|
||||
accessorGetterNode = null();
|
||||
if (!handler_.isPrivateName(propName)) {
|
||||
accessorGetterNode = null();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6. Let setter be MakeAutoAccessorSetter(homeObject, name,
|
||||
|
@ -7868,14 +7879,14 @@ bool GeneralParser<ParseHandler, Unit>::classMember(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!decorators) {
|
||||
// If the accessor is not decorated, add it to the class here.
|
||||
// Otherwise, we'll handle this when the decorators are called.
|
||||
if (addAccessorImmediately) {
|
||||
if (!handler_.addClassMemberDefinition(classMembers,
|
||||
accessorSetterNode)) {
|
||||
return false;
|
||||
}
|
||||
accessorSetterNode = null();
|
||||
if (!handler_.isPrivateName(propName)) {
|
||||
accessorSetterNode = null();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 10. Return ClassElementDefinition Record { [[Key]]: name,
|
||||
|
@ -9116,8 +9127,16 @@ GeneralParser<ParseHandler, Unit>::synthesizeAccessor(
|
|||
//
|
||||
// https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorsetter
|
||||
// 2. Let setter be CreateBuiltinFunction(setterClosure, 1, "set", « »).
|
||||
FunctionNodeType funNode =
|
||||
synthesizeAccessorBody(propNamePos, privateStateNameAtom, syntaxKind);
|
||||
StringBuffer storedMethodName(fc_);
|
||||
if (!storedMethodName.append(accessorType == AccessorType::Getter ? "get"
|
||||
: "set")) {
|
||||
return null();
|
||||
}
|
||||
TaggedParserAtomIndex funNameAtom =
|
||||
storedMethodName.finishParserAtom(this->parserAtoms(), fc_);
|
||||
|
||||
FunctionNodeType funNode = synthesizeAccessorBody(
|
||||
funNameAtom, propNamePos, privateStateNameAtom, syntaxKind);
|
||||
if (!funNode) {
|
||||
return null();
|
||||
}
|
||||
|
@ -9136,8 +9155,8 @@ GeneralParser<ParseHandler, Unit>::synthesizeAccessor(
|
|||
template <class ParseHandler, typename Unit>
|
||||
typename ParseHandler::FunctionNodeType
|
||||
GeneralParser<ParseHandler, Unit>::synthesizeAccessorBody(
|
||||
TokenPos propNamePos, TaggedParserAtomIndex propAtom,
|
||||
FunctionSyntaxKind syntaxKind) {
|
||||
TaggedParserAtomIndex funNameAtom, TokenPos propNamePos,
|
||||
TaggedParserAtomIndex propNameAtom, FunctionSyntaxKind syntaxKind) {
|
||||
if (!abortIfSyntaxParser()) {
|
||||
return null();
|
||||
}
|
||||
|
@ -9157,8 +9176,8 @@ GeneralParser<ParseHandler, Unit>::synthesizeAccessorBody(
|
|||
// Create the FunctionBox and link it to the function object.
|
||||
Directives directives(true);
|
||||
FunctionBox* funbox =
|
||||
newFunctionBox(funNode, TaggedParserAtomIndex::null(), flags,
|
||||
propNamePos.begin, directives, generatorKind, asyncKind);
|
||||
newFunctionBox(funNode, funNameAtom, flags, propNamePos.begin, directives,
|
||||
generatorKind, asyncKind);
|
||||
if (!funbox) {
|
||||
return null();
|
||||
}
|
||||
|
@ -9204,7 +9223,7 @@ GeneralParser<ParseHandler, Unit>::synthesizeAccessorBody(
|
|||
return null();
|
||||
}
|
||||
|
||||
NameNodeType privateNameNode = privateNameReference(propAtom);
|
||||
NameNodeType privateNameNode = privateNameReference(propNameAtom);
|
||||
if (!privateNameNode) {
|
||||
return null();
|
||||
}
|
||||
|
|
|
@ -1329,8 +1329,9 @@ class MOZ_STACK_CLASS GeneralParser : public PerHandlerParser<ParseHandler> {
|
|||
FunctionSyntaxKind syntaxKind,
|
||||
ClassInitializedMembers& classInitializedMembers);
|
||||
|
||||
FunctionNodeType synthesizeAccessorBody(TokenPos propNamePos,
|
||||
TaggedParserAtomIndex atom,
|
||||
FunctionNodeType synthesizeAccessorBody(TaggedParserAtomIndex funNameAtom,
|
||||
TokenPos propNamePos,
|
||||
TaggedParserAtomIndex propNameAtom,
|
||||
FunctionSyntaxKind syntaxKind);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
// |jit-test| skip-if: !getBuildConfiguration()['decorators']
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
let dec1Called = false;
|
||||
|
||||
// This explicitly tests the case where undefined is returned.
|
||||
function dec(value, context) {
|
||||
dec1Called = true;
|
||||
// return undefined
|
||||
}
|
||||
|
||||
function decorate_getter(value, context) {
|
||||
return {
|
||||
get: function() {
|
||||
return 2 * value.get.call(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function decorate_setter(value, context) {
|
||||
return {
|
||||
set: function(x) {
|
||||
return value.set.call(this, 2*x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function decorate_initializer(value, context) {
|
||||
return {
|
||||
init: function(initialValue) {
|
||||
return 2 * initialValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function checkDecoratorContext(kind, isPrivate, isStatic, name) {
|
||||
return (value, context) => {
|
||||
assertEq(typeof value, "object");
|
||||
assertEq(typeof value.get, "function");
|
||||
assertEq(typeof value.set, "function");
|
||||
assertEq(context.kind, kind);
|
||||
assertEq(typeof context.access, "object");
|
||||
assertEq(context.private, isPrivate);
|
||||
assertEq(context.static, isStatic);
|
||||
assertEq(context.name, name);
|
||||
assertEq(typeof context.addInitializer, "object");
|
||||
// return undefined
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
@dec accessor x = 1;
|
||||
@decorate_getter accessor x2 = 1;
|
||||
@decorate_setter @decorate_getter accessor x3 = 1;
|
||||
@decorate_initializer accessor x4 = 1;
|
||||
@decorate_initializer @decorate_initializer accessor x5 = 1;
|
||||
@decorate_setter @decorate_getter @decorate_initializer accessor x6 = 1;
|
||||
@checkDecoratorContext("accessor", true, false, "x8 accessor storage") accessor x8 = 1;
|
||||
@checkDecoratorContext("accessor", true, true, "x9 accessor storage") static accessor x9 = 1;
|
||||
@checkDecoratorContext("accessor", true, false, "#x10 accessor storage") accessor #x10 = 1;
|
||||
}
|
||||
|
||||
let c = new C();
|
||||
assertEq(dec1Called, true);
|
||||
assertEq(c.x, 1);
|
||||
c.x = 2;
|
||||
assertEq(c.x, 2);
|
||||
assertEq(c.x2, 2);
|
||||
assertEq(c.x3, 2);
|
||||
c.x3 = 4;
|
||||
assertEq(c.x3, 16);
|
||||
assertEq(c.x4, 2);
|
||||
assertEq(c.x5, 4);
|
||||
assertEq(c.x6, 4);
|
||||
c.x6 = 4;
|
||||
assertEq(c.x6, 16);
|
||||
|
||||
class D {
|
||||
@decorate_initializer accessor #x = 1;
|
||||
@decorate_getter accessor #x2 = 1;
|
||||
@decorate_setter @decorate_getter accessor #x3 = 1;
|
||||
|
||||
getX() {
|
||||
return this.#x;
|
||||
}
|
||||
|
||||
setX(v) {
|
||||
this.#x = v;
|
||||
}
|
||||
|
||||
getX2() {
|
||||
return this.#x2;
|
||||
}
|
||||
|
||||
setX2(v) {
|
||||
this.#x2 = v;
|
||||
}
|
||||
|
||||
getX3() {
|
||||
return this.#x3;
|
||||
}
|
||||
|
||||
setX3(v) {
|
||||
this.#x3 = v;
|
||||
}
|
||||
}
|
||||
|
||||
let d = new D();
|
||||
assertEq(d.getX(), 2);
|
||||
d.setX(4);
|
||||
assertEq(d.getX(), 4);
|
||||
assertEq(d.getX2(), 2);
|
||||
d.setX2(4);
|
||||
assertEq(d.getX2(), 8);
|
||||
assertEq(d.getX3(), 2);
|
||||
d.setX3(4);
|
||||
assertEq(d.getX3(), 16);
|
||||
|
||||
class E {
|
||||
@decorate_getter static accessor x = 1;
|
||||
}
|
||||
|
||||
assertEq(E.x, 2);
|
||||
E.x = 2;
|
||||
assertEq(E.x, 4);
|
||||
|
||||
class F {
|
||||
@decorate_getter static accessor #x = 1;
|
||||
|
||||
getX() {
|
||||
return F.#x;
|
||||
}
|
||||
|
||||
setX(v) {
|
||||
F.#x = v;
|
||||
}
|
||||
}
|
||||
let f = new F();
|
||||
assertEq(f.getX(), 2);
|
||||
f.setX(4);
|
||||
assertEq(f.getX(), 8);
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
class G {
|
||||
@(() => { return "hello!"; }) accessor x;
|
||||
}
|
||||
}, TypeError), "Returning a value other than undefined or a callable throws.";
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
class G {
|
||||
@(() => { return {get: "hello!"}; }) accessor x;
|
||||
}
|
||||
}, TypeError), "Returning a value other than undefined or a callable throws.";
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
class G {
|
||||
@(() => { return {set: "hello!"}; }) accessor x;
|
||||
}
|
||||
}, TypeError), "Returning a value other than undefined or a callable throws.";
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
class G {
|
||||
@(() => { return {init: "hello!"}; }) accessor x;
|
||||
}
|
||||
}, TypeError), "Returning a value other than undefined or a callable throws.";
|
|
@ -285,6 +285,7 @@
|
|||
MACRO_(infinity, infinity, "infinity") \
|
||||
MACRO_(intersection, intersection, "intersection") \
|
||||
MACRO_(Infinity, Infinity, "Infinity") \
|
||||
IF_DECORATORS(MACRO_(init, init, "init")) \
|
||||
MACRO_(initial, initial, "initial") \
|
||||
MACRO_(InitializeCollator, InitializeCollator, "InitializeCollator") \
|
||||
MACRO_(InitializeDateTimeFormat, InitializeDateTimeFormat, \
|
||||
|
|
Загрузка…
Ссылка в новой задаче