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

ES.isCallable not spec compliant #48

Open
mhofman opened this issue May 6, 2019 · 6 comments · May be fixed by #93
Open

ES.isCallable not spec compliant #48

mhofman opened this issue May 6, 2019 · 6 comments · May be fixed by #93

Comments

@mhofman
Copy link

mhofman commented May 6, 2019

It looks like ES.isCallable simply leverages the is-callable package. However as noted in inspect-js/is-callable#16, that implementation is not equivalent to the IsCallable from the spec.

Class constructors do have a [[Call]] internal slot which makes them callable.
The [[Call]] internal method throws TypeError if [[FunctionKind]] == "classConstructor", but that's simply the implementation of the internal method.

@ljharb
Copy link
Owner

ljharb commented May 6, 2019

It's unfortunate that typeof x === 'function' is specced to only detect [[Call]], and not also [[Construct]]; this means that a constructor that can't be invoked does, as you indicate, have a throwing [[Call]]. It would have been much better to be able to have a function that has [[Construct]] and lacks [[Call]].

I'd be happy to change IsCallable to match the spec - ie, to be return typeof x === 'function' - if I found a way to make an IsConstructor check without relying on Proxy. Do you have any ideas?

@mhofman
Copy link
Author

mhofman commented May 7, 2019

Why not use your toString / class regex logic from is-callable to implement IsConstructor ?

@ljharb
Copy link
Owner

ljharb commented May 7, 2019

It's not reliable due to many browsers not toStringing class constructors with class in them.

@mhofman
Copy link
Author

mhofman commented May 7, 2019

This looks like an interesting approach: https://stackoverflow.com/a/46759625
Relies on Reflect.construct but not Proxy

@ljharb
Copy link
Owner

ljharb commented May 7, 2019

Is Reflect in any engine that lacks Proxy? If it’s not polyfillable it’s not much of an approach :-/

@ExE-Boss
Copy link
Contributor

The current IsCallable spec only tests the presence of [[Call]] and not the value of [[IsClassConstructor]]: https://tc39.es/ecma262/#sec-iscallable.


Also, there’s a simpler IsConstructor implementation:

const isConstructorMarker = {};
const badArrayLike = {
	get length() {
		throw isConstructorMarker;
	}
};

const IsConstructor = value => {
	try {
		Reflect.construct(value, badArrayLike);
	} catch (err) {
		return err === isConstructorMarker;
	}
};

This is because Reflect.constructtarget, argumentsList [ , newTarget ] ) checks IsConstructor(target) before doing Get(argumentsList"length"): tc39/ecma262#1798 (comment).


Although, it won’t work in environments that don’t support getters.

@ExE-Boss ExE-Boss linked a pull request May 16, 2020 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants