Commit f7e2fbe3 authored by Mohamed Atef's avatar Mohamed Atef
Browse files

Bug 1321932: Statically set computed property method's function name if...

Bug 1321932: Statically set computed property method's function name if computed property name is string or number. r=arai

Differential Revision: https://phabricator.services.mozilla.com/D153889
parent 24dd519d
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -8845,6 +8845,9 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
      //            [stack] CTOR? OBJ CTOR? KEY?

      if (propVal->isDirectRHSAnonFunction()) {
        // The following branches except for the last `else` clause
        // emit emits the cases handled in
        // NameResolver::resolveFun (see NameFunctions.cpp)
        if (key->isKind(ParseNodeKind::NumberExpr)) {
          MOZ_ASSERT(accessorType == AccessorType::None);

@@ -8865,6 +8868,31 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
            //      [stack] CTOR? OBJ CTOR? VAL
            return false;
          }
        } else if (key->isKind(ParseNodeKind::ComputedName) &&
                   (key->as<UnaryNode>().kid()->isKind(
                        ParseNodeKind::NumberExpr) ||
                    key->as<UnaryNode>().kid()->isKind(
                        ParseNodeKind::StringExpr)) &&
                   accessorType == AccessorType::None) {
          ParseNode* keyKid = key->as<UnaryNode>().kid();
          if (keyKid->isKind(ParseNodeKind::NumberExpr)) {
            auto keyAtom =
                keyKid->as<NumericLiteral>().toAtom(cx, parserAtoms());
            if (!keyAtom) {
              return false;
            }
            if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
              //    [stack] CTOR? OBJ CTOR? KEY VAL
              return false;
            }
          } else {
            MOZ_ASSERT(keyKid->isKind(ParseNodeKind::StringExpr));
            auto keyAtom = keyKid->as<NameNode>().atom();
            if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
              //    [stack] CTOR? OBJ CTOR? KEY VAL
              return false;
            }
          }
        } else {
          MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName) ||
                     key->isKind(ParseNodeKind::BigIntExpr));
