bublejs / buble

https://buble.surge.sh
MIT License
871 stars 67 forks source link

Super getter/setter bug #168

Open zont opened 6 years ago

zont commented 6 years ago

Source:

class A {
  set x(value) {
    return this._xA = `A_${value}`;
  }

  get x() {
    return this._xA;
  }
}

class B extends A {
  set x(value) {
    super.x = value;
    this.y = `B_${value}`;
  }

  get x() {
    return super.x;
  }
}

const z = new B();
const w = new B();

z.x = 111;

console.log(z.x, z.y, w.x, w.y);

Result in console: A_111 B_111 undefined undefined

Buble compiled:

var A = function A () {};

var prototypeAccessors = { x: { configurable: true } };

prototypeAccessors.x.set = function (value) {
  return this._xA = "A_" + value;
};

prototypeAccessors.x.get = function () {
  return this._xA;
};

Object.defineProperties( A.prototype, prototypeAccessors );

var B = /*@__PURE__*/(function (A) {
  function B () {
    A.apply(this, arguments);
  }

  if ( A ) B.__proto__ = A;
  B.prototype = Object.create( A && A.prototype );
  B.prototype.constructor = B;

  var prototypeAccessors$1 = { x: { configurable: true } };

  prototypeAccessors$1.x.set = function (value) {
    A.prototype.x = value;
    this.y = "B_" + value;
  };

  prototypeAccessors$1.x.get = function () {
    return A.prototype.x;
  };

  Object.defineProperties( B.prototype, prototypeAccessors$1 );

  return B;
}(A));

var z = new B();
var w = new B();

z.x = 111;

console.log(z.x, z.y, w.x, w.y);

Result in console: A_111 B_111 A_111 undefined


For example, Babel compiled:

"use strict";

var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _set = function set(object, property, value, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent !== null) { set(parent, property, value, receiver); } } else if ("value" in desc && desc.writable) { desc.value = value; } else { var setter = desc.set; if (setter !== undefined) { setter.call(receiver, value); } } return value; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var A = function () {
  function A() {
    _classCallCheck(this, A);
  }

  _createClass(A, [{
    key: "x",
    set: function set(value) {
      return this._xA = "A_" + value;
    },
    get: function get() {
      return this._xA;
    }
  }]);

  return A;
}();

var B = function (_A) {
  _inherits(B, _A);

  function B() {
    _classCallCheck(this, B);

    return _possibleConstructorReturn(this, (B.__proto__ || Object.getPrototypeOf(B)).apply(this, arguments));
  }

  _createClass(B, [{
    key: "x",
    set: function set(value) {
      _set(B.prototype.__proto__ || Object.getPrototypeOf(B.prototype), "x", value, this);
      this.y = "B_" + value;
    },
    get: function get() {
      return _get(B.prototype.__proto__ || Object.getPrototypeOf(B.prototype), "x", this);
    }
  }]);

  return B;
}(A);

var z = new B();
var w = new B();

z.x = 111;

console.log(z.x, z.y, w.x, w.y);

Result in console: A_111 B_111 undefined undefined

adrianheine commented 6 years ago

Thanks for the report! So, the setter or getter of a super class is called on the prototype of that super class instead of on the instance. When assigning to a super property, Babel checks at runtime of that super property has a setter and if it does calls that setter with the correct context (same applies for getters).