[bun:ffi] ~20% faster FFI bindings for functions with arguments

This commit is contained in:
Jarred Sumner
2022-05-02 03:23:42 -07:00
parent 98393ca849
commit 5cee316d8c
10 changed files with 220 additions and 13 deletions

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2015-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "root.h"
#include "JSFFIFunction.h"
#include "JavaScriptCore/JSCJSValueInlines.h"
#include "JavaScriptCore/VM.h"
#include "ZigGlobalObject.h"
extern "C" Zig::JSFFIFunction* Bun__CreateFFIFunction(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer)
{
JSC::VM& vm = globalObject->vm();
Zig::JSFFIFunction* function = Zig::JSFFIFunction::create(vm, globalObject, argCount, Zig::toStringCopy(*symbolName), functionPointer, JSC::NoIntrinsic);
JSC::gcProtect(function);
return function;
}
namespace Zig {
using namespace JSC;
const ClassInfo JSFFIFunction::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSFFIFunction) };
JSFFIFunction::JSFFIFunction(VM& vm, NativeExecutable* executable, JSGlobalObject* globalObject, Structure* structure, FFIFunction&& function)
: Base(vm, executable, globalObject, structure)
, m_function(WTFMove(function))
{
}
template<typename Visitor>
void JSFFIFunction::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
JSFFIFunction* thisObject = jsCast<JSFFIFunction*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
}
DEFINE_VISIT_CHILDREN(JSFFIFunction);
void JSFFIFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name)
{
Base::finishCreation(vm, executable, length, name);
ASSERT(inherits(vm, info()));
}
JSFFIFunction* JSFFIFunction::create(VM& vm, Zig::GlobalObject* globalObject, unsigned length, const String& name, FFIFunction FFIFunction, Intrinsic intrinsic, NativeFunction nativeConstructor)
{
NativeExecutable* executable = vm.getHostFunction(FFIFunction, intrinsic, nativeConstructor, nullptr, name);
Structure* structure = globalObject->FFIFunctionStructure();
JSFFIFunction* function = new (NotNull, allocateCell<JSFFIFunction>(vm)) JSFFIFunction(vm, executable, globalObject, structure, WTFMove(FFIFunction));
function->finishCreation(vm, executable, length, name);
return function;
}
} // namespace JSC

View File

@@ -0,0 +1,71 @@
#pragma once
namespace Zig {
class GlobalObject;
}
#include "root.h"
#include "JavaScriptCore/JSFunction.h"
#include "JavaScriptCore/VM.h"
#include "headers-handwritten.h"
#include "BunClientData.h"
#include "WebCoreJSBuiltinInternals.h"
#include "JavaScriptCore/CallFrame.h"
namespace JSC {
class JSGlobalObject;
}
namespace Zig {
using namespace JSC;
using FFIFunction = JSC::EncodedJSValue (*)(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame);
class JSFFIFunction final : public JSC::JSFunction {
public:
using Base = JSFunction;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static constexpr bool needsDestruction = false;
static void destroy(JSCell* cell)
{
static_cast<JSFFIFunction*>(cell)->JSFFIFunction::~JSFFIFunction();
}
template<typename, SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
return nullptr;
return WebCore::subspaceForImpl<JSFFIFunction, WebCore::UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForFFIFunction.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForFFIFunction = WTFMove(space); },
[](auto& spaces) { return spaces.m_subspaceForFFIFunction.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceForFFIFunction = WTFMove(space); });
}
DECLARE_EXPORT_INFO;
JS_EXPORT_PRIVATE static JSFFIFunction* create(VM&, Zig::GlobalObject*, unsigned length, const String& name, FFIFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor);
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
ASSERT(globalObject);
return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info());
}
const FFIFunction function() { return m_function; }
private:
JSFFIFunction(VM&, NativeExecutable*, JSGlobalObject*, Structure*, FFIFunction&&);
void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name);
DECLARE_VISIT_CHILDREN;
FFIFunction m_function;
};
} // namespace JSC
extern "C" Zig::JSFFIFunction* Bun__CreateFFIFunction(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer);

View File

@@ -89,6 +89,11 @@
#include "JavaScriptCore/RemoteInspectorServer.h"
#include "WebCoreJSBuiltinInternals.h"
#include "JSBuffer.h"
#include "JSFFIFunction.h"
#include "JavaScriptCore/InternalFunction.h"
#include "JavaScriptCore/LazyClassStructure.h"
#include "JavaScriptCore/LazyClassStructureInlines.h"
#include "JavaScriptCore/FunctionPrototype.h"
using JSGlobalObject = JSC::JSGlobalObject;
using Exception = JSC::Exception;
@@ -857,6 +862,11 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
this->addStaticGlobals(extraStaticGlobals.data(), extraStaticGlobals.size());
m_JSFFIFunctionStructure.initLater(
[](LazyClassStructure::Initializer& init) {
init.setStructure(Zig::JSFFIFunction::createStructure(init.vm, init.global, init.global->m_functionPrototype.get()));
});
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "process"_s), JSC::CustomGetterSetter::create(vm, property_lazyProcessGetter, property_lazyProcessSetter),
JSC::PropertyAttribute::CustomAccessor | 0);
@@ -923,7 +933,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.append(constructor);
// thisObject->m_builtinInternalFunctions.visit(visitor);
thisObject->m_JSFFIFunctionStructure.visit(visitor);
ScriptExecutionContext* context = thisObject->scriptExecutionContext();
visitor.addOpaqueRoot(context);
}

View File

