Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 38dee8a

Browse filesBrowse files
authored
src: distinguish HTML transferable and cloneable
The HTML structured serialize algorithm treats transferable and serializable as two different bits. A web platform interface can be both transferable and serializable. Splits BaseObject::TransferMode to be able to compose the two bits and distinguishes the transferable and cloneable. PR-URL: #47956 Refs: v8/v8@cf13b9b Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
1 parent 3205b19 commit 38dee8a
Copy full SHA for 38dee8a
Expand file treeCollapse file tree

37 files changed

+547
-320
lines changed
Open diff view settings
Collapse file

‎common.gypi‎

Copy file name to clipboardExpand all lines: common.gypi
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
# Reset this number to 0 on major V8 upgrades.
3838
# Increment by one for each non-official patch applied to deps/v8.
39-
'v8_embedder_string': '-node.10',
39+
'v8_embedder_string': '-node.11',
4040

4141
##### V8 defaults for Node.js #####
4242

Collapse file

‎deps/v8/include/v8-value-serializer.h‎

Copy file name to clipboardExpand all lines: deps/v8/include/v8-value-serializer.h
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@ class V8_EXPORT ValueSerializer {
7575
*/
7676
virtual void ThrowDataCloneError(Local<String> message) = 0;
7777

78+
/**
79+
* The embedder overrides this method to enable custom host object filter
80+
* with Delegate::IsHostObject.
81+
*
82+
* This method is called at most once per serializer.
83+
*/
84+
virtual bool HasCustomHostObject(Isolate* isolate);
85+
86+
/**
87+
* The embedder overrides this method to determine if an JS object is a
88+
* host object and needs to be serialized by the host.
89+
*/
90+
virtual Maybe<bool> IsHostObject(Isolate* isolate, Local<Object> object);
91+
7892
/**
7993
* The embedder overrides this method to write some kind of host object, if
8094
* possible. If not, a suitable exception should be thrown and
Collapse file

‎deps/v8/src/api/api.cc‎

Copy file name to clipboardExpand all lines: deps/v8/src/api/api.cc
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3569,6 +3569,19 @@ Maybe<bool> ValueSerializer::Delegate::WriteHostObject(Isolate* v8_isolate,
35693569
return Nothing<bool>();
35703570
}
35713571

3572+
bool ValueSerializer::Delegate::HasCustomHostObject(Isolate* v8_isolate) {
3573+
return false;
3574+
}
3575+
3576+
Maybe<bool> ValueSerializer::Delegate::IsHostObject(Isolate* v8_isolate,
3577+
Local<Object> object) {
3578+
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
3579+
i::Handle<i::JSObject> js_object =
3580+
i::Handle<i::JSObject>::cast(Utils::OpenHandle(*object));
3581+
return Just<bool>(
3582+
i::JSObject::GetEmbedderFieldCount(js_object->map(i_isolate)));
3583+
}
3584+
35723585
Maybe<uint32_t> ValueSerializer::Delegate::GetSharedArrayBufferId(
35733586
Isolate* v8_isolate, Local<SharedArrayBuffer> shared_array_buffer) {
35743587
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
Collapse file

‎deps/v8/src/objects/value-serializer.cc‎

Copy file name to clipboardExpand all lines: deps/v8/src/objects/value-serializer.cc
+28-2Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,12 @@ ValueSerializer::ValueSerializer(Isolate* isolate,
268268
zone_(isolate->allocator(), ZONE_NAME),
269269
id_map_(isolate->heap(), ZoneAllocationPolicy(&zone_)),
270270
array_buffer_transfer_map_(isolate->heap(),
271-
ZoneAllocationPolicy(&zone_)) {}
271+
ZoneAllocationPolicy(&zone_)) {
272+
if (delegate_) {
273+
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
274+
has_custom_host_objects_ = delegate_->HasCustomHostObject(v8_isolate);
275+
}
276+
}
272277

273278
ValueSerializer::~ValueSerializer() {
274279
if (buffer_) {
@@ -582,7 +587,11 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
582587
case JS_TYPED_ARRAY_PROTOTYPE_TYPE:
583588
case JS_API_OBJECT_TYPE: {
584589
Handle<JSObject> js_object = Handle<JSObject>::cast(receiver);
585-
if (JSObject::GetEmbedderFieldCount(js_object->map(isolate_))) {
590+
Maybe<bool> is_host_object = IsHostObject(js_object);
591+
if (is_host_object.IsNothing()) {
592+
return is_host_object;
593+
}
594+
if (is_host_object.FromJust()) {
586595
return WriteHostObject(js_object);
587596
} else {
588597
return WriteJSObject(js_object);
@@ -1190,6 +1199,23 @@ Maybe<uint32_t> ValueSerializer::WriteJSObjectPropertiesSlow(
11901199
return Just(properties_written);
11911200
}
11921201

1202+
Maybe<bool> ValueSerializer::IsHostObject(Handle<JSObject> js_object) {
1203+
if (!has_custom_host_objects_) {
1204+
return Just<bool>(
1205+
JSObject::GetEmbedderFieldCount(js_object->map(isolate_)));
1206+
}
1207+
DCHECK_NOT_NULL(delegate_);
1208+
1209+
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
1210+
Maybe<bool> result =
1211+
delegate_->IsHostObject(v8_isolate, Utils::ToLocal(js_object));
1212+
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
1213+
DCHECK(!result.IsNothing());
1214+
1215+
if (V8_UNLIKELY(out_of_memory_)) return ThrowIfOutOfMemory();
1216+
return result;
1217+
}
1218+
11931219
Maybe<bool> ValueSerializer::ThrowIfOutOfMemory() {
11941220
if (out_of_memory_) {
11951221
return ThrowDataCloneError(MessageTemplate::kDataCloneErrorOutOfMemory);
Collapse file

‎deps/v8/src/objects/value-serializer.h‎

Copy file name to clipboardExpand all lines: deps/v8/src/objects/value-serializer.h
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ class ValueSerializer {
155155
Maybe<uint32_t> WriteJSObjectPropertiesSlow(
156156
Handle<JSObject> object, Handle<FixedArray> keys) V8_WARN_UNUSED_RESULT;
157157

158+
Maybe<bool> IsHostObject(Handle<JSObject> object);
159+
158160
/*
159161
* Asks the delegate to handle an error that occurred during data cloning, by
160162
* throwing an exception appropriate for the host.
@@ -172,6 +174,7 @@ class ValueSerializer {
172174
uint8_t* buffer_ = nullptr;
173175
size_t buffer_size_ = 0;
174176
size_t buffer_capacity_ = 0;
177+
bool has_custom_host_objects_ = false;
175178
bool treat_array_buffer_views_as_host_objects_ = false;
176179
bool out_of_memory_ = false;
177180
Zone zone_;
Collapse file

‎deps/v8/test/unittests/objects/value-serializer-unittest.cc‎

Copy file name to clipboardExpand all lines: deps/v8/test/unittests/objects/value-serializer-unittest.cc
+52-1Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2808,7 +2808,18 @@ TEST_F(ValueSerializerTest, UnsupportedHostObject) {
28082808

28092809
class ValueSerializerTestWithHostObject : public ValueSerializerTest {
28102810
protected:
2811-
ValueSerializerTestWithHostObject() : serializer_delegate_(this) {}
2811+
ValueSerializerTestWithHostObject() : serializer_delegate_(this) {
2812+
ON_CALL(serializer_delegate_, HasCustomHostObject)
2813+
.WillByDefault([this](Isolate* isolate) {
2814+
return serializer_delegate_
2815+
.ValueSerializer::Delegate::HasCustomHostObject(isolate);
2816+
});
2817+
ON_CALL(serializer_delegate_, IsHostObject)
2818+
.WillByDefault([this](Isolate* isolate, Local<Object> object) {
2819+
return serializer_delegate_.ValueSerializer::Delegate::IsHostObject(
2820+
isolate, object);
2821+
});
2822+
}
28122823

28132824
static const uint8_t kExampleHostObjectTag;
28142825

@@ -2832,6 +2843,9 @@ class ValueSerializerTestWithHostObject : public ValueSerializerTest {
28322843
public:
28332844
explicit SerializerDelegate(ValueSerializerTestWithHostObject* test)
28342845
: test_(test) {}
2846+
MOCK_METHOD(bool, HasCustomHostObject, (Isolate*), (override));
2847+
MOCK_METHOD(Maybe<bool>, IsHostObject, (Isolate*, Local<Object> object),
2848+
(override));
28352849
MOCK_METHOD(Maybe<bool>, WriteHostObject, (Isolate*, Local<Object> object),
28362850
(override));
28372851
void ThrowDataCloneError(Local<String> message) override {
@@ -3049,6 +3063,43 @@ TEST_F(ValueSerializerTestWithHostObject, DecodeSimpleHostObject) {
30493063
});
30503064
}
30513065

3066+
TEST_F(ValueSerializerTestWithHostObject,
3067+
RoundTripHostJSObjectWithoutCustomHostObject) {
3068+
EXPECT_CALL(serializer_delegate_, HasCustomHostObject(isolate()))
3069+
.WillOnce(Invoke([](Isolate* isolate) { return false; }));
3070+
RoundTripTest("({ a: { my_host_object: true }, get b() { return this.a; }})");
3071+
}
3072+
3073+
TEST_F(ValueSerializerTestWithHostObject, RoundTripHostJSObject) {
3074+
EXPECT_CALL(serializer_delegate_, HasCustomHostObject(isolate()))
3075+
.WillOnce(Invoke([](Isolate* isolate) { return true; }));
3076+
EXPECT_CALL(serializer_delegate_, IsHostObject(isolate(), _))
3077+
.WillRepeatedly(Invoke([this](Isolate* isolate, Local<Object> object) {
3078+
EXPECT_TRUE(object->IsObject());
3079+
Local<Context> context = isolate->GetCurrentContext();
3080+
return object->Has(context, StringFromUtf8("my_host_object"));
3081+
}));
3082+
EXPECT_CALL(serializer_delegate_, WriteHostObject(isolate(), _))
3083+
.WillOnce(Invoke([this](Isolate*, Local<Object> object) {
3084+
EXPECT_TRUE(object->IsObject());
3085+
WriteExampleHostObjectTag();
3086+
return Just(true);
3087+
}));
3088+
EXPECT_CALL(deserializer_delegate_, ReadHostObject(isolate()))
3089+
.WillOnce(Invoke([this](Isolate* isolate) {
3090+
EXPECT_TRUE(ReadExampleHostObjectTag());
3091+
Local<Context> context = isolate->GetCurrentContext();
3092+
Local<Object> obj = Object::New(isolate);
3093+
obj->Set(context, StringFromUtf8("my_host_object"), v8::True(isolate))
3094+
.Check();
3095+
return obj;
3096+
}));
3097+
RoundTripTest("({ a: { my_host_object: true }, get b() { return this.a; }})");
3098+
ExpectScriptTrue("!('my_host_object' in result)");
3099+
ExpectScriptTrue("result.a.my_host_object");
3100+
ExpectScriptTrue("result.a === result.b");
3101+
}
3102+
30523103
class ValueSerializerTestWithHostArrayBufferView
30533104
: public ValueSerializerTestWithHostObject {
30543105
protected:
Collapse file

‎lib/internal/abort_controller.js‎

Copy file name to clipboardExpand all lines: lib/internal/abort_controller.js
+16-12Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ const {
5858
const assert = require('internal/assert');
5959

6060
const {
61-
messaging_deserialize_symbol: kDeserialize,
62-
messaging_transfer_symbol: kTransfer,
63-
messaging_transfer_list_symbol: kTransferList,
64-
} = internalBinding('symbols');
61+
kDeserialize,
62+
kTransfer,
63+
kTransferList,
64+
} = require('internal/worker/js_transferable');
6565

6666
let _MessageChannel;
67-
let makeTransferable;
67+
let markTransferMode;
6868

69-
// Loading the MessageChannel and makeTransferable have to be done lazily
69+
// Loading the MessageChannel and markTransferable have to be done lazily
7070
// because otherwise we'll end up with a require cycle that ends up with
7171
// an incomplete initialization of abort_controller.
7272

@@ -75,10 +75,10 @@ function lazyMessageChannel() {
7575
return new _MessageChannel();
7676
}
7777

78-
function lazyMakeTransferable(obj) {
79-
makeTransferable ??=
80-
require('internal/worker/js_transferable').makeTransferable;
81-
return makeTransferable(obj);
78+
function lazyMarkTransferMode(obj, cloneable, transferable) {
79+
markTransferMode ??=
80+
require('internal/worker/js_transferable').markTransferMode;
81+
markTransferMode(obj, cloneable, transferable);
8282
}
8383

8484
const clearTimeoutRegistry = new SafeFinalizationRegistry(clearTimeout);
@@ -355,7 +355,10 @@ function createAbortSignal(init = kEmptyObject) {
355355
signal[kAborted] = aborted;
356356
signal[kReason] = reason;
357357
signal[kComposite] = composite;
358-
return transferable ? lazyMakeTransferable(signal) : signal;
358+
if (transferable) {
359+
lazyMarkTransferMode(signal, false, true);
360+
}
361+
return signal;
359362
}
360363

361364
function abortSignal(signal, reason) {
@@ -411,7 +414,8 @@ class AbortController {
411414
function transferableAbortSignal(signal) {
412415
if (signal?.[kAborted] === undefined)
413416
throw new ERR_INVALID_ARG_TYPE('signal', 'AbortSignal', signal);
414-
return lazyMakeTransferable(signal);
417+
lazyMarkTransferMode(signal, false, true);
418+
return signal;
415419
}
416420

417421
/**
Collapse file

‎lib/internal/blob.js‎

Copy file name to clipboardExpand all lines: lib/internal/blob.js
+9-7Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const {
3232
const { URL } = require('internal/url');
3333

3434
const {
35-
makeTransferable,
35+
markTransferMode,
3636
kClone,
3737
kDeserialize,
3838
} = require('internal/worker/js_transferable');
@@ -136,6 +136,8 @@ class Blob {
136136
* @constructs {Blob}
137137
*/
138138
constructor(sources = [], options) {
139+
markTransferMode(this, true, false);
140+
139141
if (sources === null ||
140142
typeof sources[SymbolIterator] !== 'function' ||
141143
typeof sources === 'string') {
@@ -167,9 +169,6 @@ class Blob {
167169
type = `${type}`;
168170
this[kType] = RegExpPrototypeExec(disallowedTypeCharacters, type) !== null ?
169171
'' : StringPrototypeToLowerCase(type);
170-
171-
// eslint-disable-next-line no-constructor-return
172-
return makeTransferable(this);
173172
}
174173

175174
[kInspect](depth, options) {
@@ -385,16 +384,19 @@ class Blob {
385384
}
386385

387386
function ClonedBlob() {
388-
return makeTransferable(ReflectConstruct(function() {}, [], Blob));
387+
return ReflectConstruct(function() {
388+
markTransferMode(this, true, false);
389+
}, [], Blob);
389390
}
390391
ClonedBlob.prototype[kDeserialize] = () => {};
391392

392393
function createBlob(handle, length, type = '') {
393-
return makeTransferable(ReflectConstruct(function() {
394+
return ReflectConstruct(function() {
395+
markTransferMode(this, true, false);
394396
this[kHandle] = handle;
395397
this[kType] = type;
396398
this[kLength] = length;
397-
}, [], Blob));
399+
}, [], Blob);
398400
}
399401

400402
ObjectDefineProperty(Blob.prototype, SymbolToStringTag, {
Collapse file

‎lib/internal/blocklist.js‎

Copy file name to clipboardExpand all lines: lib/internal/blocklist.js
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const {
2020
} = require('internal/socketaddress');
2121

2222
const {
23-
JSTransferable,
23+
markTransferMode,
2424
kClone,
2525
kDeserialize,
2626
} = require('internal/worker/js_transferable');
@@ -36,9 +36,9 @@ const {
3636

3737
const { validateInt32, validateString } = require('internal/validators');
3838

39-
class BlockList extends JSTransferable {
39+
class BlockList {
4040
constructor() {
41-
super();
41+
markTransferMode(this, true, false);
4242
this[kHandle] = new BlockListHandle();
4343
this[kHandle][owner_symbol] = this;
4444
}
@@ -148,9 +148,9 @@ class BlockList extends JSTransferable {
148148
}
149149
}
150150

151-
class InternalBlockList extends JSTransferable {
151+
class InternalBlockList {
152152
constructor(handle) {
153-
super();
153+
markTransferMode(this, true, false);
154154
this[kHandle] = handle;
155155
if (handle !== undefined)
156156
handle[owner_symbol] = this;
Collapse file

‎lib/internal/crypto/keys.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/keys.js
+3-5Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const {
5757
} = require('internal/util/types');
5858

5959
const {
60-
makeTransferable,
60+
markTransferMode,
6161
kClone,
6262
kDeserialize,
6363
} = require('internal/worker/js_transferable');
@@ -706,24 +706,22 @@ ObjectDefineProperties(CryptoKey.prototype, {
706706
// All internal code must use new InternalCryptoKey to create
707707
// CryptoKey instances. The CryptoKey class is exposed to end
708708
// user code but is not permitted to be constructed directly.
709-
// Using makeTransferable also allows the CryptoKey to be
709+
// Using markTransferMode also allows the CryptoKey to be
710710
// cloned to Workers.
711711
class InternalCryptoKey {
712712
constructor(
713713
keyObject,
714714
algorithm,
715715
keyUsages,
716716
extractable) {
717+
markTransferMode(this, true, false);
717718
// Using symbol properties here currently instead of private
718719
// properties because (for now) the performance penalty of
719720
// private fields is still too high.
720721
this[kKeyObject] = keyObject;
721722
this[kAlgorithm] = algorithm;
722723
this[kExtractable] = extractable;
723724
this[kKeyUsages] = keyUsages;
724-
725-
// eslint-disable-next-line no-constructor-return
726-
return makeTransferable(this);
727725
}
728726

729727
[kClone]() {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.