Skip to content

Commit

Permalink
Start implementing internal V8 APIs (#12821)
Browse files Browse the repository at this point in the history
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
  • Loading branch information
190n and Jarred-Sumner authored Aug 15, 2024
1 parent 5bc45e2 commit dc2929d
Show file tree
Hide file tree
Showing 86 changed files with 3,636 additions and 106 deletions.
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ cmake_path(APPEND LOCAL_ZIG_CACHE_DIR "local")
cmake_path(APPEND GLOBAL_ZIG_CACHE_DIR "global")

# Used in process.version, process.versions.node, napi, and elsewhere
set(REPORTED_NODEJS_VERSION "22.3.0")
set(REPORTED_NODEJS_VERSION "22.6.0")
# Used in process.versions.modules and compared while loading V8 modules
set(REPORTED_NODEJS_ABI_VERSION "127")

# WebKit uses -std=gnu++20 on non-macOS non-Windows
# If we do not set this, it will crash at startup on the first memory allocation.
Expand Down Expand Up @@ -664,6 +666,7 @@ file(GLOB BUN_CPP ${CONFIGURE_DEPENDS}
"${BUN_SRC}/bun.js/bindings/sqlite/*.cpp"
"${BUN_SRC}/bun.js/bindings/webcrypto/*.cpp"
"${BUN_SRC}/bun.js/bindings/webcrypto/*/*.cpp"
"${BUN_SRC}/bun.js/bindings/v8/*.cpp"
"${BUN_SRC}/deps/picohttpparser/picohttpparser.c"
)
list(APPEND BUN_RAW_SOURCES ${BUN_CPP})
Expand Down Expand Up @@ -1040,6 +1043,7 @@ add_compile_definitions(
"BUILDING_JSCONLY__"
"BUN_DYNAMIC_JS_LOAD_PATH=\"${BUN_WORKDIR}/js\""
"REPORTED_NODEJS_VERSION=\"${REPORTED_NODEJS_VERSION}\""
"REPORTED_NODEJS_ABI_VERSION=${REPORTED_NODEJS_ABI_VERSION}"
)

if(NOT ASSERT_ENABLED)
Expand Down
21 changes: 17 additions & 4 deletions src/bun.js/bindings/BunProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,12 @@ static JSValue constructVersions(VM& vm, JSObject* processObject)
object->putDirect(vm, JSC::Identifier::fromString(vm, "icu"_s), JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(U_ICU_VERSION)))), 0);
object->putDirect(vm, JSC::Identifier::fromString(vm, "unicode"_s), JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(U_UNICODE_VERSION)))), 0);

#define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x)
object->putDirect(vm, JSC::Identifier::fromString(vm, "modules"_s),
JSC::JSValue(JSC::jsString(vm, makeString("115"_s))));
JSC::JSValue(JSC::jsString(vm, makeString(ASCIILiteral::fromLiteralUnsafe(STRINGIFY(REPORTED_NODEJS_ABI_VERSION))))));
#undef STRINGIFY
#undef STRINGIFY_IMPL

return object;
}
Expand Down Expand Up @@ -288,7 +292,9 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
return JSC::JSValue::encode(JSC::JSValue {});
}

globalObject->pendingNapiModule = exports;
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, moduleObject);
globalObject->m_pendingNapiModuleAndExports[1].set(vm, globalObject, exports);

Strong<JSC::Unknown> strongExports;

if (exports.isCell()) {
Expand Down Expand Up @@ -352,9 +358,10 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
}

