| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d6862b0 commit f2f4ce9
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1038,6 +1038,7 @@ | |||
| 1038 | 1038 | 'test/cctest/test_aliased_buffer.cc', | |
| 1039 | 1039 | 'test/cctest/test_base64.cc', | |
| 1040 | 1040 | 'test/cctest/test_base_object_ptr.cc', | |
| 1041 | + 'test/cctest/test_cppgc.cc', | ||
| 1041 | 1042 | 'test/cctest/test_node_postmortem_metadata.cc', | |
| 1042 | 1043 | 'test/cctest/test_environment.cc', | |
| 1043 | 1044 | 'test/cctest/test_linked_binding.cc', | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -70,23 +70,28 @@ Realm* BaseObject::realm() const { | |||
| 70 | 70 | return realm_; | |
| 71 | 71 | } | |
| 72 | 72 | ||
| 73 | - bool BaseObject::IsBaseObject(v8::Local<v8::Object> obj) { | ||
| 73 | + bool BaseObject::IsBaseObject(IsolateData* isolate_data, | ||
| 74 | + v8::Local<v8::Object> obj) { | ||
| 74 | 75 | if (obj->InternalFieldCount() < BaseObject::kInternalFieldCount) { | |
| 75 | 76 | return false; | |
| 76 | 77 | } | |
| 77 | - void* ptr = | ||
| 78 | - obj->GetAlignedPointerFromInternalField(BaseObject::kEmbedderType); | ||
| 79 | - return ptr == &kNodeEmbedderId; | ||
| 78 | + | ||
| 79 | + uint16_t* ptr = static_cast<uint16_t*>( | ||
| 80 | + obj->GetAlignedPointerFromInternalField(BaseObject::kEmbedderType)); | ||
| 81 | + return ptr == isolate_data->embedder_id_for_non_cppgc(); | ||
| 80 | 82 | } | |
| 81 | 83 | ||
| 82 | - void BaseObject::TagBaseObject(v8::Local<v8::Object> object) { | ||
| 84 | + void BaseObject::TagBaseObject(IsolateData* isolate_data, | ||
| 85 | + v8::Local<v8::Object> object) { | ||
| 83 | 86 | DCHECK_GE(object->InternalFieldCount(), BaseObject::kInternalFieldCount); | |
| 84 | - object->SetAlignedPointerInInternalField(BaseObject::kEmbedderType, | ||
| 85 | - &kNodeEmbedderId); | ||
| 87 | + object->SetAlignedPointerInInternalField( | ||
| 88 | + BaseObject::kEmbedderType, isolate_data->embedder_id_for_non_cppgc()); | ||
| 86 | 89 | } | |
| 87 | 90 | ||
| 88 | - void BaseObject::SetInternalFields(v8::Local<v8::Object> object, void* slot) { | ||
| 89 | - TagBaseObject(object); | ||
| 91 | + void BaseObject::SetInternalFields(IsolateData* isolate_data, | ||
| 92 | + v8::Local<v8::Object> object, | ||
| 93 | + void* slot) { | ||
| 94 | + TagBaseObject(isolate_data, object); | ||
| 90 | 95 | object->SetAlignedPointerInInternalField(BaseObject::kSlot, slot); | |
| 91 | 96 | } | |
| 92 | 97 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,7 +17,7 @@ BaseObject::BaseObject(Realm* realm, Local<Object> object) | |||
| 17 | 17 | : persistent_handle_(realm->isolate(), object), realm_(realm) { | |
| 18 | 18 | CHECK_EQ(false, object.IsEmpty()); | |
| 19 | 19 | CHECK_GE(object->InternalFieldCount(), BaseObject::kInternalFieldCount); | |
| 20 | - SetInternalFields(object, static_cast<void*>(this)); | ||
| 20 | + SetInternalFields(realm->isolate_data(), object, static_cast<void*>(this)); | ||
| 21 | 21 | realm->AddCleanupHook(DeleteMe, static_cast<void*>(this)); | |
| 22 | 22 | realm->modify_base_object_count(1); | |
| 23 | 23 | } | |
@@ -66,18 +66,13 @@ void BaseObject::MakeWeak() { | |||
| 66 | 66 | WeakCallbackType::kParameter); | |
| 67 | 67 | } | |
| 68 | 68 | ||
| 69 | - // This just has to be different from the Chromium ones: | ||
| 70 | - // https://source.chromium.org/chromium/chromium/src/+/main:gin/public/gin_embedders.h;l=18-23;drc=5a758a97032f0b656c3c36a3497560762495501a | ||
| 71 | - // Otherwise, when Node is loaded in an isolate which uses cppgc, cppgc will | ||
| 72 | - // misinterpret the data stored in the embedder fields and try to garbage | ||
| 73 | - // collect them. | ||
| 74 | - uint16_t kNodeEmbedderId = 0x90de; | ||
| 75 | - | ||
| 76 | 69 | void BaseObject::LazilyInitializedJSTemplateConstructor( | |
| 77 | 70 | const FunctionCallbackInfo<Value>& args) { | |
| 78 | 71 | DCHECK(args.IsConstructCall()); | |
| 79 | 72 | CHECK_GE(args.This()->InternalFieldCount(), BaseObject::kInternalFieldCount); | |
| 80 | - SetInternalFields(args.This(), nullptr); | ||
| 73 | + Environment* env = Environment::GetCurrent(args); | ||
| 74 | + DCHECK_NOT_NULL(env); | ||
| 75 | + SetInternalFields(env->isolate_data(), args.This(), nullptr); | ||
| 81 | 76 | } | |
| 82 | 77 | ||
| 83 | 78 | Local<FunctionTemplate> BaseObject::MakeLazilyInitializedJSTemplate( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,8 +41,6 @@ namespace worker { | |||
| 41 | 41 | class TransferData; | |
| 42 | 42 | } | |
| 43 | 43 | ||
| 44 | - extern uint16_t kNodeEmbedderId; | ||
| 45 | - | ||
| 46 | 44 | class BaseObject : public MemoryRetainer { | |
| 47 | 45 | public: | |
| 48 | 46 | enum InternalFields { kEmbedderType, kSlot, kInternalFieldCount }; | |
@@ -74,10 +72,13 @@ class BaseObject : public MemoryRetainer { | |||
| 74 | 72 | // was also passed to the `BaseObject()` constructor initially. | |
| 75 | 73 | // This may return `nullptr` if the C++ object has not been constructed yet, | |
| 76 | 74 | // e.g. when the JS object used `MakeLazilyInitializedJSTemplate`. | |
| 77 | - static inline void SetInternalFields(v8::Local<v8::Object> object, | ||
| 75 | + static inline void SetInternalFields(IsolateData* isolate_data, | ||
| 76 | + v8::Local<v8::Object> object, | ||
| 78 | 77 | void* slot); | |
| 79 | - static inline bool IsBaseObject(v8::Local<v8::Object> object); | ||
| 80 | - static inline void TagBaseObject(v8::Local<v8::Object> object); | ||
| 78 | + static inline bool IsBaseObject(IsolateData* isolate_data, | ||
| 79 | + v8::Local<v8::Object> object); | ||
| 80 | + static inline void TagBaseObject(IsolateData* isolate_data, | ||
| 81 | + v8::Local<v8::Object> object); | ||
| 81 | 82 | static void LazilyInitializedJSTemplateConstructor( | |
| 82 | 83 | const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 83 | 84 | static inline BaseObject* FromJSObject(v8::Local<v8::Value> object); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -61,6 +61,14 @@ inline uv_loop_t* IsolateData::event_loop() const { | |||
| 61 | 61 | return event_loop_; | |
| 62 | 62 | } | |
| 63 | 63 | ||
| 64 | + inline uint16_t* IsolateData::embedder_id_for_cppgc() const { | ||
| 65 | + return &(wrapper_data_->cppgc_id); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + inline uint16_t* IsolateData::embedder_id_for_non_cppgc() const { | ||
| 69 | + return &(wrapper_data_->non_cppgc_id); | ||
| 70 | + } | ||
| 71 | + | ||
| 64 | 72 | inline NodeArrayBufferAllocator* IsolateData::node_allocator() const { | |
| 65 | 73 | return node_allocator_; | |
| 66 | 74 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,7 @@ | |||
| 19 | 19 | #include "tracing/agent.h" | |
| 20 | 20 | #include "tracing/traced_value.h" | |
| 21 | 21 | #include "util-inl.h" | |
| 22 | + #include "v8-cppgc.h" | ||
| 22 | 23 | #include "v8-profiler.h" | |
| 23 | 24 | ||
| 24 | 25 | #include <algorithm> | |
@@ -28,6 +29,7 @@ | |||
| 28 | 29 | #include <iostream> | |
| 29 | 30 | #include <limits> | |
| 30 | 31 | #include <memory> | |
| 32 | + #include <unordered_map> | ||
| 31 | 33 | ||
| 32 | 34 | namespace node { | |
| 33 | 35 | ||
@@ -497,6 +499,11 @@ void IsolateData::CreateProperties() { | |||
| 497 | 499 | contextify::ContextifyContext::InitializeGlobalTemplates(this); | |
| 498 | 500 | } | |
| 499 | 501 | ||
| 502 | + constexpr uint16_t kDefaultCppGCEmebdderID = 0x90de; | ||
| 503 | + Mutex IsolateData::isolate_data_mutex_; | ||
| 504 | + std::unordered_map<uint16_t, std::unique_ptr<PerIsolateWrapperData>> | ||
| 505 | + IsolateData::wrapper_data_map_; | ||
| 506 | + | ||
| 500 | 507 | IsolateData::IsolateData(Isolate* isolate, | |
| 501 | 508 | uv_loop_t* event_loop, | |
| 502 | 509 | MultiIsolatePlatform* platform, | |
@@ -510,6 +517,46 @@ IsolateData::IsolateData(Isolate* isolate, | |||
| 510 | 517 | snapshot_data_(snapshot_data) { | |
| 511 | 518 | options_.reset( | |
| 512 | 519 | new PerIsolateOptions(*(per_process::cli_options->per_isolate))); | |
| 520 | + v8::CppHeap* cpp_heap = isolate->GetCppHeap(); | ||
| 521 | + | ||
| 522 | + uint16_t cppgc_id = kDefaultCppGCEmebdderID; | ||
| 523 | + if (cpp_heap != nullptr) { | ||
| 524 | + // The general convention of the wrappable layout for cppgc in the | ||
| 525 | + // ecosystem is: | ||
| 526 | + // [ 0 ] -> embedder id | ||
| 527 | + // [ 1 ] -> wrappable instance | ||
| 528 | + // If the Isolate includes a CppHeap attached by another embedder, | ||
| 529 | + // And if they also use the field 0 for the ID, we DCHECK that | ||
| 530 | + // the layout matches our layout, and record the embedder ID for cppgc | ||
| 531 | + // to avoid accidentally enabling cppgc on non-cppgc-managed wrappers . | ||
| 532 | + v8::WrapperDescriptor descriptor = cpp_heap->wrapper_descriptor(); | ||
| 533 | + if (descriptor.wrappable_type_index == BaseObject::kEmbedderType) { | ||
| 534 | + cppgc_id = descriptor.embedder_id_for_garbage_collected; | ||
| 535 | + DCHECK_EQ(descriptor.wrappable_instance_index, BaseObject::kSlot); | ||
| 536 | + } | ||
| 537 | + // If the CppHeap uses the slot we use to put non-cppgc-traced BaseObject | ||
| 538 | + // for embedder ID, V8 could accidentally enable cppgc on them. So | ||
| 539 | + // safe guard against this. | ||
| 540 | + DCHECK_NE(descriptor.wrappable_type_index, BaseObject::kSlot); | ||
| 541 | + } | ||
| 542 | + // We do not care about overflow since we just want this to be different | ||
| 543 | + // from the cppgc id. | ||
| 544 | + uint16_t non_cppgc_id = cppgc_id + 1; | ||
| 545 | + | ||
| 546 | + { | ||
| 547 | + // GC could still be run after the IsolateData is destroyed, so we store | ||
| 548 | + // the ids in a static map to ensure pointers to them are still valid | ||
| 549 | + // then. In practice there should be very few variants of the cppgc id | ||
| 550 | + // in one process so the size of this map should be very small. | ||
| 551 | + node::Mutex::ScopedLock lock(isolate_data_mutex_); | ||
| 552 | + auto it = wrapper_data_map_.find(cppgc_id); | ||
| 553 | + if (it == wrapper_data_map_.end()) { | ||
| 554 | + auto pair = wrapper_data_map_.emplace( | ||
| 555 | + cppgc_id, new PerIsolateWrapperData{cppgc_id, non_cppgc_id}); | ||
| 556 | + it = pair.first; | ||
| 557 | + } | ||
| 558 | + wrapper_data_ = it->second.get(); | ||
| 559 | + } | ||
| 513 | 560 | ||
| 514 | 561 | if (snapshot_data == nullptr) { | |
| 515 | 562 | CreateProperties(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -124,13 +124,19 @@ struct IsolateDataSerializeInfo { | |||
| 124 | 124 | const IsolateDataSerializeInfo& i); | |
| 125 | 125 | }; | |
| 126 | 126 | ||
| 127 | + struct PerIsolateWrapperData { | ||
| 128 | + uint16_t cppgc_id; | ||
| 129 | + uint16_t non_cppgc_id; | ||
| 130 | + }; | ||
| 131 | + | ||
| 127 | 132 | class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { | |
| 128 | 133 | public: | |
| 129 | 134 | IsolateData(v8::Isolate* isolate, | |
| 130 | 135 | uv_loop_t* event_loop, | |
| 131 | 136 | MultiIsolatePlatform* platform = nullptr, | |
| 132 | 137 | ArrayBufferAllocator* node_allocator = nullptr, | |
| 133 | 138 | const SnapshotData* snapshot_data = nullptr); | |
| 139 | + | ||
| 134 | 140 | SET_MEMORY_INFO_NAME(IsolateData) | |
| 135 | 141 | SET_SELF_SIZE(IsolateData) | |
| 136 | 142 | void MemoryInfo(MemoryTracker* tracker) const override; | |
@@ -139,6 +145,9 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { | |||
| 139 | 145 | bool is_building_snapshot() const { return is_building_snapshot_; } | |
| 140 | 146 | void set_is_building_snapshot(bool value) { is_building_snapshot_ = value; } | |
| 141 | 147 | ||
| 148 | + uint16_t* embedder_id_for_cppgc() const; | ||
| 149 | + uint16_t* embedder_id_for_non_cppgc() const; | ||
| 150 | + | ||
| 142 | 151 | inline uv_loop_t* event_loop() const; | |
| 143 | 152 | inline MultiIsolatePlatform* platform() const; | |
| 144 | 153 | inline const SnapshotData* snapshot_data() const; | |
@@ -223,6 +232,11 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { | |||
| 223 | 232 | std::shared_ptr<PerIsolateOptions> options_; | |
| 224 | 233 | worker::Worker* worker_context_ = nullptr; | |
| 225 | 234 | bool is_building_snapshot_ = false; | |
| 235 | + PerIsolateWrapperData* wrapper_data_; | ||
| 236 | + | ||
| 237 | + static Mutex isolate_data_mutex_; | ||
| 238 | + static std::unordered_map<uint16_t, std::unique_ptr<PerIsolateWrapperData>> | ||
| 239 | + wrapper_data_map_; | ||
| 226 | 240 | }; | |
| 227 | 241 | ||
| 228 | 242 | struct ContextInfo { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -318,7 +318,7 @@ class SerializerDelegate : public ValueSerializer::Delegate { | |||
| 318 | 318 | } | |
| 319 | 319 | ||
| 320 | 320 | Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override { | |
| 321 | - if (BaseObject::IsBaseObject(object)) { | ||
| 321 | + if (BaseObject::IsBaseObject(env_->isolate_data(), object)) { | ||
| 322 | 322 | return WriteHostObject( | |
| 323 | 323 | BaseObjectPtr<BaseObject> { Unwrap<BaseObject>(object) }); | |
| 324 | 324 | } | |
@@ -514,7 +514,8 @@ Maybe<bool> Message::Serialize(Environment* env, | |||
| 514 | 514 | serializer.TransferArrayBuffer(id, ab); | |
| 515 | 515 | continue; | |
| 516 | 516 | } else if (entry->IsObject() && | |
| 517 | - BaseObject::IsBaseObject(entry.As<Object>())) { | ||
| 517 | + BaseObject::IsBaseObject(env->isolate_data(), | ||
| 518 | + entry.As<Object>())) { | ||
| 518 | 519 | // Check if the source MessagePort is being transferred. | |
| 519 | 520 | if (!source_port.IsEmpty() && entry == source_port) { | |
| 520 | 521 | ThrowDataCloneException( | |
@@ -1281,7 +1282,8 @@ JSTransferable::NestedTransferables() const { | |||
| 1281 | 1282 | Local<Value> value; | |
| 1282 | 1283 | if (!list->Get(context, i).ToLocal(&value)) | |
| 1283 | 1284 | return Nothing<BaseObjectList>(); | |
| 1284 | - if (value->IsObject() && BaseObject::IsBaseObject(value.As<Object>())) | ||
| 1285 | + if (value->IsObject() && | ||
| 1286 | + BaseObject::IsBaseObject(env()->isolate_data(), value.As<Object>())) | ||
| 1285 | 1287 | ret.emplace_back(Unwrap<BaseObject>(value)); | |
| 1286 | 1288 | } | |
| 1287 | 1289 | return Just(ret); | |
@@ -1336,7 +1338,8 @@ BaseObjectPtr<BaseObject> JSTransferable::Data::Deserialize( | |||
| 1336 | 1338 | if (!env->messaging_deserialize_create_object() | |
| 1337 | 1339 | ->Call(context, Null(env->isolate()), 1, &info) | |
| 1338 | 1340 | .ToLocal(&ret) || | |
| 1339 | - !ret->IsObject() || !BaseObject::IsBaseObject(ret.As<Object>())) { | ||
| 1341 | + !ret->IsObject() || | ||
| 1342 | + !BaseObject::IsBaseObject(env->isolate_data(), ret.As<Object>())) { | ||
| 1340 | 1343 | return {}; | |
| 1341 | 1344 | } | |
| 1342 | 1345 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1202,14 +1202,16 @@ void DeserializeNodeInternalFields(Local<Object> holder, | |||
| 1202 | 1202 | ||
| 1203 | 1203 | StartupData SerializeNodeContextInternalFields(Local<Object> holder, | |
| 1204 | 1204 | int index, | |
| 1205 | - void* env) { | ||
| 1205 | + void* callback_data) { | ||
| 1206 | 1206 | // We only do one serialization for the kEmbedderType slot, the result | |
| 1207 | 1207 | // contains everything necessary for deserializing the entire object, | |
| 1208 | 1208 | // including the fields whose index is bigger than kEmbedderType | |
| 1209 | 1209 | // (most importantly, BaseObject::kSlot). | |
| 1210 | 1210 | // For Node.js this design is enough for all the native binding that are | |
| 1211 | 1211 | // serializable. | |
| 1212 | - if (index != BaseObject::kEmbedderType || !BaseObject::IsBaseObject(holder)) { | ||
| 1212 | + Environment* env = static_cast<Environment*>(callback_data); | ||
| 1213 | + if (index != BaseObject::kEmbedderType || | ||
| 1214 | + !BaseObject::IsBaseObject(env->isolate_data(), holder)) { | ||
| 1213 | 1215 | return StartupData{nullptr, 0}; | |
| 1214 | 1216 | } | |
| 1215 | 1217 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -34,6 +34,7 @@ void NodeTestEnvironment::SetUp() { | |||
| 34 | 34 | } | |
| 35 | 35 | ||
| 36 | 36 | void NodeTestEnvironment::TearDown() { | |
| 37 | + cppgc::ShutdownProcess(); | ||
| 37 | 38 | v8::V8::Dispose(); | |
| 38 | 39 | v8::V8::DisposePlatform(); | |
| 39 | 40 | NodeZeroIsolateTestFixture::platform->Shutdown(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments