Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: port defineLazyProperties to native code #57081

Merged
merged 12 commits into from
Feb 18, 2025
14 changes: 2 additions & 12 deletions lib/internal/bootstrap/web/exposed-wildcard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ const {

const {
exposeInterface,
lazyDOMExceptionClass,
exposeLazyInterfaces,
exposeGetterAndSetter,
exposeNamespace,
} = require('internal/util');
const config = internalBinding('config');
const { exposeLazyDOMExceptionProperty } = internalBinding('messaging');

// https://console.spec.whatwg.org/#console-namespace
exposeNamespace(globalThis, 'console',
Expand All @@ -28,16 +27,7 @@ const { URL, URLSearchParams } = require('internal/url');
exposeInterface(globalThis, 'URL', URL);
// https://url.spec.whatwg.org/#urlsearchparams
exposeInterface(globalThis, 'URLSearchParams', URLSearchParams);
exposeGetterAndSetter(globalThis,
'DOMException',
() => {
const DOMException = lazyDOMExceptionClass();
exposeInterface(globalThis, 'DOMException', DOMException);
return DOMException;
},
(value) => {
exposeInterface(globalThis, 'DOMException', value);
});
exposeLazyDOMExceptionProperty(globalThis);

// https://dom.spec.whatwg.org/#interface-abortcontroller
// Lazy ones.
Expand Down
41 changes: 1 addition & 40 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const {
const { signals } = internalBinding('constants').os;
const {
guessHandleType: _guessHandleType,
defineLazyProperties,
privateSymbols: {
arrow_message_private_symbol,
decorated_private_symbol,
Expand Down Expand Up @@ -610,46 +611,6 @@ function exposeGetterAndSetter(target, name, getter, setter = undefined) {
});
}

function defineLazyProperties(target, id, keys, enumerable = true) {
const descriptors = { __proto__: null };
let mod;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
let lazyLoadedValue;
function set(value) {
ObjectDefineProperty(target, key, {
__proto__: null,
writable: true,
value,
});
}
ObjectDefineProperty(set, 'name', {
__proto__: null,
value: `set ${key}`,
});
function get() {
mod ??= require(id);
if (lazyLoadedValue === undefined) {
lazyLoadedValue = mod[key];
set(lazyLoadedValue);
}
return lazyLoadedValue;
}
ObjectDefineProperty(get, 'name', {
__proto__: null,
value: `get ${key}`,
});
descriptors[key] = {
__proto__: null,
configurable: true,
enumerable,
get,
set,
};
}
ObjectDefineProperties(target, descriptors);
}

function defineReplaceableLazyAttribute(target, id, keys, writable = true, check) {
let mod;
for (let i = 0; i < keys.length; i++) {
Expand Down
29 changes: 29 additions & 0 deletions src/node_messaging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,31 @@ static void BroadcastChannel(const FunctionCallbackInfo<Value>& args) {
}
}

static void ExposeLazyDOMExceptionPropertyGetter(
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
auto context = info.GetIsolate()->GetCurrentContext();
Local<Function> domexception = GetDOMException(context).ToLocalChecked();
info.GetReturnValue().Set(domexception);
}
static void ExposeLazyDOMExceptionProperty(
const FunctionCallbackInfo<Value>& args) {
CHECK_GE(args.Length(), 1); // target
CHECK(
args[0]
->IsObject()); // target: Object where to define the lazy properties.

Isolate* isolate = args.GetIsolate();
auto target = args[0].As<v8::Object>();

target
->SetLazyDataProperty(isolate->GetCurrentContext(),
FIXED_ONE_BYTE_STRING(isolate, "DOMException"),
ExposeLazyDOMExceptionPropertyGetter,
Null(isolate),
v8::DontEnum)
.Check();
}

static void CreatePerIsolateProperties(IsolateData* isolate_data,
Local<ObjectTemplate> target) {
Isolate* isolate = isolate_data->isolate();
Expand All @@ -1669,6 +1694,10 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
isolate_data->message_port_constructor_string(),
GetMessagePortConstructorTemplate(isolate_data));

SetMethod(isolate,
target,
"exposeLazyDOMExceptionProperty",
ExposeLazyDOMExceptionProperty);
// These are not methods on the MessagePort prototype, because
// the browser equivalents do not provide them.
SetMethod(isolate, target, "stopMessagePort", MessagePort::Stop);
Expand Down
56 changes: 56 additions & 0 deletions src/node_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,61 @@ static void IsInsideNodeModules(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(result);
}

static void DefineLazyPropertiesGetter(
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
Realm* realm = Realm::GetCurrent(info);
Isolate* isolate = realm->isolate();
auto context = isolate->GetCurrentContext();
Local<Value> arg = info.Data();
auto require_result = realm->builtin_module_require()
->Call(context, Null(isolate), 1, &arg)
.ToLocalChecked();
info.GetReturnValue().Set(
require_result.As<v8::Object>()->Get(context, name).ToLocalChecked());
}
static void DefineLazyProperties(const FunctionCallbackInfo<Value>& args) {
CHECK_GE(
args.Length(),
3); // target: object, id: string, keys: string[][, enumerable = true]
CHECK(
args[0]
->IsObject()); // target: Object where to define the lazy properties.
CHECK(args[1]->IsString()); // id: Internal module to lazy-load where the API
// to expose are implemented.
CHECK(args[2]
->IsArray()); // keys: Keys to map from `require(id)` and `target`.
CHECK(args.Length() == 3 ||
args[3]->IsBoolean()); // enumerable: Whether the property should be
// enumerable.

Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
auto context = isolate->GetCurrentContext();

auto target = args[0].As<v8::Object>();
Local<Value> id = args[1];
v8::PropertyAttribute attribute =
args.Length() == 3 || args[3]->IsTrue() ? v8::None : v8::DontEnum;

const Local<Array> keys = args[2].As<Array>();
size_t length = keys->Length();
for (size_t i = 0; i < length; i++) {
Local<Value> key;
if (!keys->Get(context, i).ToLocal(&key)) {
// V8 will have scheduled an error to be thrown.
return;
}
CHECK(key->IsString());
target
->SetLazyDataProperty(context,
key.As<v8::String>(),
DefineLazyPropertiesGetter,
id,
attribute)
.Check();
}
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GetPromiseDetails);
registry->Register(GetProxyDetails);
Expand Down Expand Up @@ -448,6 +503,7 @@ void Initialize(Local<Object> target,
}

SetMethod(context, target, "isInsideNodeModules", IsInsideNodeModules);
SetMethod(context, target, "defineLazyProperties", DefineLazyProperties);
SetMethodNoSideEffect(
context, target, "getPromiseDetails", GetPromiseDetails);
SetMethodNoSideEffect(context, target, "getProxyDetails", GetProxyDetails);
Expand Down
27 changes: 0 additions & 27 deletions test/common/wpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,6 @@ class WPTRunner {
switch (name) {
case 'Window': {
this.globalThisInitScripts.push('globalThis.Window = Object.getPrototypeOf(globalThis).constructor;');
this.loadLazyGlobals();
break;
}

Expand All @@ -609,32 +608,6 @@ class WPTRunner {
}
}

loadLazyGlobals() {
const lazyProperties = [
'DOMException',
'Performance', 'PerformanceEntry', 'PerformanceMark', 'PerformanceMeasure',
'PerformanceObserver', 'PerformanceObserverEntryList', 'PerformanceResourceTiming',
'Blob', 'atob', 'btoa',
'MessageChannel', 'MessagePort', 'MessageEvent',
'EventTarget', 'Event',
'AbortController', 'AbortSignal',
'performance',
'TransformStream', 'TransformStreamDefaultController',
'WritableStream', 'WritableStreamDefaultController', 'WritableStreamDefaultWriter',
'ReadableStream', 'ReadableStreamDefaultReader',
'ReadableStreamBYOBReader', 'ReadableStreamBYOBRequest',
'ReadableByteStreamController', 'ReadableStreamDefaultController',
'ByteLengthQueuingStrategy', 'CountQueuingStrategy',
'TextEncoder', 'TextDecoder', 'TextEncoderStream', 'TextDecoderStream',
'CompressionStream', 'DecompressionStream',
];
if (Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO) {
lazyProperties.push('crypto', 'Crypto', 'CryptoKey', 'SubtleCrypto');
}
const script = lazyProperties.map((name) => `globalThis.${name};`).join('\n');
this.globalThisInitScripts.push(script);
}

// TODO(joyeecheung): work with the upstream to port more tests in .html
// to .js.
async runJsTests() {
Expand Down
2 changes: 0 additions & 2 deletions test/wpt/test-domexception.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ const { WPTRunner } = require('../common/wpt');

const runner = new WPTRunner('webidl/ecmascript-binding/es-exceptions');

runner.loadLazyGlobals();

runner.runJsTests();
Loading