if (callCountAtStart != globalObject->napiModuleRegisterCallCount) {
JSValue resultValue = globalObject->pendingNapiModule;
globalObject->pendingNapiModule = JSValue {};
JSValue resultValue = globalObject->m_pendingNapiModuleAndExports[0].get();
globalObject->napiModuleRegisterCallCount = 0;
globalObject->m_pendingNapiModuleAndExports[0].clear();
globalObject->m_pendingNapiModuleAndExports[1].clear();

RETURN_IF_EXCEPTION(scope, {});

Expand All @@ -374,6 +381,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
#define dlsym GetProcAddress
#endif

// TODO(@190n) look for node_register_module_vXYZ according to BuildOptions.reported_nodejs_version
// (bun/src/env.zig:36) and the table at https://github.com/nodejs/node/blob/main/doc/abi_version_registry.json
napi_register_module_v1 = reinterpret_cast<JSC::EncodedJSValue (*)(JSC::JSGlobalObject*,
JSC::EncodedJSValue)>(
dlsym(handle, "napi_register_module_v1"));
Expand All @@ -397,6 +406,9 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,

RETURN_IF_EXCEPTION(scope, {});

globalObject->m_pendingNapiModuleAndExports[0].clear();
globalObject->m_pendingNapiModuleAndExports[1].clear();

// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/node_api.cc#L734-L742
// https://github.com/oven-sh/bun/issues/1288
if (!resultValue.isEmpty() && !scope.exception() && (!strongExports || resultValue != strongExports.get())) {
Expand Down Expand Up @@ -1788,6 +1800,7 @@ static JSValue constructProcessConfigObject(VM& vm, JSObject* processObject)
JSC::JSObject* variables = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 1);
variables->putDirect(vm, JSC::Identifier::fromString(vm, "v8_enable_i8n_support"_s),
JSC::jsNumber(1), 0);
variables->putDirect(vm, JSC::Identifier::fromString(vm, "enable_lto"_s), JSC::jsBoolean(false), 0);
config->putDirect(vm, JSC::Identifier::fromString(vm, "target_defaults"_s), JSC::constructEmptyObject(globalObject), 0);
config->putDirect(vm, JSC::Identifier::fromString(vm, "variables"_s), variables, 0);

Expand Down
14 changes: 14 additions & 0 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
#include "Base64Helpers.h"
#include "wtf/text/OrdinalNumber.h"
#include "ErrorCode.h"
#include "v8/V8GlobalInternals.h"

#if ENABLE(REMOTE_INSPECTOR)
#include "JavaScriptCore/RemoteInspectorServer.h"
Expand Down Expand Up @@ -2685,6 +2686,15 @@ void GlobalObject::finishCreation(VM& vm)
init.set(WebCore::createJSSQLStatementStructure(init.owner));
});

m_V8GlobalInternals.initLater(
[](const JSC::LazyProperty<JSC::JSGlobalObject, v8::GlobalInternals>::Initializer& init) {
init.set(
v8::GlobalInternals::create(
init.vm,
v8::GlobalInternals::createStructure(init.vm, init.owner),
jsDynamicCast<Zig::GlobalObject*>(init.owner)));
});

