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

Instance of subclass lacks expected type and property #1763

Closed
tubular400 opened this issue Dec 9, 2020 · 6 comments
Closed

Instance of subclass lacks expected type and property #1763

tubular400 opened this issue Dec 9, 2020 · 6 comments

Comments

@tubular400
Copy link

I have 4 files, as follows:

package.json

{
  "scripts": {
    "run:k6": "echo k6 version: `k6 version`; k6 run -i 1 start.js; echo; echo; echo; echo",
    "run:node": "echo Node version: `node --version`; node start.js"
  },
  "type": "module"
}

start.js

import Company from './Company.js';

const options = {
  discardResponseBodies: true,
};

export default vu;
export { options, setup };

function createApiObjects() {
  return {
    company: new Company(),
  };
}

function setup() {
  const api = createApiObjects();

  console.log(`api.company instanceof Company: ${api.company instanceof Company}`);
  console.log(`api.company.f: ${api.company.f}`);

  const company = api.company.f();
}

function vu() {
  1
}

if (typeof __VU === 'undefined') { // Node.js
  setup();
}

Company.js

import General from './General.js';

export default class Company extends General() {}

General.js

export default (Superclass = Object) =>
  class General extends Superclass {
    f() {}
  };

If run npm run run:node, I get the following output (junk from npm not included):

Node version: v14.15.1
api.company instanceof Company: true
api.company.f: f() {}

That is what I would expect to see, based on my knowledge of JavaScript.

If run npm run run:k6, the output includes:

k6 version: k6 v0.29.0 (2020-11-11T13:27:19+0000/d9bced3, go1.15.3, linux/amd64)
[uninteresting lines from k6 snipped]
INFO[0001] api.company instanceof Company: false         source=console
INFO[0001] api.company.f: undefined                      source=console
ERRO[0004] Engine error                                  hint="TypeError: Object has no member 'f' at setup (file:///tmp/k6-bug/start.js:22:30(31))"

Note that the value for api.company instanceof Company is the opposite of what I get with Node.js, and the value for api.company.f is different too. Because api.company.f is undefined, I get the error message at the end.

To me, this looks like a clear bug in k6.

My operating system is Debian Linux 10.

@tubular400 tubular400 added the bug label Dec 9, 2020
@tubular400
Copy link
Author

BTW, this bug is blocking me, so I would appreciate a workaround, or an estimate of when it will be fixed.

@mstoykov
Copy link
Contributor

Hi @tubular400 , this seems to be a bug in our fairly old version of babel. And it works fine as long as

  class General extends Superclass {

doesn't have the extends ... no idea why though :( .

Running with #1663 it seems to work, but as that is unlikely to be merged soon (or if it's up to me ... never), so if you need to use it as is I can recommend that you use an updated version of babel through npm to the transformation before you run it through k6. There is repo showing how this can be done. This also in general lets your run with --compatibility-mode=base, which also uses less memory and is faster (mostly because of the lack of the additional polyfills).

I will be leaving this open, for future reference and for the discussion on whether we should update babel.

@mstoykov
Copy link
Contributor

For this script (simplified of the above):

class General extends Object {
    f() {}
};

class Company extends General {}


var company = new Company();

console.log(`company instanceof Company: ${company instanceof Company}`);
console.log(`company.f: ${company.f}`);
company.f();

#1663 generates (after some trying to make it more readable):

"use strict";
function _instanceof(left, right) {if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {return !!right[Symbol.hasInstance](left);
} else {return left instanceof right;
}}function _typeof(obj) {"@babel/helpers - typeof";
    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {_typeof = function _typeof(obj) {return typeof obj;
    };
    } else {_typeof = function _typeof(obj) {return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    };
    }return _typeof(obj);
}function _classCallCheck(instance, Constructor) {if (!_instanceof(instance, Constructor)) {throw new TypeError("Cannot call a class as a 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);
}}function _createClass(Constructor, protoProps, staticProps) {if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}function _inherits(subClass, superClass) {if (typeof superClass !== "function" && superClass !== null) {throw new TypeError("Super expression must either be null or a function");
}subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } });
if (superClass) _setPrototypeOf(subClass, superClass);
}function _createSuper(Derived) {var hasNativeReflectConstruct = _isNativeReflectConstruct();
    return function _createSuperInternal() {var Super = _getPrototypeOf(Derived),result;
        if (hasNativeReflectConstruct) {var NewTarget = _getPrototypeOf(this).constructor;
            result = Reflect.construct(Super, arguments, NewTarget);
        } else {result = Super.apply(this, arguments);
        }return _possibleConstructorReturn(this, result);
    };
}function _possibleConstructorReturn(self, call) {if (call && (_typeof(call) === "object" || typeof call === "function")) {return call;
}return _assertThisInitialized(self);
}function _assertThisInitialized(self) {if (self === void 0) {throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}return self;
}function _wrapNativeSuper(Class) {var _cache = typeof Map === "function" ? new Map() : undefined;
    _wrapNativeSuper = function _wrapNativeSuper(Class) {if (Class === null || !_isNativeFunction(Class)) return Class;
        if (typeof Class !== "function") {throw new TypeError("Super expression must either be null or a function");
        }if (typeof _cache !== "undefined") {if (_cache.has(Class)) return _cache.get(Class);
            _cache.set(Class, Wrapper);
        }function Wrapper() {return _construct(Class, arguments, _getPrototypeOf(this).constructor);
        }Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
        return _setPrototypeOf(Wrapper, Class);
    };
    return _wrapNativeSuper(Class);
}function _construct(Parent, args, Class) {if (_isNativeReflectConstruct()) {_construct = Reflect.construct;
} else {_construct = function _construct(Parent, args, Class) {var a = [null];
    a.push.apply(a, args);
    var Constructor = Function.bind.apply(Parent, a);
    var instance = new Constructor();
    if (Class) _setPrototypeOf(instance, Class.prototype);
    return instance;
};
}return _construct.apply(null, arguments);
}function _isNativeReflectConstruct() {if (typeof Reflect === "undefined" || !Reflect.construct) return false;
    if (Reflect.construct.sham) return false;
    if (typeof Proxy === "function") return true;
    try {Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
        return true;
    } catch (e) {return false;
    }}function _isNativeFunction(fn) {return Function.toString.call(fn).indexOf("[native code]") !== -1;
    }function _setPrototypeOf(o, p) {_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {o.__proto__ = p;
        return o;
    };
        return _setPrototypeOf(o, p);
    }fun
{} }]);
return General;
}( /*#__PURE__*/_wrapNativeSuper(Object));

