src: port defineLazyProperties
to native code
This allows us to have getters not observable from JS side. PR-URL: https://github.com/nodejs/node/pull/57081 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
parent
7a6b12895e
commit
2086877e81
@ -12,12 +12,11 @@ const {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
exposeInterface,
|
exposeInterface,
|
||||||
lazyDOMExceptionClass,
|
|
||||||
exposeLazyInterfaces,
|
exposeLazyInterfaces,
|
||||||
exposeGetterAndSetter,
|
|
||||||
exposeNamespace,
|
exposeNamespace,
|
||||||
} = require('internal/util');
|
} = require('internal/util');
|
||||||
const config = internalBinding('config');
|
const config = internalBinding('config');
|
||||||
|
const { exposeLazyDOMExceptionProperty } = internalBinding('messaging');
|
||||||
|
|
||||||
// https://console.spec.whatwg.org/#console-namespace
|
// https://console.spec.whatwg.org/#console-namespace
|
||||||
exposeNamespace(globalThis, 'console',
|
exposeNamespace(globalThis, 'console',
|
||||||
@ -28,16 +27,7 @@ const { URL, URLSearchParams } = require('internal/url');
|
|||||||
exposeInterface(globalThis, 'URL', URL);
|
exposeInterface(globalThis, 'URL', URL);
|
||||||
// https://url.spec.whatwg.org/#urlsearchparams
|
// https://url.spec.whatwg.org/#urlsearchparams
|
||||||
exposeInterface(globalThis, 'URLSearchParams', URLSearchParams);
|
exposeInterface(globalThis, 'URLSearchParams', URLSearchParams);
|
||||||
exposeGetterAndSetter(globalThis,
|
exposeLazyDOMExceptionProperty(globalThis);
|
||||||
'DOMException',
|
|
||||||
() => {
|
|
||||||
const DOMException = lazyDOMExceptionClass();
|
|
||||||
exposeInterface(globalThis, 'DOMException', DOMException);
|
|
||||||
return DOMException;
|
|
||||||
},
|
|
||||||
(value) => {
|
|
||||||
exposeInterface(globalThis, 'DOMException', value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#interface-abortcontroller
|
// https://dom.spec.whatwg.org/#interface-abortcontroller
|
||||||
// Lazy ones.
|
// Lazy ones.
|
||||||
|
@ -55,6 +55,7 @@ const {
|
|||||||
const { signals } = internalBinding('constants').os;
|
const { signals } = internalBinding('constants').os;
|
||||||
const {
|
const {
|
||||||
guessHandleType: _guessHandleType,
|
guessHandleType: _guessHandleType,
|
||||||
|
defineLazyProperties,
|
||||||
privateSymbols: {
|
privateSymbols: {
|
||||||
arrow_message_private_symbol,
|
arrow_message_private_symbol,
|
||||||
decorated_private_symbol,
|
decorated_private_symbol,
|
||||||
@ -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) {
|
function defineReplaceableLazyAttribute(target, id, keys, writable = true, check) {
|
||||||
let mod;
|
let mod;
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
@ -1645,6 +1645,36 @@ static void BroadcastChannel(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ExposeLazyDOMExceptionPropertyGetter(
|
||||||
|
Local<v8::Name> name, const v8::PropertyCallbackInfo<Value>& info) {
|
||||||
|
auto context = info.GetIsolate()->GetCurrentContext();
|
||||||
|
Local<Function> domexception;
|
||||||
|
if (!GetDOMException(context).ToLocal(&domexception)) {
|
||||||
|
// V8 will have scheduled an error to be thrown.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info.GetReturnValue().Set(domexception);
|
||||||
|
}
|
||||||
|
static void ExposeLazyDOMExceptionProperty(
|
||||||
|
const FunctionCallbackInfo<Value>& args) {
|
||||||
|
CHECK_GE(args.Length(), 1);
|
||||||
|
CHECK(args[0]->IsObject());
|
||||||
|
|
||||||
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
auto target = args[0].As<Object>();
|
||||||
|
|
||||||
|
if (target
|
||||||
|
->SetLazyDataProperty(isolate->GetCurrentContext(),
|
||||||
|
FIXED_ONE_BYTE_STRING(isolate, "DOMException"),
|
||||||
|
ExposeLazyDOMExceptionPropertyGetter,
|
||||||
|
Local<Value>(),
|
||||||
|
v8::DontEnum)
|
||||||
|
.IsNothing()) {
|
||||||
|
// V8 will have scheduled an error to be thrown.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||||
Local<ObjectTemplate> target) {
|
Local<ObjectTemplate> target) {
|
||||||
Isolate* isolate = isolate_data->isolate();
|
Isolate* isolate = isolate_data->isolate();
|
||||||
@ -1669,6 +1699,10 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|||||||
isolate_data->message_port_constructor_string(),
|
isolate_data->message_port_constructor_string(),
|
||||||
GetMessagePortConstructorTemplate(isolate_data));
|
GetMessagePortConstructorTemplate(isolate_data));
|
||||||
|
|
||||||
|
SetMethod(isolate,
|
||||||
|
target,
|
||||||
|
"exposeLazyDOMExceptionProperty",
|
||||||
|
ExposeLazyDOMExceptionProperty);
|
||||||
// These are not methods on the MessagePort prototype, because
|
// These are not methods on the MessagePort prototype, because
|
||||||
// the browser equivalents do not provide them.
|
// the browser equivalents do not provide them.
|
||||||
SetMethod(isolate, target, "stopMessagePort", MessagePort::Stop);
|
SetMethod(isolate, target, "stopMessagePort", MessagePort::Stop);
|
||||||
@ -1714,6 +1748,8 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
|||||||
registry->Register(MessagePort::MoveToContext);
|
registry->Register(MessagePort::MoveToContext);
|
||||||
registry->Register(SetDeserializerCreateObjectFunction);
|
registry->Register(SetDeserializerCreateObjectFunction);
|
||||||
registry->Register(StructuredClone);
|
registry->Register(StructuredClone);
|
||||||
|
registry->Register(ExposeLazyDOMExceptionProperty);
|
||||||
|
registry->Register(ExposeLazyDOMExceptionPropertyGetter);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
@ -348,6 +348,69 @@ static void IsInsideNodeModules(const FunctionCallbackInfo<Value>& args) {
|
|||||||
args.GetReturnValue().Set(result);
|
args.GetReturnValue().Set(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void DefineLazyPropertiesGetter(
|
||||||
|
Local<v8::Name> name, const v8::PropertyCallbackInfo<Value>& info) {
|
||||||
|
Realm* realm = Realm::GetCurrent(info);
|
||||||
|
Isolate* isolate = realm->isolate();
|
||||||
|
auto context = isolate->GetCurrentContext();
|
||||||
|
Local<Value> arg = info.Data();
|
||||||
|
Local<Value> require_result;
|
||||||
|
if (!realm->builtin_module_require()
|
||||||
|
->Call(context, Null(isolate), 1, &arg)
|
||||||
|
.ToLocal(&require_result)) {
|
||||||
|
// V8 will have scheduled an error to be thrown.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Local<Value> ret;
|
||||||
|
if (!require_result.As<v8::Object>()->Get(context, name).ToLocal(&ret)) {
|
||||||
|
// V8 will have scheduled an error to be thrown.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info.GetReturnValue().Set(ret);
|
||||||
|
}
|
||||||
|
static void DefineLazyProperties(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
// target: object, id: string, keys: string[][, enumerable = true]
|
||||||
|
CHECK_GE(args.Length(), 3);
|
||||||
|
// target: Object where to define the lazy properties.
|
||||||
|
CHECK(args[0]->IsObject());
|
||||||
|
// id: Internal module to lazy-load where the API to expose are implemented.
|
||||||
|
CHECK(args[1]->IsString());
|
||||||
|
// keys: Keys to map from `require(id)` and `target`.
|
||||||
|
CHECK(args[2]->IsArray());
|
||||||
|
// enumerable: Whether the property should be enumerable.
|
||||||
|
CHECK(args.Length() == 3 || args[3]->IsBoolean());
|
||||||
|
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
Isolate* isolate = env->isolate();
|
||||||
|
auto context = isolate->GetCurrentContext();
|
||||||
|
|
||||||
|
auto target = args[0].As<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());
|
||||||
|
if (target
|
||||||
|
->SetLazyDataProperty(context,
|
||||||
|
key.As<String>(),
|
||||||
|
DefineLazyPropertiesGetter,
|
||||||
|
id,
|
||||||
|
attribute)
|
||||||
|
.IsNothing()) {
|
||||||
|
// V8 will have scheduled an error to be thrown.
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||||
registry->Register(GetPromiseDetails);
|
registry->Register(GetPromiseDetails);
|
||||||
registry->Register(GetProxyDetails);
|
registry->Register(GetProxyDetails);
|
||||||
@ -364,6 +427,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
|||||||
registry->Register(fast_guess_handle_type_.GetTypeInfo());
|
registry->Register(fast_guess_handle_type_.GetTypeInfo());
|
||||||
registry->Register(ParseEnv);
|
registry->Register(ParseEnv);
|
||||||
registry->Register(IsInsideNodeModules);
|
registry->Register(IsInsideNodeModules);
|
||||||
|
registry->Register(DefineLazyProperties);
|
||||||
|
registry->Register(DefineLazyPropertiesGetter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize(Local<Object> target,
|
void Initialize(Local<Object> target,
|
||||||
@ -448,6 +513,7 @@ void Initialize(Local<Object> target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetMethod(context, target, "isInsideNodeModules", IsInsideNodeModules);
|
SetMethod(context, target, "isInsideNodeModules", IsInsideNodeModules);
|
||||||
|
SetMethod(context, target, "defineLazyProperties", DefineLazyProperties);
|
||||||
SetMethodNoSideEffect(
|
SetMethodNoSideEffect(
|
||||||
context, target, "getPromiseDetails", GetPromiseDetails);
|
context, target, "getPromiseDetails", GetPromiseDetails);
|
||||||
SetMethodNoSideEffect(context, target, "getProxyDetails", GetProxyDetails);
|
SetMethodNoSideEffect(context, target, "getProxyDetails", GetProxyDetails);
|
||||||
|
@ -595,7 +595,6 @@ class WPTRunner {
|
|||||||
switch (name) {
|
switch (name) {
|
||||||
case 'Window': {
|
case 'Window': {
|
||||||
this.globalThisInitScripts.push('globalThis.Window = Object.getPrototypeOf(globalThis).constructor;');
|
this.globalThisInitScripts.push('globalThis.Window = Object.getPrototypeOf(globalThis).constructor;');
|
||||||
this.loadLazyGlobals();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
// TODO(joyeecheung): work with the upstream to port more tests in .html
|
||||||
// to .js.
|
// to .js.
|
||||||
async runJsTests() {
|
async runJsTests() {
|
||||||
|
@ -4,6 +4,4 @@ const { WPTRunner } = require('../common/wpt');
|
|||||||
|
|
||||||
const runner = new WPTRunner('webidl/ecmascript-binding/es-exceptions');
|
const runner = new WPTRunner('webidl/ecmascript-binding/es-exceptions');
|
||||||
|
|
||||||
runner.loadLazyGlobals();
|
|
||||||
|
|
||||||
runner.runJsTests();
|
runner.runJsTests();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user