Skip to content

Commit cf13b9b

Browse files
legendecasV8 LUCI CQ
authored andcommitted
[objects] Determine host objects with serializer delegate
Enable custom host objects filter with ValueSerializer::Delegate when ValueSerializer::Delegate::HasCustomHostObject returns true. This allows the embedder to serialize JavaScript implemented host objects. Bug: v8:11927 Change-Id: I70e7aa70b10dc1053c113d521479cbe746febc7e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4385565 Commit-Queue: Chengzhong Wu (legendecas) <[email protected]> Reviewed-by: Camillo Bruni <[email protected]> Cr-Commit-Position: refs/heads/main@{#87055}
1 parent bb28174 commit cf13b9b

File tree

5 files changed

+110
-3
lines changed

5 files changed

+110
-3
lines changed

include/v8-value-serializer.h

Lines 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

src/api/api.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3572,6 +3572,19 @@ Maybe<bool> ValueSerializer::Delegate::WriteHostObject(Isolate* v8_isolate,
35723572
return Nothing<bool>();
35733573
}
35743574

3575+
bool ValueSerializer::Delegate::HasCustomHostObject(Isolate* v8_isolate) {
3576+
return false;
3577+
}
3578+
3579+
Maybe<bool> ValueSerializer::Delegate::IsHostObject(Isolate* v8_isolate,
3580+
Local<Object> object) {
3581+
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
3582+
i::Handle<i::JSObject> js_object =
3583+
i::Handle<i::JSObject>::cast(Utils::OpenHandle(*object));
3584+
return Just<bool>(
3585+
i::JSObject::GetEmbedderFieldCount(js_object->map(i_isolate)));
3586+
}
3587+
35753588
Maybe<uint32_t> ValueSerializer::Delegate::GetSharedArrayBufferId(
35763589
Isolate* v8_isolate, Local<SharedArrayBuffer> shared_array_buffer) {
35773590
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);

src/objects/value-serializer.cc

Lines 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);

src/objects/value-serializer.h

Lines 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_;

test/unittests/objects/value-serializer-unittest.cc

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2811,7 +2811,18 @@ TEST_F(ValueSerializerTest, UnsupportedHostObject) {
28112811

28122812
class ValueSerializerTestWithHostObject : public ValueSerializerTest {
28132813
protected:
2814-
ValueSerializerTestWithHostObject() : serializer_delegate_(this) {}
2814+
ValueSerializerTestWithHostObject() : serializer_delegate_(this) {
2815+
ON_CALL(serializer_delegate_, HasCustomHostObject)
2816+
.WillByDefault([this](Isolate* isolate) {
2817+
return serializer_delegate_
2818+
.ValueSerializer::Delegate::HasCustomHostObject(isolate);
2819+
});
2820+
ON_CALL(serializer_delegate_, IsHostObject)
2821+
.WillByDefault([this](Isolate* isolate, Local<Object> object) {
2822+
return serializer_delegate_.ValueSerializer::Delegate::IsHostObject(
2823+
isolate, object);
2824+
});
2825+
}
28152826

28162827
static const uint8_t kExampleHostObjectTag;
28172828

@@ -2835,6 +2846,9 @@ class ValueSerializerTestWithHostObject : public ValueSerializerTest {
28352846
public:
28362847
explicit SerializerDelegate(ValueSerializerTestWithHostObject* test)
28372848
: test_(test) {}
2849+
MOCK_METHOD(bool, HasCustomHostObject, (Isolate*), (override));
2850+
MOCK_METHOD(Maybe<bool>, IsHostObject, (Isolate*, Local<Object> object),
2851+
(override));
28382852
MOCK_METHOD(Maybe<bool>, WriteHostObject, (Isolate*, Local<Object> object),
28392853
(override));
28402854
void ThrowDataCloneError(Local<String> message) override {
@@ -3052,6 +3066,43 @@ TEST_F(ValueSerializerTestWithHostObject, DecodeSimpleHostObject) {
30523066
});
30533067
}
30543068

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

0 commit comments

Comments
 (0)