Ignore:
Timestamp:
Aug 27, 2020, 9:41:27 AM (5 years ago)
Author:
[email protected]
Message:

JSClassRef should work with JS class syntax.
https://bugs.webkit.org/show_bug.cgi?id=215047

Reviewed by Darin Adler.

This is done by checking if value returned by the
callAsConstructor parameter to JSObjectMakeConstructor returns an
object allocated as the jsClass parameter. When that happens we
replace the prototype of the returned object with the prototype of
the new.target. Ideally we would have passed the derived classes
constructor from the beginning of our support for JS subclassing
but at this point that's probably not compatible with too many
applications.

  • API/APICallbackFunction.h:

(JSC::APICallbackFunction::construct):

  • API/JSObjectRef.h:
  • API/tests/testapi.cpp:

(APIString::APIString):
(TestAPI::markedJSValueArrayAndGC):
(TestAPI::classDefinitionWithJSSubclass):
(testCAPIViaCpp):

  • API/tests/testapi.mm:

(testObjectiveCAPI):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/APICallbackFunction.h

    r261464 r266236  
    8080    VM& vm = getVM(globalObject);
    8181    auto scope = DECLARE_THROW_SCOPE(vm);
    82     JSObject* constructor = callFrame->jsCallee();
     82    JSValue callee = callFrame->jsCallee();
     83    T* constructor = jsCast<T*>(callFrame->jsCallee());
    8384    JSContextRef ctx = toRef(globalObject);
    8485    JSObjectRef constructorRef = toRef(constructor);
    8586
    86     JSObjectCallAsConstructorCallback callback = jsCast<T*>(constructor)->constructCallback();
     87    JSObjectCallAsConstructorCallback callback = constructor->constructCallback();
    8788    if (callback) {
     89        JSValue prototype;
     90        JSValue newTarget = callFrame->newTarget();
     91        // If we are doing a derived class construction get the .prototype property off the new target first so we behave closer to normal JS.
     92        if (newTarget != constructor) {
     93            prototype = newTarget.get(globalObject, vm.propertyNames->prototype);
     94            RETURN_IF_EXCEPTION(scope, { });
     95        }
     96
    8897        size_t argumentCount = callFrame->argumentCount();
    8998        Vector<JSValueRef, 16> arguments;
     
    98107            result = callback(ctx, constructorRef, argumentCount, arguments.data(), &exception);
    99108        }
     109
    100110        if (exception) {
    101111            throwException(globalObject, scope, toJS(globalObject, exception));
     
    105115        if (!result)
    106116            return throwVMTypeError(globalObject, scope);
    107         return JSValue::encode(toJS(result));
     117
     118        JSObject* newObject = toJS(result);
     119        // This won't trigger proxy traps on newObject's prototype handler but that's probably desirable here anyway.
     120        if (newTarget != constructor && newObject->getPrototypeDirect(vm) == constructor->get(globalObject, vm.propertyNames->prototype)) {
     121            RETURN_IF_EXCEPTION(scope, { });
     122            newObject->setPrototype(vm, globalObject, prototype);
     123            RETURN_IF_EXCEPTION(scope, { });
     124        }
     125
     126        return JSValue::encode(newObject);
    108127    }
    109128   
    110     return JSValue::encode(toJS(JSObjectMake(ctx, jsCast<JSCallbackConstructor*>(constructor)->classRef(), nullptr)));
     129    return JSValue::encode(toJS(JSObjectMake(ctx, jsCast<JSCallbackConstructor*>(callee)->classRef(), nullptr)));
    111130}
    112131
Note: See TracChangeset for help on using the changeset viewer.