@@ -6,6 +6,7 @@
namespace JSC {
class Structure;
class Identifier;
class LazyClassStructure;
} // namespace JSC
@@ -127,6 +128,7 @@ public:
void setConsole(void* console);
void installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm);
WebCore::JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; }
JSC::Structure* FFIFunctionStructure() { return m_JSFFIFunctionStructure.getInitializedOnMainThread(this); }
private:
void addBuiltinGlobals(JSC::VM&);
@@ -139,6 +141,7 @@ private:
Lock m_gcLock;
WebCore::ScriptExecutionContext* m_scriptExecutionContext;
Ref<WebCore::DOMWrapperWorld> m_world;
LazyClassStructure m_JSFFIFunctionStructure;
};
class JSMicrotaskCallback : public RefCounted<JSMicrotaskCallback> {

View File

@@ -1,4 +1,4 @@
// Auto-generated by src/javascript/jsc/headergen/sizegen.cpp at 2021-09-05 16:21:1630884091.
// Auto-generated by src/javascript/jsc/headergen/sizegen.cpp at 2022-05-02 01:43:1651481039.
// These are the byte sizes for the different object types with bindings in JavaScriptCore.
// This allows us to safely return stack allocated C++ types to Zig.
// It is only safe to do this when these sizes are correct.
@@ -15,6 +15,12 @@
// Run "jsc-bindings-headers" twice because it uses these values in the output. That's how all the bJSC__.* types are created - from these values.
pub const JSC__JSObject = 16;
pub const JSC__JSObject_align = 8;
pub const WebCore__DOMURL = 112;
pub const WebCore__DOMURL_align = 8;
pub const WebCore__FetchHeaders = 40;
pub const WebCore__FetchHeaders_align = 8;
pub const SystemError = 72;
pub const SystemError_align = 8;
pub const JSC__JSCell = 8;
pub const JSC__JSCell_align = 4;
pub const JSC__JSString = 16;
@@ -35,7 +41,7 @@ pub const JSC__SourceCode = 24;
pub const JSC__SourceCode_align = 8;
pub const JSC__JSFunction = 32;
pub const JSC__JSFunction_align = 8;
pub const JSC__JSGlobalObject = 2400;
pub const JSC__JSGlobalObject = 2312;
pub const JSC__JSGlobalObject_align = 8;
pub const WTF__URL = 40;
pub const WTF__URL_align = 8;
@@ -47,7 +53,7 @@ pub const JSC__PropertyName = 8;
pub const JSC__PropertyName_align = 8;
pub const JSC__Exception = 40;
pub const JSC__Exception_align = 8;
pub const JSC__VM = 48824;
pub const JSC__VM = 52168;
pub const JSC__VM_align = 8;
pub const JSC__ThrowScope = 8;
pub const JSC__ThrowScope_align = 8;
@@ -59,9 +65,16 @@ pub const JSC__Identifier = 8;
pub const JSC__Identifier_align = 8;
pub const WTF__StringImpl = 24;
pub const WTF__StringImpl_align = 8;
pub const WTF__ExternalStringImpl = 32;
pub const WTF__ExternalStringImpl = 40;
pub const WTF__ExternalStringImpl_align = 8;
pub const WTF__StringView = 16;
pub const WTF__StringView_align = 8;
pub const Zig__GlobalObject = 2400;
pub const Zig__GlobalObject = 2384;
pub const Zig__GlobalObject_align = 8;
pub const Bun__Readable = 24;
pub const Bun__Readable_align = 4;
pub const Bun__Writable = 20;
pub const Bun__Writable_align = 4;
pub const Bun__Path = 8;
pub const Bun__Path_align = 8;
pub const Bun_FFI_PointerOffsetToArgumentsList = 6;

View File

@@ -17,6 +17,7 @@ public:
/* --- bun --- */
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBuffer;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFFIFunction;
/* --- bun --- */
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForGlobalObject;

View File

@@ -17,6 +17,7 @@ public:
DOMIsoSubspaces() = default;
/*-- BUN --*/
std::unique_ptr<IsoSubspace> m_subspaceForBuffer;
std::unique_ptr<IsoSubspace> m_subspaceForFFIFunction;
/*-- BUN --*/
// std::unique_ptr<IsoSubspace> m_subspaceForTouch;

View File

@@ -31,12 +31,18 @@ export class CString extends String {
#cachedArrayBuffer;
get arrayBuffer() {
return (this.#cachedArrayBuffer ||= toArrayBuffer.apply(
null,
typeof this.byteOffset === "number" &&
typeof this.byteLength === typeof this.byteOffset
? [this.ptr, this.byteOffset, this.byteLength]
: [this.ptr]
if (this.#cachedArrayBuffer) {
return this.#cachedArrayBuffer;
}
if (!this.ptr) {
return (this.#cachedArrayBuffer = new ArrayBuffer(0));
}
return (this.#cachedArrayBuffer = toArrayBuffer(
this.ptr,
this.byteOffset,
this.byteLength
));
}
}

View File

@@ -3,8 +3,18 @@
#include <iostream>
using namespace std;
#include "root.h"
#include "ZigGlobalObject.h"
#include "Path.h"
#include "DOMURL.h"
#include "headers-cpp.h"
#include "JavaScriptCore/CallFrame.h"
int main() {
time_t rawtime;
struct tm *timeinfo;
@@ -44,6 +54,7 @@ int main() {
cout << "pub const " << names[i] << " = " << sizes[i] << ";\n";
cout << "pub const " << names[i] << "_align = " << aligns[i] << ";\n";
}
cout << "pub const Bun_FFI_PointerOffsetToArgumentsList = << "
<< JSC::CallFrame::argumentOffset(0) << ";\n";
return 0;
}