;
var

Company = /*#__PURE__*/function (_General) {_inherits(Company, _General);
    var _super2 = _createSuper(Company);
    function Company() {_classCallCheck(this, Company);
        return _super2.apply(this, arguments);
    }return Company;
}(General);



var company = new Company();


console.log("company instanceof Company: ".concat(_instanceof(company, Company)));

console.log("company.f: ".concat(company.f));

company.f();

and the current master generates:

"use strict";


var _createClass = function () {function defineProperties(target, props) {for (var i = 0;
    i < props.length;
    i++) {var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descr
    iptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
}}return function (Constructor, protoProps, staticProps) {if (protoProps) d
    efineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {if (!(instance instanceof Co
    nstructor)) {throw new TypeError("Cannot call a class as a function");
}}function _possibleConstructorReturn(self, call) {if (!self) {throw new ReferenceError("this hasn't been initialised - super() hasn't been cal
    led");
}return call && (typeof call === "object" || typeof call === "function") ? call : self;
}function _inherits(subClass, superClass) {if (typeof superClass !== "function" && superClass !== null) {throw new TypeE
    rror("Super expression must either be null or a function, not " + typeof superClass);
}subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, wri
    table: true, configurable: true } });
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}var General = function (_Object) {_inherits(General, _Obj
    ect);
    function General() {_classCallCheck(this, General);
        return _possibleConstructorReturn(this, (General.__proto__ || Object.getPrototypeOf(General)).apply(this, arguments));
    }_createClass(General, [{ key: "f", val
        ue: function f()
        {} }]);
    return General;
}(Object);

;


var Company = function (_General) {_inherits(Company, _General);
    function Company() {_classCallCheck(this, Company);
        return _possibleConstructorReturn(this, (Company.__proto__ || Object.getPrototypeOf(Company)).apply(th
            is, arguments));
    }return Company;
}(General);



var company = new Company();


console.log("company instanceof Company: " + (company instanceof Company));

console.log("company.f: " + company.f);

company.f();

Obviously, the new babel generates way more code ... and probably some of it is what makes this work so this doesn't seem like a goja bug to me ... but maybe I am wrong and the code of the babel should've also worked, but doesn't because of some goja issue.

This code above doesn't work since at least v0.24.0 of k6

@tubular400
Copy link
Author

Thanks, @mstoykov.

I went for the quickest possible workaround, which was to remove extends Superclass.

@mstoykov
Copy link
Contributor

mstoykov commented Jan 5, 2021

Just for the record this only seems to happen if Object is extended. If you just extend some other empty class it works perfectly

@na-- na-- added the js-compat label Jan 27, 2021
@mstoykov
Copy link
Contributor

Fixed with #2618

@na-- na-- added this to the v0.40.0 milestone Aug 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants