Skip to content

Commit

Permalink
Merge pull request #1013 from NativeScript/tdermendzhiev/functions-ov…
Browse files Browse the repository at this point in the history
…erloading

feat: Add support for methods with same name and different parameters
  • Loading branch information
tdermendjiev committed Dec 21, 2018
2 parents 4d6b4ce + 7085dc9 commit 741db1e
Show file tree
Hide file tree
Showing 42 changed files with 1,240 additions and 599 deletions.
6 changes: 4 additions & 2 deletions src/NativeScript/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ set(HEADER_FILES
Calling/FFICallback.h
Calling/FFICallbackInlines.h
Calling/FFICallPrototype.h
Calling/FFIFunctionCall.h
Calling/CFunctionWrapper.h
Calling/FFIFunctionCallback.h
Calling/FunctionWrapper.h
GlobalObject.h
inspector/CachedResource.h
inspector/DomainBackendDispatcher.h
Expand Down Expand Up @@ -115,8 +116,9 @@ set(SOURCE_FILES
Calling/FFICache.cpp
Calling/FFICall.cpp
Calling/FFICallPrototype.cpp
Calling/FFIFunctionCall.mm
Calling/CFunctionWrapper.mm
Calling/FFIFunctionCallback.cpp
Calling/FunctionWrapper.cpp
GlobalObject.mm
GlobalObject.moduleLoader.mm
inspector/CachedResource.mm
Expand Down
82 changes: 82 additions & 0 deletions src/NativeScript/Calling/CFunctionWrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// CFunctionWrapper.h
// NativeScript
//
// Created by Yavor Georgiev on 07.07.14.
// Copyright (c) 2014 г. Telerik. All rights reserved.
//

#ifndef __NativeScript__CFunctionWrapper__
#define __NativeScript__CFunctionWrapper__

#include "FFICall.h"
#include "FunctionWrapper.h"

namespace NativeScript {

class CFunctionCall : public FFICall {

public:
CFunctionCall(FunctionWrapper* owner, void* functionPointer, bool retainsReturnedCocoaObjects)
: FFICall(owner)
, _functionPointer(functionPointer)
, _retainsReturnedCocoaObjects(retainsReturnedCocoaObjects) {
}

DECLARE_INFO;

template <typename CellType>
static JSC::IsoSubspace* subspaceFor(JSC::VM& vm) {
return &vm.tnsCFunctionWrapperSpace;
}

void* functionPointer() const {
return this->_functionPointer;
}
bool retainsReturnedCocoaObjects() const {
return this->_retainsReturnedCocoaObjects;
}

private:
void* _functionPointer;

bool _retainsReturnedCocoaObjects;
};

class CFunctionWrapper : public FunctionWrapper {
public:
typedef FunctionWrapper Base;

static CFunctionWrapper* create(JSC::VM& vm, JSC::Structure* structure, void* functionPointer, const WTF::String& name, JSC::JSCell* returnType, const WTF::Vector<JSC::JSCell*>& parameterTypes, bool retainsReturnedCocoaObjects) {
CFunctionWrapper* function = new (NotNull, JSC::allocateCell<CFunctionWrapper>(vm.heap)) CFunctionWrapper(vm, structure);
function->finishCreation(vm, functionPointer, name, returnType, parameterTypes, retainsReturnedCocoaObjects);
return function;
}

DECLARE_INFO;

static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) {
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
}

void* functionPointer() {
return static_cast<CFunctionCall*>(this->onlyFuncInContainer())->functionPointer();
}

private:
CFunctionWrapper(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure) {
}

void finishCreation(JSC::VM&, void* functionPointer, const WTF::String& name, JSC::JSCell* returnType, const WTF::Vector<JSC::JSCell*>& parameterTypes, bool retainsReturnedCocoaObjects);

static void destroy(JSC::JSCell* cell) {
static_cast<CFunctionWrapper*>(cell)->~CFunctionWrapper();
}

static void preInvocation(FFICall*, JSC::ExecState*, FFICall::Invocation&);
static void postInvocation(FFICall*, JSC::ExecState*, FFICall::Invocation&);
};
} // namespace NativeScript

#endif /* defined(__NativeScript__CFunctionWrapper__) */
34 changes: 34 additions & 0 deletions src/NativeScript/Calling/CFunctionWrapper.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// CFunctionWrapper.cpp
// NativeScript
//
// Created by Yavor Georgiev on 07.07.14.
// Copyright (c) 2014 г. Telerik. All rights reserved.
//

#include "CFunctionWrapper.h"

namespace NativeScript {
using namespace JSC;

const ClassInfo CFunctionWrapper::s_info = { "CFunctionWrapper", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(CFunctionWrapper) };

void CFunctionWrapper::finishCreation(VM& vm, void* functionPointer, const WTF::String& name, JSCell* returnType, const WTF::Vector<JSCell*>& parameterTypes, bool retainsReturnedCocoaObjects) {
Base::finishCreation(vm, name);
auto call = std::make_unique<CFunctionCall>(this, functionPointer, retainsReturnedCocoaObjects);
call->initializeFFI(vm, { &preInvocation, &postInvocation }, returnType, parameterTypes);
this->_functionsContainer.push_back(std::move(call));
Base::initializeFunctionWrapper(vm, parameterTypes.size());
}

void CFunctionWrapper::preInvocation(FFICall* callee, ExecState*, FFICall::Invocation& invocation) {
invocation.function = const_cast<void*>(static_cast<CFunctionCall*>(callee)->functionPointer());
}

void CFunctionWrapper::postInvocation(FFICall* callee, ExecState*, FFICall::Invocation& invocation) {
CFunctionCall* call = static_cast<CFunctionCall*>(callee);
if (call->retainsReturnedCocoaObjects()) {
[invocation.getResult<id>() release];
}
}
}
143 changes: 5 additions & 138 deletions src/NativeScript/Calling/FFICall.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//
// FFICall.cpp
// FunctionWrapper.cpp
// NativeScript
//
// Created by Yavor Georgiev on 12.06.14.
// Copyright (c) 2014 г. Telerik. All rights reserved.
// Created by Teodor Dermendzhiev on 10/15/18.
//

#include "FFICall.h"
#include "FFICache.h"
#include "FunctionWrapper.h"
#include <JavaScriptCore/JSPromiseDeferred.h>
#include <JavaScriptCore/StrongInlines.h>
#include <JavaScriptCore/interpreter/FrameTracers.h>
Expand All @@ -16,9 +17,6 @@
#include <malloc/malloc.h>

namespace NativeScript {
using namespace JSC;

const ClassInfo FFICall::s_info = { "FFICall", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(FFICall) };

void deleteCif(ffi_cif* cif) {
delete[] cif->arg_types;
Expand All @@ -30,11 +28,10 @@ void FFICall::initializeFFI(VM& vm, const InvocationHooks& hooks, JSCell* return

this->_initialArgumentIndex = initialArgumentIndex;

this->_returnTypeCell.set(vm, this, returnType);
this->_returnTypeCell.set(vm, owner, returnType);
this->_returnType = getFFITypeMethodTable(vm, returnType);

size_t parametersCount = parameterTypes.size();
this->putDirect(vm, vm.propertyNames->length, jsNumber(parametersCount), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete);

const ffi_type** parameterTypesFFITypes = new const ffi_type*[parametersCount + initialArgumentIndex];

Expand All @@ -47,7 +44,7 @@ void FFICall::initializeFFI(VM& vm, const InvocationHooks& hooks, JSCell* return

for (size_t i = 0; i < parametersCount; i++) {
JSCell* parameterTypeCell = parameterTypes[i];
this->_parameterTypesCells.append(WriteBarrier<JSCell>(vm, this, parameterTypeCell));
this->_parameterTypesCells.append(WriteBarrier<JSCell>(vm, owner, parameterTypeCell));

const FFITypeMethodTable& ffiTypeMethodTable = getFFITypeMethodTable(vm, parameterTypeCell);
this->_parameterTypes.append(ffiTypeMethodTable);
Expand All @@ -72,7 +69,6 @@ void FFICall::initializeFFI(VM& vm, const InvocationHooks& hooks, JSCell* return
this->_stackSize += malloc_good_size(std::max(this->_cif->arg_types[i]->size, sizeof(ffi_arg)));
}
}

std::shared_ptr<ffi_cif> FFICall::getCif(unsigned int nargs, ffi_type* rtype, ffi_type** atypes) {

WTF::LockHolder lock(FFICache::global()->_cacheLock);
Expand All @@ -87,133 +83,4 @@ std::shared_ptr<ffi_cif> FFICall::getCif(unsigned int nargs, ffi_type* rtype, ff
return FFICache::global()->cifCache[this->signatureVector];
}

FFICall::~FFICall() {
WTF::LockHolder lock(FFICache::global()->_cacheLock);
if (this->_cif.use_count() == 2) {
FFICache::FFIMap::const_iterator it;
it = FFICache::global()->cifCache.find(this->signatureVector);
if (it != FFICache::global()->cifCache.end()) {
FFICache::global()->cifCache.erase(it);
}
}
}

void FFICall::visitChildren(JSCell* cell, SlotVisitor& visitor) {
Base::visitChildren(cell, visitor);

FFICall* ffiCall = jsCast<FFICall*>(cell);
visitor.append(ffiCall->_returnTypeCell);
visitor.append(ffiCall->_parameterTypesCells.begin(), ffiCall->_parameterTypesCells.end());
}

EncodedJSValue JSC_HOST_CALL FFICall::call(ExecState* execState) {
FFICall* callee = jsCast<FFICall*>(execState->callee().asCell());
Invocation invocation(callee);
ReleasePoolHolder releasePoolHolder(execState);

JSC::VM& vm = execState->vm();
auto scope = DECLARE_THROW_SCOPE(vm);

callee->preCall(execState, invocation);
callee->_invocationHooks.pre(callee, execState, invocation);
if (scope.exception()) {
return JSValue::encode(scope.exception());
}

{
JSLock::DropAllLocks locksDropper(execState);
ffi_call(callee->_cif.get(), FFI_FN(invocation.function), invocation._buffer + callee->_returnOffset, reinterpret_cast<void**>(invocation._buffer + callee->_argsArrayOffset));
}

JSValue result = callee->_returnType.read(execState, invocation._buffer + callee->_returnOffset, callee->_returnTypeCell.get());

if (InvocationHook post = callee->_invocationHooks.post) {
post(callee, execState, invocation);
}

return JSValue::encode(result);
}

JSObject* FFICall::async(ExecState* execState, JSValue thisValue, const ArgList& arguments) {
__block std::unique_ptr<Invocation> invocation(new Invocation(this));
ReleasePoolHolder releasePoolHolder(execState);

Register* fakeCallFrame = new Register[CallFrame::headerSizeInRegisters + execState->argumentCount() + 1];
ExecState* fakeExecState = ExecState::create(fakeCallFrame);

fakeExecState->setArgumentCountIncludingThis(arguments.size() + 1);
fakeExecState->setCallee(this);
fakeExecState->setThisValue(thisValue);
fakeExecState->setCodeBlock(nullptr);
fakeExecState->setCallerFrame(execState->callerFrame());
for (size_t i = 0; i < arguments.size(); i++) {
fakeExecState->setArgument(i, arguments.at(i));
}
ASSERT(fakeExecState->argumentCount() == arguments.size());

{
JSC::VM& vm = execState->vm();
auto scope = DECLARE_THROW_SCOPE(vm);

TopCallFrameSetter frameSetter(execState->vm(), fakeExecState);
this->preCall(fakeExecState, *invocation);
this->_invocationHooks.pre(this, fakeExecState, *invocation);
if (Exception* exception = scope.exception()) {
delete[] fakeCallFrame;
return exception;
}
}

JSPromiseDeferred* deferred = JSPromiseDeferred::create(execState, execState->lexicalGlobalObject());
auto* releasePool = new ReleasePoolBase::Item(releasePoolHolder.relinquish());
__block Strong<FFICall> callee(execState->vm(), this);

dispatch_async(dispatch_get_global_queue(0, 0), ^{
JSC::VM& vm = fakeExecState->vm();
auto scope = DECLARE_CATCH_SCOPE(vm);

ffi_call(callee->_cif.get(), FFI_FN(invocation->function), invocation->resultBuffer(), reinterpret_cast<void**>(invocation->_buffer + callee->_argsArrayOffset));

// Native call is made outside of the VM lock by design.
// For more information see https://github.com/NativeScript/ios-runtime/issues/215 and it's corresponding PR.
// This creates a racing condition which might corrupt the internal state of the VM but
// a fix for it is outside of this PR's scope, so I'm leaving it like it has always been.

JSLockHolder lockHolder(vm);
// we no longer have a valid caller on the stack, what with being async and all
fakeExecState->setCallerFrame(CallFrame::noCaller());

JSValue result;
{
TopCallFrameSetter frameSetter(fakeExecState->vm(), fakeExecState);
result = _returnType.read(fakeExecState, invocation->_buffer + _returnOffset, _returnTypeCell.get());

if (InvocationHook post = _invocationHooks.post) {
post(this, fakeExecState, *invocation);
}
}

if (Exception* exception = scope.exception()) {
scope.clearException();
CallData rejectCallData;
CallType rejectCallType = JSC::getCallData(vm, deferred->reject(), rejectCallData);

MarkedArgumentBuffer rejectArguments;
rejectArguments.append(exception->value());
JSC::call(fakeExecState->lexicalGlobalObject()->globalExec(), deferred->reject(), rejectCallType, rejectCallData, jsUndefined(), rejectArguments);
} else {
CallData resolveCallData;
CallType resolveCallType = JSC::getCallData(vm, deferred->resolve(), resolveCallData);

MarkedArgumentBuffer resolveArguments;
resolveArguments.append(result);
JSC::call(fakeExecState->lexicalGlobalObject()->globalExec(), deferred->resolve(), resolveCallType, resolveCallData, jsUndefined(), resolveArguments);
}

delete[] fakeCallFrame;
delete releasePool;
});

return deferred->promise();
}
} // namespace NativeScript
Loading

0 comments on commit 741db1e

Please sign in to comment.