+34 −0
Original line number Diff line number Diff line
@@ -258,6 +258,7 @@ class NameResolver : public ParseNodeVisitor<NameResolver> {

    // If the function is assigned to something, then that is very relevant.
    if (assignment) {
      // e.g, foo = function() {}
      if (assignment->is<AssignmentNode>()) {
        assignment = assignment->as<AssignmentNode>().left();
      }
@@ -281,14 +282,47 @@ class NameResolver : public ParseNodeVisitor<NameResolver> {
        ParseNode* left = node->as<BinaryNode>().left();
        if (left->isKind(ParseNodeKind::ObjectPropertyName) ||
            left->isKind(ParseNodeKind::StringExpr)) {
          // Here we handle two cases:
          // 1) ObjectPropertyName category, e.g `foo: function() {}`
          // 2) StringExpr category, e.g `"foo": function() {}`
          if (!appendPropertyReference(left->as<NameNode>().atom())) {
            return false;
          }
        } else if (left->isKind(ParseNodeKind::NumberExpr)) {
          // This case handles Number expression Anonymous Functions
          // for example:  `{ 10: function() {} }`.
          if (!appendNumericPropertyReference(
                  left->as<NumericLiteral>().value())) {
            return false;
          }
        } else if (left->isKind(ParseNodeKind::ComputedName) &&
                   (left->as<UnaryNode>().kid()->isKind(
                        ParseNodeKind::StringExpr) ||
                    left->as<UnaryNode>().kid()->isKind(
                        ParseNodeKind::NumberExpr)) &&
                   node->as<PropertyDefinition>().accessorType() ==
                       AccessorType::None) {
          // In this branch we handle computed property with string
          // or numeric literal:
          // e.g, `{ ["foo"]: function(){} }`, and `{ [10]: function() {} }`.
          //
          // Note we only handle the names that are known at compile time,
          // so if we have `var x = 5/"foo"; { [x]: function(){} }`, we don't
          // handle that here, it's handled at runtime by JSOp::SetFunName.
          // The accessor type of the property must be AccessorType::None,
          // given getters and setters need prefix and we cannot handle it here.
          ParseNode* kid = left->as<UnaryNode>().kid();
          if (kid->isKind(ParseNodeKind::StringExpr)) {
            if (!appendPropertyReference(kid->as<NameNode>().atom())) {
              return false;
            }
          } else {
            MOZ_ASSERT(kid->isKind(ParseNodeKind::NumberExpr));
            if (!appendNumericPropertyReference(
                    kid->as<NumericLiteral>().value())) {
              return false;
            }
          }
        } else {
          MOZ_ASSERT(left->isKind(ParseNodeKind::ComputedName) ||
                     left->isKind(ParseNodeKind::BigIntExpr));
+57 −0
Original line number Diff line number Diff line
var obj = {
  ["func"]: function() {},
  ["genFunc"]: function*() {},
  ["asyncFunc"]: async function() {},
  ["asyncGenFunc"]: async function*() {},
  ["arrowFunc"]: ()=>{},
  ["asyncArrowFunc"]: async ()=>{},
  ["method"]() {},
  ["anonClass"]: class {},
  ["nonAnonymousFunc"]: function F() {},
  ["nonAnonymousClass"]: class C{},
  get ["getter"]() {},
  set ["setter"](x) {},
};

assertEq(obj.func.name, "func");
assertEq(obj.genFunc.name, "genFunc");
assertEq(obj.asyncFunc.name, "asyncFunc");
assertEq(obj.asyncGenFunc.name, "asyncGenFunc");
assertEq(obj.arrowFunc.name, "arrowFunc");
assertEq(obj.asyncArrowFunc.name, "asyncArrowFunc");
assertEq(obj.method.name, "method");
assertEq(obj.anonClass.name, "anonClass");
assertEq(obj.nonAnonymousFunc.name, "F");
assertEq(obj.nonAnonymousClass.name, "C");

assertEq(Object.getOwnPropertyDescriptor(obj, "getter").get.name, "get getter");
assertEq(Object.getOwnPropertyDescriptor(obj, "setter").set.name, "set setter");

let dummy = class {
  ["func"]() {}
  *["genFunc"] () {}
  async ["asyncFunc"]() {}
  async *["asyncGenFunc"]() {}
  ["arrowFunc"] = ()=>{}
  ["asyncArrowFunc"] = async ()=>{};
  ["method"]() {}
  get ["getter"]() {}
  set ["setter"](x) {}
};

dum = new dummy();

assertEq(dum.func.name, "func");
assertEq(dum.genFunc.name, "genFunc");
assertEq(dum.asyncFunc.name, "asyncFunc");
assertEq(dum.asyncGenFunc.name, "asyncGenFunc");
assertEq(dum.arrowFunc.name, "arrowFunc");
assertEq(dum.asyncArrowFunc.name, "asyncArrowFunc");
assertEq(dum.method.name, "method");

assertEq(Object.getOwnPropertyDescriptor(dummy.prototype, "getter").get.name, "get getter");
assertEq(Object.getOwnPropertyDescriptor(dummy.prototype, "setter").set.name, "set setter");

if (typeof reportCompare === "function")
  reportCompare(true, true);
+57 −0
Original line number Diff line number Diff line
var obj = {
  [1]: function() {},
  [2]: function*() {},
  [3]: async function() {},
  [4]: async function*() {},
  [5]: ()=>{},
  [6]: async ()=>{},
  [7] () {},
  [8]: class {},
  [9]: function F() {},
  [10]: class C{},
  get [11]() {},
  set [12](x) {},
};

assertEq(obj[1].name, "1");
assertEq(obj[2].name, "2");
assertEq(obj[3].name, "3");
assertEq(obj[4].name, "4");
assertEq(obj[5].name, "5");
assertEq(obj[6].name, "6");
assertEq(obj[7].name, "7");
assertEq(obj[8].name, "8");
assertEq(obj[9].name, "F");
assertEq(obj[10].name, "C");
assertEq(Object.getOwnPropertyDescriptor(obj, "11").get.name, "get 11");
assertEq(Object.getOwnPropertyDescriptor(obj, "12").set.name, "set 12");

let dummy = class {
  [1]() {}
  *[2]() {}
  async [3]() {}
  async *[4]() {}
  [5] = ()=>{}
  [6] = async ()=>{};
  [7] () {}
  get [11]() {}
  set [12](x) {}
};

dum = new dummy();

assertEq(dum[1].name, "1");
assertEq(dum[2].name, "2");
assertEq(dum[3].name, "3");
assertEq(dum[4].name, "4");
assertEq(dum[5].name, "5");
assertEq(dum[6].name, "6");
assertEq(dum[7].name, "7");

assertEq(Object.getOwnPropertyDescriptor(dummy.prototype, "11").get.name, "get 11");
assertEq(Object.getOwnPropertyDescriptor(dummy.prototype, "12").set.name, "set 12");


if (typeof reportCompare === "function")
  reportCompare(true, true);
+72 −0
Original line number Diff line number Diff line
// Guessed Atoms tests.
// Test String literals
var obj = {
  ["func"]: function() {return function() {} },
  ["arrowFunc"]: ()=>{return function() {} },
  ["method"]() {return function() {} },
  ["nonAnonymousFunc"]: function F() {return function() {} },
  get ["getter"]() {return function() {} },
};


assertEq(displayName(obj.func()), "func/<");
assertEq(displayName(obj.arrowFunc()), "arrowFunc/<");
assertEq(displayName(obj.method()), "method/<");
assertEq(displayName(obj.nonAnonymousFunc()), "F/<");

// We don't support guessed name for accessor
assertEq(displayName(obj.getter), "");

let dummy = class {
  ["func"]() {return function() {} }
  ["arrowFunc"] = ()=>{return function() {} };
  ["method"]() {return function() {} }
  get ["getter"]() {return function() {} }
};

dum = new dummy();
assertEq(displayName(dum.func()), "func/<");
// We don't support guessed name for class field with computed name
assertEq(displayName(dum.arrowFunc()), "dummy</</<");
assertEq(displayName(dum.method()), "method/<");
// We don't support guessed name for accessor
assertEq(displayName(dummy.prototype.getter), "dummy</<");


// Test numeric literals
var objN = {
  [1]: function() {return function() {}},
  [5]: ()=>{return function() {}},
  [7] () {return function() {}},
  [8]: class {},
  [9]: function F() {return function() {}},
  [10]: class C{},
  get [11]() {return function() {}},
};
assertEq(displayName(objN[1]()), "1/<");
assertEq(displayName(objN[5]()), "5/<");
assertEq(displayName(objN[7]()), "7/<");
assertEq(displayName(objN[8]), "8");
assertEq(displayName(objN[9]()), "F/<");
assertEq(displayName(objN[10]), "C");
// We don't support guessed name for accessor
assertEq(displayName(objN[11]), "objN/<");

let foo = class {
  [1] () {return function() {} }
  [5] = ()=>{return function() {} };
  [7] () {return function() {} }
  get [11]() {return function() {} }
};

f = new foo();

assertEq(displayName(f[1]()), "1/<");
// We don't support guessed name for class field with computed name
assertEq(displayName(f[5]()), "foo</</<");
assertEq(displayName(f[7]()), "7/<");
assertEq(displayName(f[11]), "foo</<");


if (typeof reportCompare === "function")
  reportCompare(true, true);