m_memoryFootprintStructure.initLater(
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
init.set(
Expand Down Expand Up @@ -3530,6 +3540,9 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.append(thisObject->m_nextTickQueue);
visitor.append(thisObject->m_errorConstructorPrepareStackTraceValue);

visitor.append(thisObject->m_pendingNapiModuleAndExports[0]);
visitor.append(thisObject->m_pendingNapiModuleAndExports[1]);

thisObject->m_asyncBoundFunctionStructure.visit(visitor);
thisObject->m_bunObject.visit(visitor);
thisObject->m_cachedNodeVMGlobalObjectStructure.visit(visitor);
Expand Down Expand Up @@ -3557,6 +3570,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_JSHTTPSResponseSinkClassStructure.visit(visitor);
thisObject->m_JSSocketAddressStructure.visit(visitor);
thisObject->m_JSSQLStatementStructure.visit(visitor);
thisObject->m_V8GlobalInternals.visit(visitor);
thisObject->m_JSStringDecoderClassStructure.visit(visitor);
thisObject->m_lazyPreloadTestModuleObject.visit(visitor);
thisObject->m_lazyReadableStreamPrototypeMap.visit(visitor);
Expand Down
13 changes: 10 additions & 3 deletions src/bun.js/bindings/ZigGlobalObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ namespace Bun {
class InternalModuleRegistry;
} // namespace Bun

namespace v8 {
class GlobalInternals;
} // namespace v8

#include "root.h"
#include "headers-handwritten.h"
#include <JavaScriptCore/CatchScope.h>
Expand Down Expand Up @@ -282,6 +286,8 @@ class GlobalObject : public Bun::GlobalScope {

Structure* JSSQLStatementStructure() const { return m_JSSQLStatementStructure.getInitializedOnMainThread(this); }

v8::GlobalInternals* V8GlobalInternals() const { return m_V8GlobalInternals.getInitializedOnMainThread(this); }

bool hasProcessObject() const { return m_processObject.isInitialized(); }

RefPtr<WebCore::Performance> performance();
Expand Down Expand Up @@ -396,6 +402,9 @@ class GlobalObject : public Bun::GlobalScope {
// Error.prepareStackTrace
mutable WriteBarrier<JSC::Unknown> m_errorConstructorPrepareStackTraceValue;

// When a napi module initializes on dlopen, we need to know what the value is
mutable JSC::WriteBarrier<Unknown> m_pendingNapiModuleAndExports[2];

// The original, unmodified Error.prepareStackTrace.
//
// We set a default value for this to mimick Node.js behavior It is a
Expand Down Expand Up @@ -447,9 +456,6 @@ class GlobalObject : public Bun::GlobalScope {

JSC::Structure* pendingVirtualModuleResultStructure() { return m_pendingVirtualModuleResultStructure.get(this); }

// When a napi module initializes on dlopen, we need to know what the value is
// This value is not observed by GC. It should be extremely ephemeral.
JSValue pendingNapiModule = JSValue {};
// We need to know if the napi module registered itself or we registered it.
// To do that, we count the number of times we register a module.
int napiModuleRegisterCallCount = 0;
Expand Down Expand Up @@ -559,6 +565,7 @@ class GlobalObject : public Bun::GlobalScope {
LazyProperty<JSGlobalObject, Structure> m_NapiPrototypeStructure;
LazyProperty<JSGlobalObject, Structure> m_NAPIFunctionStructure;
LazyProperty<JSGlobalObject, Structure> m_JSSQLStatementStructure;
LazyProperty<JSGlobalObject, v8::GlobalInternals> m_V8GlobalInternals;

LazyProperty<JSGlobalObject, JSObject> m_bunObject;
LazyProperty<JSGlobalObject, JSObject> m_cryptoObject;
Expand Down
15 changes: 6 additions & 9 deletions src/bun.js/bindings/napi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "JavaScriptCore/JSGlobalObject.h"
#include "JavaScriptCore/SourceCode.h"
#include "js_native_api_types.h"
#include "v8/V8HandleScope.h"

#include "helpers.h"
#include <JavaScriptCore/JSObjectInlines.h>
Expand Down Expand Up @@ -341,6 +342,7 @@ class NAPIFunction : public JSC::JSFunction {
NAPICallFrame frame(JSC::ArgList(args), function->m_dataPtr);

auto scope = DECLARE_THROW_SCOPE(vm);
v8::HandleScope handleScope(v8::Isolate::fromGlobalObject(static_cast<Zig::GlobalObject*>(globalObject)));

auto result = callback(env, NAPICallFrame::toNapiCallbackInfo(frame));

Expand Down Expand Up @@ -879,7 +881,7 @@ extern "C" void napi_module_register(napi_module* mod)
JSC::VM& vm = globalObject->vm();
auto keyStr = WTF::String::fromUTF8(mod->nm_modname);
globalObject->napiModuleRegisterCallCount++;
JSValue pendingNapiModule = globalObject->pendingNapiModule;
JSValue pendingNapiModule = globalObject->m_pendingNapiModuleAndExports[0].get();
JSObject* object = (pendingNapiModule && pendingNapiModule.isObject()) ? pendingNapiModule.getObject()
: nullptr;

Expand All @@ -893,7 +895,6 @@ extern "C" void napi_module_register(napi_module* mod)
object = Bun::JSCommonJSModule::create(globalObject, keyStr, exportsObject, false, jsUndefined());
strongExportsObject = { vm, exportsObject };
} else {
globalObject->pendingNapiModule = JSC::JSValue();
JSValue exportsObject = object->getIfPropertyExists(globalObject, WebCore::builtinNames(vm).exportsPublicName());
RETURN_IF_EXCEPTION(scope, void());

Expand All @@ -910,17 +911,13 @@ extern "C" void napi_module_register(napi_module* mod)

if (resultValue.isEmpty()) {
JSValue errorInstance = createError(globalObject, makeString("Node-API module \""_s, keyStr, "\" returned an error"_s));
globalObject->pendingNapiModule = errorInstance;
vm.writeBarrier(globalObject, errorInstance);
EnsureStillAliveScope ensureAlive(globalObject->pendingNapiModule);
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance);
return;
}

if (!resultValue.isObject()) {
JSValue errorInstance = createError(globalObject, makeString("Expected Node-API module \""_s, keyStr, "\" to return an exports object"_s));
globalObject->pendingNapiModule = errorInstance;
vm.writeBarrier(globalObject, errorInstance);
EnsureStillAliveScope ensureAlive(globalObject->pendingNapiModule);
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance);
return;
}

Expand All @@ -931,7 +928,7 @@ extern "C" void napi_module_register(napi_module* mod)
strongObject->put(strongObject.get(), globalObject, WebCore::builtinNames(vm).exportsPublicName(), resultValue, slot);
}

globalObject->pendingNapiModule = object;
globalObject->m_pendingNapiModuleAndExports[1].set(vm, globalObject, object);
}

extern "C" napi_status napi_wrap(napi_env env,
Expand Down
83 changes: 0 additions & 83 deletions src/bun.js/bindings/v8.cpp

This file was deleted.

23 changes: 23 additions & 0 deletions src/bun.js/bindings/v8/V8Array.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "V8Array.h"

#include "V8HandleScope.h"

using JSC::ArrayAllocationProfile;
using JSC::JSArray;
using JSC::JSValue;

namespace v8 {

Local<Array> Array::New(Isolate* isolate, Local<Value>* elements, size_t length)
{
V8_UNIMPLEMENTED();
// TODO fix for v8 layout
Zig::GlobalObject* globalObject = isolate->globalObject();
JSArray* array = JSC::constructArray(globalObject,
static_cast<ArrayAllocationProfile*>(nullptr),
reinterpret_cast<JSValue*>(elements),
(unsigned int)length);
return isolate->currentHandleScope()->createLocal<Array>(array);
}

}
16 changes: 16 additions & 0 deletions src/bun.js/bindings/v8/V8Array.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "v8.h"
#include "V8Object.h"
#include "V8Local.h"
#include "V8Isolate.h"
#include "V8Value.h"

namespace v8 {

class Array : public Object {
public:
BUN_EXPORT static Local<Array> New(Isolate* isolate, Local<Value>* elements, size_t length);
};

}
16 changes: 16 additions & 0 deletions src/bun.js/bindings/v8/V8Boolean.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "V8Boolean.h"
#include "V8HandleScope.h"

namespace v8 {

bool Boolean::Value() const
{
return localToJSValue(Isolate::GetCurrent()->globalInternals()).asBoolean();
}

Local<Boolean> Boolean::New(Isolate* isolate, bool value)
{
return isolate->currentHandleScope()->createLocal<Boolean>(JSC::jsBoolean(value));
}

}
Loading

0 comments on commit dc2929d

Please sign in to comment.