JSI HostFunctions should support being called as a constructor
- Dominant language
- JavaScript
- Stars
- 11.3k
- Forks
- 859
- Avg merge
- 1h 30m
- Merged PRs (30d)
- 3
Description
## Problem
Right now if you try to do `new SomeHostFunc();` in js, you get a type error:
```
JavaScript terminated via uncaught exception: This function cannot be used as a constructor.
TypeError: This function cannot be used as a constructor.
at global (:1:13)
```
## Solution
I'm guessing that `Function::createFromHostFunction()` only populates the `[[Call]]` internal property and not `[[Construct]]` (I haven't checked if it provides a `prototype` property or not). It seems like the simplest solution would be to populate `[[Construct]]` as well, so that `createFromHostFunction()` behaves similarly to a `function() {}` expression in js. This may also need to provide some way for the host function to detect if it has been called normally vs as a constructor.
Alternatively, you could provide a separate `Function::createConstructorFromHostFunction()` that behaves similarly to a `class {}` expression and only supports `[[Construct]]` but not `[[Call]]`. This would make it harder (but not impossible) to have types that provide the friendly behavior of behaving the same for `Foo(...args)` and `new Foo(...args)` (side rant: this *really* should have been the default behavior for ES6 classes... ☹️)
Finally, at the very least, until one of these is done, it would be nice to document in `jsi.h` that HostFunctions only support `[[Call]]` and not `[[Construct]]`.
## Additional Context
This is the somewhat gross workaround I'm using in order to make a constructable JS type backed by a HostFunction (I'm using some internal helpers, but it should be clear enough):
```cpp
auto ctor = globalType(env, "Function")
.call(env,
"nativeFunc",
util::format(R"(
return function %1(...args) {
"use strict";
if (!new.target)
throw TypeError("%1() must be called as a constructor");
nativeFunc(this, ...args);
})", s_type.name))
.asObject(env).asFunction(env)
.call(env, std::move(nativeFunc))
.asObject(env).asFunction(env);
```
Contributor guide
Assessment
This issue has not been assessed yet.