Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

super call in private instance method incorrectly calls overridden method #9788

Closed
MattiasBuelens opened this issue Mar 29, 2019 · 3 comments
Labels
i: bug i: needs triage outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Comments

@MattiasBuelens
Copy link
Contributor

MattiasBuelens commented Mar 29, 2019

Bug Report

Current Behavior
Calling a super method from within a private instance method or accessor calls the overridden method instead of the superclass's method. Similarly, accessing a super property accesses the overridden property instead of the superclass's property.

For reference: in #9580, I reported that super method calls and property accesses didn't work at all, which was fixed in #9704. This issue shows that although such a super call/access no longer throws, it can still call the wrong method/accessor.

Input Code
REPL link

I still can't get the REPL link to include additional plugins, so you have to manually add these:

  • @babel/plugin-proposal-class-properties
  • @babel/plugin-proposal-private-methods
class SuperClass {
    someMethod() {
        console.log('super');
    }
}

class SubClass extends SuperClass {
    #privateMethod() {
        super.someMethod();
    }

    publicMethod() {
        this.#privateMethod();
    }

    someMethod() {
        console.log('sub');
    }
}

new SubClass().publicMethod();

Expected behavior/code
The console logs a message:

super

Actual behavior/code
The console logs a message:

sub
Generated code
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }

function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }

class SuperClass {
  someMethod() {
    console.log('super');
  }

}

class SubClass extends SuperClass {
  constructor(...args) {
    super(...args);

    _privateMethod.add(this);
  }

  publicMethod() {
    _classPrivateMethodGet(this, _privateMethod, _privateMethod2).call(this);
  }

  someMethod() {
    console.log('sub');
  }

}

var _privateMethod = new WeakSet();

var _privateMethod2 = function _privateMethod2() {
  _get(_getPrototypeOf(this), "someMethod", this).call(this);
};

new SubClass().publicMethod();

Babel Configuration (.babelrc, package.json, cli command)

{
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-private-methods"
  ]
}

Environment

  • Babel version(s): 7.4.2
  • Node/npm version: 10.15.0
  • OS: Windows 10
  • Monorepo: no
  • How you are using Babel: cli

Possible Solution
The generated _privateMethod2 is incorrect:

var _privateMethod2 = function _privateMethod2() {
  _get(_getPrototypeOf(this), "someMethod", this).call(this);
};

_getPrototypeOf(this) returns SubClass.prototype, so we call SubClass.prototype.someMethod (which is incorrect).

Instead, I would expect the generated code to look something like:

var _privateMethod2 = function _privateMethod2() {
  _get(_getPrototypeOf(SubClass.prototype), "someMethod", this).call(this);
};

Additional context/Screenshots
N/A

@babel-bot
Copy link
Collaborator

Hey @MattiasBuelens! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community
that typically always has someone willing to help. You can sign-up here
for an invite.

@MattiasBuelens
Copy link
Contributor Author

Interestingly, it does work when setting { loose: true } for both proposal plugins. The generated _privateMethod2 calls the correct method:

var _privateMethod2 = function _privateMethod2() {
  SuperClass.prototype.someMethod.call(this);
};
Generated code in loose mode
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }

var id = 0;

function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }

class SuperClass {
  someMethod() {
    console.log('super');
  }

}

class SubClass extends SuperClass {
  constructor(...args) {
    super(...args);
    Object.defineProperty(this, _privateMethod, {
      value: _privateMethod2
    });
  }

  publicMethod() {
    _classPrivateFieldLooseBase(this, _privateMethod)[_privateMethod]();
  }

  someMethod() {
    console.log('sub');
  }

}

var _privateMethod = _classPrivateFieldLooseKey("privateMethod");

var _privateMethod2 = function _privateMethod2() {
  SuperClass.prototype.someMethod.call(this);
};

new SubClass().publicMethod();
.babelrc
{
  "plugins": [
    [
      "@babel/plugin-proposal-class-properties",
      {
        "loose": true
      }
    ],
    [
      "@babel/plugin-proposal-private-methods",
      {
        "loose": true
      }
    ]
  ]
}

@nicolo-ribaudo
Copy link
Member

Fixed by #9801

@lock lock bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Jul 1, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Jul 1, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
i: bug i: needs triage outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
Development

No branches or pull requests

3 participants