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

assert.throws() does not accept an arrow function as the second argument #3275

Closed
asukakenji opened this issue Oct 8, 2015 · 10 comments
Closed
Labels
assert Issues and PRs related to the assert subsystem.

Comments

@asukakenji
Copy link

asukakenji commented Oct 8, 2015

Code:

var assert = require("assert");

console.log("Checkpoint 1");
assert.throws(function() { throw new TypeError(); }, function(err) { return err instanceof TypeError; });

console.log("Checkpoint 2");
assert.throws(() => { throw new TypeError(); }, function(err) { return err instanceof TypeError; });

console.log("Checkpoint 3");
assert.throws(() => { throw new TypeError(); }, (err) => { return err instanceof TypeError; });

console.log("Checkpoint 4");

Result:

Checkpoint 1
Checkpoint 2
Checkpoint 3
assert.js:271
  } else if (actual instanceof expected) {
                               ^

TypeError: Function has non-object prototype 'undefined' in instanceof check
    at expectedException (assert.js:271:32)
    at Function._throws (assert.js:310:8)
    at Function.assert.throws (assert.js:319:11)
    at Object.<anonymous> (/path/to/bug.js:10:8)
    at Module._compile (module.js:434:26)
    at Object.Module._extensions..js (module.js:452:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:475:10)
    at startup (node.js:118:18)

The results are the same no matter which of the following syntax is used:

(err) => { return err instanceof TypeError; }
err => { return err instanceof TypeError; }
err => err instanceof TypeError

[BridgeAR: fixed examples by adding missing return statements]

@asukakenji
Copy link
Author

May be related to: #3188

@thefourtheye
Copy link
Contributor

Important thing to understand here is, arrow functions will not have their prototype property defined. It will be undefined. Since instanceof checks the prototypes and it couldn't find one in case of arrow functions, it throws that error.

Please keep in mind that, assert is not a general purpose assertion library.

@thefourtheye thefourtheye added question Issues that look for answers. assert Issues and PRs related to the assert subsystem. labels Oct 8, 2015
@bnoordhuis
Copy link
Member

It is a somewhat unsatisfying dichotomy though, especially since (function() {}).__proto__ === (() => {}).__proto__.

@thefourtheye
Copy link
Contributor

True. I am not sure why they chose to compare prototype instead of the internal property.

function f() {}
f.prototype = undefined;
console.log({} instanceof f);

Even this throws the same error.

@bnoordhuis
Copy link
Member

My reading of the ES6 spec is that, for better or worse, V8's behavior is conforming. SpiderMonkey works the same way, FWIW. I'll file a pull request with a workaround.

@bnoordhuis
Copy link
Member

#3276

@asukakenji
Copy link
Author

@thefourtheye :

It may be a silly idea, but I am afraid that f.prototype in your code snippet does not make sense to me.

You wrote:

Important thing to understand here is, arrow functions will not have their prototype property defined. It will be undefined. Since instanceof checks the prototypes and it couldn't find one in case of arrow functions, it throws that error.

I think the .prototype property belongs to the functions (constructors), while the .__proto__ (or the internal [[prototype]] property, to be exact) property belongs to the instances. To find the prototype of an instance, we use xxx.__proto__ or Object.getPrototypeOf(xxx). It should be equal to the .prototype property of the function used to construct the instance. The instanceof operator traces the prototype chain via the .__proto__ property, not the .prototype property.

Here is a demonstration showing how it works:

function Person() {}

var person = new Person();

console.log(person.__proto__ === Person.prototype);    // true

console.log(person.prototype === undefined);    // true

console.log(person.__proto__.__proto__ === Object.prototype);    // true

I wonder why xxx instanceof Function or xxx.__proto__ === Function.prototype or Object.getPrototypeOf(xxx) === Function.prototype could not be used in assert.js. I just view the assert.js source code and I know why now: the code involved is dealing with the function's "first use case".

Here is a demonstration showing how they work:

console.log(typeof () => {} === "function");    // true

console.log((() => {}) instanceof Function);    // true

console.log(typeof (() => {}).__proto__ === "function");    // true

console.log((() => {}).__proto__ === Function.prototype);    // true

console.log(typeof Object.getPrototypeOf(() => {}) === "function");    // true

console.log(Object.getPrototypeOf(() => {}) === Function.prototype);    // true

console.log(typeof (() => {}).prototype === "undefined");    // true

console.log((() => {}).prototype === undefined);    // true

console.log("--------");

console.log(typeof function() {} === "function");    // true

console.log((function() {}) instanceof Function);    // true

console.log(typeof (function() {}).__proto__ === "function");    // true

console.log((function() {}).__proto__ === Function.prototype);    // true

console.log(typeof Object.getPrototypeOf(function() {}) === "function");    // true

console.log(Object.getPrototypeOf(function() {}) === Function.prototype);    // true

console.log(typeof (function() {}).prototype === "object");    // true

console.log((function() {}).prototype === Function.prototype);    // false

console.log("--------");

I am using Node.js v4.1.2. I confirmed that using use strict or --harmony or both does not make a difference in the above results.

@asukakenji
Copy link
Author

Some more ideas:

function Person() {}

var person = new Person();

console.log(person instanceof Person);    // true

Person.prototype = undefined;

// Still works!
console.log(Object.getPrototypeOf(person).constructor === Person);    // true

console.log(person instanceof Person);    // exception

VS

function f() {}

console.log({} instanceof f);    // false

f.prototype = undefined;

// Still works!
console.log(Object.getPrototypeOf({}).constructor === f);    // false

console.log({} instanceof f);    // exception

@mscdex mscdex removed the question Issues that look for answers. label Oct 8, 2015
bnoordhuis added a commit to bnoordhuis/io.js that referenced this issue Oct 8, 2015
`x instanceof f` where f is an arrow function throws a (spec-conforming)
"Function has non-object prototype 'undefined' in instanceof check"
exception.

Add a workaround so that it's possible to pass arrow functions as the
second argument to assert.throws().  The try/catch block is a little
jarring but swapping around the clauses in the if statements changes
the semantics too much.

Fixes: nodejs#3275
PR-URL: nodejs#3276
Reviewed-By: Michaël Zasso <mic.besace@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
bnoordhuis added a commit that referenced this issue Oct 10, 2015
`x instanceof f` where f is an arrow function throws a (spec-conforming)
"Function has non-object prototype 'undefined' in instanceof check"
exception.

Add a workaround so that it's possible to pass arrow functions as the
second argument to assert.throws().  The try/catch block is a little
jarring but swapping around the clauses in the if statements changes
the semantics too much.

Fixes: #3275
PR-URL: #3276
Reviewed-By: Michaël Zasso <mic.besace@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
@lumaxis
Copy link

lumaxis commented Aug 25, 2017

Is this issue broken again in Node 8.4.0 (or intentionally changed)? I'm experiencing the exact same issue here when trying to pass an arrow function as the second argument to .throws().

@BridgeAR
Copy link
Member

@lumaxis I can not reproduce this. Do you have a test case for me?

@asukakenji I updated your original examples to include the missing return statement in arrow functions with braces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assert Issues and PRs related to the assert subsystem.
Projects
None yet
Development

No branches or pull requests

6 participants