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 {
|
||||
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',
|
||||
@ -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.
|
||||
|
@ -55,6 +55,7 @@ const {
|
||||
const { signals } = internalBinding('constants').os;
|
||||
const {
|
||||
guessHandleType: _guessHandleType,
|
||||
defineLazyProperties,
|
||||
privateSymbols: {
|
||||
arrow_message_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) {
|
||||
let mod;
|
||||
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,
|
||||
Local<ObjectTemplate> target) {
|
||||
Isolate* isolate = isolate_data->isolate();
|
||||
@ -1669,6 +1699,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);
|
||||
@ -1714,6 +1748,8 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
registry->Register(MessagePort::MoveToContext);
|
||||
registry->Register(SetDeserializerCreateObjectFunction);
|
||||
registry->Register(StructuredClone);
|
||||
registry->Register(ExposeLazyDOMExceptionProperty);
|
||||
registry->Register(ExposeLazyDOMExceptionPropertyGetter);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -348,6 +348,69 @@ static void IsInsideNodeModules(const FunctionCallbackInfo<Value>& args) {
|
||||
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) {
|
||||
registry->Register(GetPromiseDetails);
|
||||
registry->Register(GetProxyDetails);
|
||||
@ -364,6 +427,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
registry->Register(fast_guess_handle_type_.GetTypeInfo());
|
||||
registry->Register(ParseEnv);
|
||||
registry->Register(IsInsideNodeModules);
|
||||
registry->Register(DefineLazyProperties);
|
||||
registry->Register(DefineLazyPropertiesGetter);
|
||||
}
|
||||
|
||||
void Initialize(Local<Object> target,
|
||||
@ -448,6 +513,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);
|
||||
|
@ -595,7 +595,6 @@ class WPTRunner {
|
||||
switch (name) {
|
||||
case 'Window': {
|
||||
this.globalThisInitScripts.push('globalThis.Window = Object.getPrototypeOf(globalThis).constructor;');
|
||||
this.loadLazyGlobals();
|
||||
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
|
||||
// to .js.
|
||||
async runJsTests() {
|
||||
|
@ -4,6 +4,4 @@ const { WPTRunner } = require('../common/wpt');
|
||||
|
||||
const runner = new WPTRunner('webidl/ecmascript-binding/es-exceptions');
|
||||
|
||||
runner.loadLazyGlobals();
|
||||
|
||||
runner.runJsTests();
|
||||
|
Loading…
x
Reference in New Issue
Block a user