| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b6bced8 commit c3a41d8
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,46 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common.js'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + | ||
| 6 | + const bench = common.createBenchmark(main, { | ||
| 7 | + type: ['string', 'object', 'arraybuffer'], | ||
| 8 | + n: [1e4], | ||
| 9 | + }); | ||
| 10 | + | ||
| 11 | + function main({ n, type }) { | ||
| 12 | + const data = []; | ||
| 13 | + | ||
| 14 | + switch (type) { | ||
| 15 | + case 'string': | ||
| 16 | + for (let i = 0; i < n; ++i) { | ||
| 17 | + data.push(new Date().toISOString()); | ||
| 18 | + } | ||
| 19 | + break; | ||
| 20 | + case 'object': | ||
| 21 | + for (let i = 0; i < n; ++i) { | ||
| 22 | + data.push({ ...process.config }); | ||
| 23 | + } | ||
| 24 | + break; | ||
| 25 | + case 'arraybuffer': | ||
| 26 | + for (let i = 0; i < n; ++i) { | ||
| 27 | + data.push(new ArrayBuffer(10)); | ||
| 28 | + } | ||
| 29 | + break; | ||
| 30 | + default: | ||
| 31 | + throw new Error('Unsupported payload type'); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + const run = type === 'arraybuffer' ? (i) => { | ||
| 35 | + data[i] = structuredClone(data[i], { transfer: [ data[i] ] }); | ||
| 36 | + } : (i) => { | ||
| 37 | + data[i] = structuredClone(data[i]); | ||
| 38 | + }; | ||
| 39 | + | ||
| 40 | + bench.start(); | ||
| 41 | + for (let i = 0; i < n; ++i) { | ||
| 42 | + run(i); | ||
| 43 | + } | ||
| 44 | + bench.end(n); | ||
| 45 | + assert.strictEqual(data.length, n); | ||
| 46 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -172,7 +172,7 @@ rules: | |||
| 172 | 172 | - name: setTimeout | |
| 173 | 173 | message: Use `const { setTimeout } = require('timers');` instead of the global. | |
| 174 | 174 | - name: structuredClone | |
| 175 | - message: Use `const { structuredClone } = require('internal/structured_clone');` instead of the global. | ||
| 175 | + message: Use `const { structuredClone } = internalBinding('messaging');` instead of the global. | ||
| 176 | 176 | - name: SubtleCrypto | |
| 177 | 177 | message: Use `const { SubtleCrypto } = require('internal/crypto/webcrypto');` instead of the global. | |
| 178 | 178 | no-restricted-modules: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,11 +31,8 @@ const { | |||
| 31 | 31 | } = require('internal/process/task_queues'); | |
| 32 | 32 | defineOperation(globalThis, 'queueMicrotask', queueMicrotask); | |
| 33 | 33 | ||
| 34 | - defineLazyProperties( | ||
| 35 | - globalThis, | ||
| 36 | - 'internal/structured_clone', | ||
| 37 | - ['structuredClone'], | ||
| 38 | - ); | ||
| 34 | + const { structuredClone } = internalBinding('messaging'); | ||
| 35 | + defineOperation(globalThis, 'structuredClone', structuredClone); | ||
| 39 | 36 | defineLazyProperties(globalThis, 'buffer', ['atob', 'btoa']); | |
| 40 | 37 | ||
| 41 | 38 | // https://html.spec.whatwg.org/multipage/web-messaging.html#broadcasting-to-other-browsing-contexts | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,7 +31,7 @@ const { | |||
| 31 | 31 | }, | |
| 32 | 32 | } = require('internal/errors'); | |
| 33 | 33 | ||
| 34 | - const { structuredClone } = require('internal/structured_clone'); | ||
| 34 | + const { structuredClone } = internalBinding('messaging'); | ||
| 35 | 35 | const { | |
| 36 | 36 | lazyDOMException, | |
| 37 | 37 | kEnumerableProperty, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -85,9 +85,7 @@ const { | |||
| 85 | 85 | kControllerErrorFunction, | |
| 86 | 86 | } = require('internal/streams/utils'); | |
| 87 | 87 | ||
| 88 | - const { | ||
| 89 | - structuredClone, | ||
| 90 | - } = require('internal/structured_clone'); | ||
| 88 | + const { structuredClone } = internalBinding('messaging'); | ||
| 91 | 89 | ||
| 92 | 90 | const { | |
| 93 | 91 | ArrayBufferViewGetBuffer, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1008,6 +1008,47 @@ static Maybe<bool> ReadIterable(Environment* env, | |||
| 1008 | 1008 | return Just(true); | |
| 1009 | 1009 | } | |
| 1010 | 1010 | ||
| 1011 | + bool GetTransferList(Environment* env, | ||
| 1012 | + Local<Context> context, | ||
| 1013 | + Local<Value> transfer_list_v, | ||
| 1014 | + TransferList* transfer_list_out) { | ||
| 1015 | + if (transfer_list_v->IsNullOrUndefined()) { | ||
| 1016 | + // Browsers ignore null or undefined, and otherwise accept an array or an | ||
| 1017 | + // options object. | ||
| 1018 | + return true; | ||
| 1019 | + } | ||
| 1020 | + | ||
| 1021 | + if (!transfer_list_v->IsObject()) { | ||
| 1022 | + THROW_ERR_INVALID_ARG_TYPE( | ||
| 1023 | + env, "Optional transferList argument must be an iterable"); | ||
| 1024 | + return false; | ||
| 1025 | + } | ||
| 1026 | + | ||
| 1027 | + bool was_iterable; | ||
| 1028 | + if (!ReadIterable(env, context, *transfer_list_out, transfer_list_v) | ||
| 1029 | + .To(&was_iterable)) | ||
| 1030 | + return false; | ||
| 1031 | + if (!was_iterable) { | ||
| 1032 | + Local<Value> transfer_option; | ||
| 1033 | + if (!transfer_list_v.As<Object>() | ||
| 1034 | + ->Get(context, env->transfer_string()) | ||
| 1035 | + .ToLocal(&transfer_option)) | ||
| 1036 | + return false; | ||
| 1037 | + if (!transfer_option->IsUndefined()) { | ||
| 1038 | + if (!ReadIterable(env, context, *transfer_list_out, transfer_option) | ||
| 1039 | + .To(&was_iterable)) | ||
| 1040 | + return false; | ||
| 1041 | + if (!was_iterable) { | ||
| 1042 | + THROW_ERR_INVALID_ARG_TYPE( | ||
| 1043 | + env, "Optional options.transfer argument must be an iterable"); | ||
| 1044 | + return false; | ||
| 1045 | + } | ||
| 1046 | + } | ||
| 1047 | + } | ||
| 1048 | + | ||
| 1049 | + return true; | ||
| 1050 | + } | ||
| 1051 | + | ||
| 1011 | 1052 | void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) { | |
| 1012 | 1053 | Environment* env = Environment::GetCurrent(args); | |
| 1013 | 1054 | Local<Object> obj = args.This(); | |
@@ -1018,33 +1059,10 @@ void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) { | |||
| 1018 | 1059 | "MessagePort.postMessage"); | |
| 1019 | 1060 | } | |
| 1020 | 1061 | ||
| 1021 | - if (!args[1]->IsNullOrUndefined() && !args[1]->IsObject()) { | ||
| 1022 | - // Browsers ignore null or undefined, and otherwise accept an array or an | ||
| 1023 | - // options object. | ||
| 1024 | - return THROW_ERR_INVALID_ARG_TYPE(env, | ||
| 1025 | - "Optional transferList argument must be an iterable"); | ||
| 1026 | - } | ||
| 1027 | - | ||
| 1028 | 1062 | TransferList transfer_list; | |
| 1029 | - if (args[1]->IsObject()) { | ||
| 1030 | - bool was_iterable; | ||
| 1031 | - if (!ReadIterable(env, context, transfer_list, args[1]).To(&was_iterable)) | ||
| 1032 | - return; | ||
| 1033 | - if (!was_iterable) { | ||
| 1034 | - Local<Value> transfer_option; | ||
| 1035 | - if (!args[1].As<Object>()->Get(context, env->transfer_string()) | ||
| 1036 | - .ToLocal(&transfer_option)) return; | ||
| 1037 | - if (!transfer_option->IsUndefined()) { | ||
| 1038 | - if (!ReadIterable(env, context, transfer_list, transfer_option) | ||
| 1039 | - .To(&was_iterable)) return; | ||
| 1040 | - if (!was_iterable) { | ||
| 1041 | - return THROW_ERR_INVALID_ARG_TYPE(env, | ||
| 1042 | - "Optional options.transfer argument must be an iterable"); | ||
| 1043 | - } | ||
| 1044 | - } | ||
| 1045 | - } | ||
| 1063 | + if (!GetTransferList(env, context, args[1], &transfer_list)) { | ||
| 1064 | + return; | ||
| 1046 | 1065 | } | |
| 1047 | - | ||
| 1048 | 1066 | MessagePort* port = Unwrap<MessagePort>(args.This()); | |
| 1049 | 1067 | // Even if the backing MessagePort object has already been deleted, we still | |
| 1050 | 1068 | // want to serialize the message to ensure spec-compliant behavior w.r.t. | |
@@ -1535,6 +1553,48 @@ static void SetDeserializerCreateObjectFunction( | |||
| 1535 | 1553 | env->set_messaging_deserialize_create_object(args[0].As<Function>()); | |
| 1536 | 1554 | } | |
| 1537 | 1555 | ||
| 1556 | + static void StructuredClone(const FunctionCallbackInfo<Value>& args) { | ||
| 1557 | + Isolate* isolate = args.GetIsolate(); | ||
| 1558 | + Local<Context> context = isolate->GetCurrentContext(); | ||
| 1559 | + Realm* realm = Realm::GetCurrent(context); | ||
| 1560 | + Environment* env = realm->env(); | ||
| 1561 | + | ||
| 1562 | + if (args.Length() == 0) { | ||
| 1563 | + return THROW_ERR_MISSING_ARGS(env, "The value argument must be specified"); | ||
| 1564 | + } | ||
| 1565 | + | ||
| 1566 | + Local<Value> value = args[0]; | ||
| 1567 | + | ||
| 1568 | + TransferList transfer_list; | ||
| 1569 | + if (!args[1]->IsNullOrUndefined()) { | ||
| 1570 | + if (!args[1]->IsObject()) { | ||
| 1571 | + return THROW_ERR_INVALID_ARG_TYPE( | ||
| 1572 | + env, "The options argument must be either an object or undefined"); | ||
| 1573 | + } | ||
| 1574 | + Local<Object> options = args[1].As<Object>(); | ||
| 1575 | + Local<Value> transfer_list_v; | ||
| 1576 | + if (!options->Get(context, env->transfer_string()) | ||
| 1577 | + .ToLocal(&transfer_list_v)) { | ||
| 1578 | + return; | ||
| 1579 | + } | ||
| 1580 | + | ||
| 1581 | + // TODO(joyeecheung): implement this in JS land to avoid the C++ -> JS | ||
| 1582 | + // cost to convert a sequence into an array. | ||
| 1583 | + if (!GetTransferList(env, context, transfer_list_v, &transfer_list)) { | ||
| 1584 | + return; | ||
| 1585 | + } | ||
| 1586 | + } | ||
| 1587 | + | ||
| 1588 | + std::shared_ptr<Message> msg = std::make_shared<Message>(); | ||
| 1589 | + Local<Value> result; | ||
| 1590 | + if (msg->Serialize(env, context, value, transfer_list, Local<Object>()) | ||
| 1591 | + .IsNothing() || | ||
| 1592 | + !msg->Deserialize(env, context, nullptr).ToLocal(&result)) { | ||
| 1593 | + return; | ||
| 1594 | + } | ||
| 1595 | + args.GetReturnValue().Set(result); | ||
| 1596 | + } | ||
| 1597 | + | ||
| 1538 | 1598 | static void MessageChannel(const FunctionCallbackInfo<Value>& args) { | |
| 1539 | 1599 | Environment* env = Environment::GetCurrent(args); | |
| 1540 | 1600 | if (!args.IsConstructCall()) { | |
@@ -1615,6 +1675,7 @@ static void InitMessaging(Local<Object> target, | |||
| 1615 | 1675 | "setDeserializerCreateObjectFunction", | |
| 1616 | 1676 | SetDeserializerCreateObjectFunction); | |
| 1617 | 1677 | SetMethod(context, target, "broadcastChannel", BroadcastChannel); | |
| 1678 | + SetMethod(context, target, "structuredClone", StructuredClone); | ||
| 1618 | 1679 | ||
| 1619 | 1680 | { | |
| 1620 | 1681 | Local<Function> domexception = GetDOMException(context).ToLocalChecked(); | |
@@ -1638,6 +1699,7 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | |||
| 1638 | 1699 | registry->Register(MessagePort::ReceiveMessage); | |
| 1639 | 1700 | registry->Register(MessagePort::MoveToContext); | |
| 1640 | 1701 | registry->Register(SetDeserializerCreateObjectFunction); | |
| 1702 | + registry->Register(StructuredClone); | ||
| 1641 | 1703 | } | |
| 1642 | 1704 | ||
| 1643 | 1705 | } // anonymous namespace | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,23 +1,17 @@ | |||
| 1 | - // Flags: --expose-internals | ||
| 2 | 1 | 'use strict'; | |
| 3 | - /* eslint-disable no-global-assign */ | ||
| 4 | 2 | ||
| 5 | 3 | require('../common'); | |
| 4 | + const assert = require('assert'); | ||
| 6 | 5 | ||
| 7 | - const { | ||
| 8 | - structuredClone: _structuredClone, | ||
| 9 | - } = require('internal/structured_clone'); | ||
| 6 | + assert.throws(() => structuredClone(), { code: 'ERR_MISSING_ARGS' }); | ||
| 7 | + assert.throws(() => structuredClone(undefined, ''), { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 8 | + assert.throws(() => structuredClone(undefined, 1), { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 9 | + assert.throws(() => structuredClone(undefined, { transfer: 1 }), { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 10 | + assert.throws(() => structuredClone(undefined, { transfer: '' }), { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 10 | 11 | ||
| 11 | - const { | ||
| 12 | - strictEqual, | ||
| 13 | - throws, | ||
| 14 | - } = require('assert'); | ||
| 15 | - | ||
| 16 | - strictEqual(globalThis.structuredClone, _structuredClone); | ||
| 17 | - structuredClone = undefined; | ||
| 18 | - strictEqual(globalThis.structuredClone, undefined); | ||
| 19 | - | ||
| 20 | - // Restore the value for the known globals check. | ||
| 21 | - structuredClone = _structuredClone; | ||
| 22 | - | ||
| 23 | - throws(() => _structuredClone(), /ERR_MISSING_ARGS/); | ||
| 12 | + // Options can be null or undefined. | ||
| 13 | + assert.strictEqual(structuredClone(undefined), undefined); | ||
| 14 | + assert.strictEqual(structuredClone(undefined, null), undefined); | ||
| 15 | + // Transfer can be null or undefined. | ||
| 16 | + assert.strictEqual(structuredClone(undefined, { transfer: null }), undefined); | ||
| 17 | + assert.strictEqual(structuredClone(undefined, { }), undefined); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments