[Webkit-unassigned] [Bug 206985] New: No clean way of calling JSObjectCallAsFunction with an undefined 'this' object
bugzilla-daemon at webkit.org
bugzilla-daemon at webkit.org
Wed Jan 29 20:47:22 PST 2020
https://bugs.webkit.org/show_bug.cgi?id=206985
Bug ID: 206985
Summary: No clean way of calling JSObjectCallAsFunction with an
undefined 'this' object
Product: WebKit
Version: WebKit Nightly Build
Hardware: All
OS: All
Status: NEW
Severity: Normal
Priority: P2
Component: JavaScriptCore
Assignee: webkit-unassigned at lists.webkit.org
Reporter: gabriel at kronopath.net
This isn't a bug, but instead is, as far as I can tell, a bit of an oversight in the API.
If you have a JSObjectRef that points to a function object, you can call that function with JSObjectCallAsFunction. This is useful for handling callbacks from native code in C++ (heavily simplified code):
// C++ side:
void nativeFunction(JSObjectRef callback) {
// ...
JSObjectCallAsFunction(ctx, callback, thisObject, 1, resultArgs, &exception);
// ...
}
// JS side:
nativeFunction(function (result) {
doSomeStuffWith(result);
});
This is roughly analogous to callback-based programming in JS:
function longRunningFunction(callback) {
// ...
callback();
// ...
}
longRunningFunction(function (result) {
doSomeStuffWith(result);
});
There's a particular quirk that comes up with the 'this' object, though. In non-strict mode, accessing 'this' inside the callback will give you the global object:
longRunningFunction(function (result) {
this === globalThis; // true in non-strict mode
});
But when you are in strict mode, 'this' ends up being undefined:
"use strict";
longRunningFunction(function (result) {
this === undefined; // true
});
Currently, in callbacks from a native function, you can emulate the non-strict-style behaviour by passing nullptr as the thisObject in JSObjectCallAsFunction. As the docs say, this sets 'this' to be the global object.
The implementation of this behaviour is here: https://github.com/WebKit/webkit/blob/db267339ce27f469f18aa15b64f0915c1ea33869/Source/JavaScriptCore/API/JSObjectRef.cpp#L696-L697
However, this is true regardless of whether your function was declared to be strict or non-strict. JSC always sets 'this' to be the global object if you pass nullptr. The only other alternative is to pass an actual alternative object for 'thisObject'.
There doesn't seem to be a clean way of emulating the strict-mode functionality where 'this' is undefined.
auto undefinedThis = JSValueToObject(ctx, JSValueMakeUndefined(ctx), &exception);
// Check exception here
JSObjectCallAsFunction(ctx, callback, undefinedThis, 1, resultArgs, &exception);
This doesn't work because JSValueToObject raises a JS exception saying "undefined is not an object".
auto undefinedThis = const_cast<JSObjectRef>(JSValueMakeUndefined(ctx));
JSObjectCallAsFunction(ctx, callback, undefinedThis, 1, resultArgs, &exception);
This seems to work, but it really doesn't seem kosher. I'm not an expert in the JSC codebase yet, it seems like this works because it just so happens that no one in the call chain of JSObjectCallAsFunction ever actually treats thisObject as an object, in the sense of trying to access its properties and the like. It just manipulates the wrapper values for it and dodges all the footguns that could happen along the way, and if the implementation ever changes it's possible that this hack would run into one of them.
Is this solution safe to use? It seems to work, but this doesn't look like the way the API was intended to work. But, as far as I can tell, this is the only way to have an 'undefined' 'this' from a function call triggered by native code.
If you compare this to Facebook's Hermes VM, they have two methods for calling function objects, one callWithThis where you can provide your own custom 'this' object, and one that's just 'call', where it leaves it either undefined or sets it to the global object depending on strictness. That latter API, specifically with strict semantics, is what JSC currently lacks.
https://github.com/facebook/hermes/blob/6e7eecd93bfd8c3f8c2f790a30b1fe98eefe5e28/API/jsi/jsi/jsi-inl.h#L222-L265
Would it make sense to add this to the JSC API?
--
You are receiving this mail because:
You are the assignee for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.webkit.org/pipermail/webkit-unassigned/attachments/20200130/7d43aae5/attachment.htm>
More information about the webkit-unassigned
mailing list