Ignore:
Timestamp:
Oct 10, 2013, 11:20:13 AM (12 years ago)
Author:
[email protected]
Message:

Objective-C API: blocks aren't callable via 'new'
https://bugs.webkit.org/show_bug.cgi?id=122561

Reviewed by Oliver Hunt.

Currently the only way for clients to vend new native objects to JavaScript code
is via factory methods in the form of exported class methods or blocks. Blocks can
be called like normal functions from JavaScript code, but they cannot be invoked
with 'new'. This would give a simple way for clients to expose constructor-like
behavior to their JavaScript code.

This patch adds the ability for blocks to be invoked as if they were a constructor.
Blocks invoked as constructors are required to return an object. If the block doesn't
return an object then an error is thrown. The 'this' object is not provided to the
block and must be created within the block itself.

This patch also unifies the native 'construct' callback used in both the C and Obj-C
APIs under the APICallbackFunction struct, similar to how we unified the 'call' callback
between ObjCCallbackFunction and JSCallbackFunction before.

This patch also adds tests to make sure that different blocks generate objects that
correctly behave when queried with instanceof. It also makes sure that the correct
JS exception is thrown when a block fails to return an object.

  • API/APICallbackFunction.h:

(JSC::APICallbackFunction::call):
(JSC::APICallbackFunction::construct):

  • API/JSCallbackConstructor.cpp:

(JSC::JSCallbackConstructor::getConstructData):

  • API/JSCallbackConstructor.h:

(JSC::JSCallbackConstructor::constructCallback):

  • API/JSCallbackFunction.h:

(JSC::JSCallbackFunction::functionCallback):

  • API/ObjCCallbackFunction.h:

(JSC::ObjCCallbackFunction::functionCallback):
(JSC::ObjCCallbackFunction::constructCallback):

  • API/ObjCCallbackFunction.mm:

(JSC::objCCallbackFunctionCallAsConstructor):
(JSC::ObjCCallbackFunction::ObjCCallbackFunction):
(JSC::ObjCCallbackFunction::create):
(JSC::ObjCCallbackFunction::getConstructData):

  • API/tests/testapi.mm:
File:
1 edited

Legend:

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

    r156240 r157234  
    3030#include "APIShims.h"
    3131#include "Error.h"
     32#include "JSCallbackConstructor.h"
    3233#include <wtf/Vector.h>
    3334
     
    3738
    3839template <typename T> static EncodedJSValue JSC_HOST_CALL call(ExecState*);
     40template <typename T> static EncodedJSValue JSC_HOST_CALL construct(ExecState*);
    3941
    4042};
     
    5759    {
    5860        APICallbackShim callbackShim(exec);
    59         result = jsCast<T*>(toJS(functionRef))->m_callback(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception);
     61        result = jsCast<T*>(toJS(functionRef))->functionCallback()(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception);
    6062    }
    6163    if (exception)
     
    6870    return JSValue::encode(toJS(exec, result));
    6971}
     72
     73template <typename T>
     74EncodedJSValue JSC_HOST_CALL APICallbackFunction::construct(ExecState* exec)
     75{
     76    JSObject* constructor = exec->callee();
     77    JSContextRef ctx = toRef(exec);
     78    JSObjectRef constructorRef = toRef(constructor);
     79
     80    JSObjectCallAsConstructorCallback callback = jsCast<T*>(constructor)->constructCallback();
     81    if (callback) {
     82        size_t argumentCount = exec->argumentCount();
     83        Vector<JSValueRef, 16> arguments;
     84        arguments.reserveInitialCapacity(argumentCount);
     85        for (size_t i = 0; i < argumentCount; ++i)
     86            arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i)));
     87
     88        JSValueRef exception = 0;
     89        JSObjectRef result;
     90        {
     91            APICallbackShim callbackShim(exec);
     92            result = callback(ctx, constructorRef, argumentCount, arguments.data(), &exception);
     93        }
     94        if (exception) {
     95            exec->vm().throwException(exec, toJS(exec, exception));
     96            return JSValue::encode(toJS(exec, exception));
     97        }
     98        // result must be a valid JSValue.
     99        if (!result)
     100            return throwVMTypeError(exec);
     101        return JSValue::encode(toJS(result));
     102    }
     103   
     104    return JSValue::encode(toJS(JSObjectMake(ctx, jsCast<JSCallbackConstructor*>(constructor)->classRef(), 0)));
     105}
     106
    70107} // namespace JSC
    71108
Note: See TracChangeset for help on using the changeset viewer.