GeezerVison is showing github.com — src: make structuredClone work for process.env · nodejs/node@b88ee54
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: src/env_properties.h +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 334 | 334 | V(contextify_global_template, v8::ObjectTemplate) \ | 335 | 335 | V(contextify_wrapper_template, v8::ObjectTemplate) \ | 336 | 336 | V(compiled_fn_entry_template, v8::ObjectTemplate) \ | | 337 | + V(env_proxy_template, v8::ObjectTemplate) \ | | 338 | + V(env_proxy_ctor_template, v8::FunctionTemplate) \ | 337 | 339 | V(dir_instance_template, v8::ObjectTemplate) \ | 338 | 340 | V(fd_constructor_template, v8::ObjectTemplate) \ | 339 | 341 | V(fdclose_constructor_template, v8::ObjectTemplate) \ | |
Collapse file Copy file name to clipboardExpand all lines: src/node_env_var.cc +30-5Lines changed: 30 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -13,7 +13,7 @@ using v8::Boolean; | 13 | 13 | using v8::Context; | 14 | 14 | using v8::DontDelete; | 15 | 15 | using v8::DontEnum; | 16 | | -using v8::EscapableHandleScope; | | 16 | +using v8::FunctionTemplate; | 17 | 17 | using v8::HandleScope; | 18 | 18 | using v8::Integer; | 19 | 19 | using v8::Isolate; | @@ -316,6 +316,26 @@ Maybe<bool> KVStore::AssignFromObject(Local<Context> context, | 316 | 316 | return Just(true); | 317 | 317 | } | 318 | 318 |
| | 319 | +// TODO(bnoordhuis) Not super efficient but called infrequently. Not worth | | 320 | +// the trouble yet of specializing for RealEnvStore and MapKVStore. | | 321 | +Maybe<bool> KVStore::AssignToObject(v8::Isolate* isolate, | | 322 | + v8::Local<v8::Context> context, | | 323 | + v8::Local<v8::Object> object) { | | 324 | + HandleScope scope(isolate); | | 325 | + Local<Array> keys = Enumerate(isolate); | | 326 | + uint32_t keys_length = keys->Length(); | | 327 | + for (uint32_t i = 0; i < keys_length; i++) { | | 328 | + Local<Value> key; | | 329 | + Local<String> value; | | 330 | + bool ok = keys->Get(context, i).ToLocal(&key); | | 331 | + ok = ok && key->IsString(); | | 332 | + ok = ok && Get(isolate, key.As<String>()).ToLocal(&value); | | 333 | + ok = ok && object->Set(context, key, value).To(&ok); | | 334 | + if (!ok) return Nothing<bool>(); | | 335 | + } | | 336 | + return Just(true); | | 337 | +} | | 338 | + | 319 | 339 | static void EnvGetter(Local<Name> property, | 320 | 340 | const PropertyCallbackInfo<Value>& info) { | 321 | 341 | Environment* env = Environment::GetCurrent(info); | @@ -436,9 +456,13 @@ static void EnvDefiner(Local<Name> property, | 436 | 456 | } | 437 | 457 | } | 438 | 458 |
| 439 | | -MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context, Isolate* isolate) { | 440 | | - EscapableHandleScope scope(isolate); | 441 | | - Local<ObjectTemplate> env_proxy_template = ObjectTemplate::New(isolate); | | 459 | +void CreateEnvProxyTemplate(Isolate* isolate, IsolateData* isolate_data) { | | 460 | + HandleScope scope(isolate); | | 461 | + if (!isolate_data->env_proxy_template().IsEmpty()) return; | | 462 | + Local<FunctionTemplate> env_proxy_ctor_template = | | 463 | + FunctionTemplate::New(isolate); | | 464 | + Local<ObjectTemplate> env_proxy_template = | | 465 | + ObjectTemplate::New(isolate, env_proxy_ctor_template); | 442 | 466 | env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration( | 443 | 467 | EnvGetter, | 444 | 468 | EnvSetter, | @@ -449,7 +473,8 @@ MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context, Isolate* isolate) { | 449 | 473 | nullptr, | 450 | 474 | Local<Value>(), | 451 | 475 | PropertyHandlerFlags::kHasNoSideEffect)); | 452 | | - return scope.EscapeMaybe(env_proxy_template->NewInstance(context)); | | 476 | + isolate_data->set_env_proxy_template(env_proxy_template); | | 477 | + isolate_data->set_env_proxy_ctor_template(env_proxy_ctor_template); | 453 | 478 | } | 454 | 479 |
| 455 | 480 | void RegisterEnvVarExternalReferences(ExternalReferenceRegistry* registry) { | |
Collapse file Copy file name to clipboardExpand all lines: src/node_messaging.cc +30-2Lines changed: 30 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -42,6 +42,10 @@ namespace node { | 42 | 42 |
| 43 | 43 | using BaseObjectList = std::vector<BaseObjectPtr<BaseObject>>; | 44 | 44 |
| | 45 | +// Hack to have WriteHostObject inform ReadHostObject that the value | | 46 | +// should be treated as a regular JS object. Used to transfer process.env. | | 47 | +static const uint32_t kNormalObject = static_cast<uint32_t>(-1); | | 48 | + | 45 | 49 | BaseObject::TransferMode BaseObject::GetTransferMode() const { | 46 | 50 | return BaseObject::TransferMode::kUntransferable; | 47 | 51 | } | @@ -98,8 +102,17 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { | 98 | 102 | uint32_t id; | 99 | 103 | if (!deserializer->ReadUint32(&id)) | 100 | 104 | return MaybeLocal<Object>(); | 101 | | - CHECK_LT(id, host_objects_.size()); | 102 | | - return host_objects_[id]->object(isolate); | | 105 | + if (id != kNormalObject) { | | 106 | + CHECK_LT(id, host_objects_.size()); | | 107 | + return host_objects_[id]->object(isolate); | | 108 | + } | | 109 | + EscapableHandleScope scope(isolate); | | 110 | + Local<Context> context = isolate->GetCurrentContext(); | | 111 | + Local<Value> object; | | 112 | + if (!deserializer->ReadValue(context).ToLocal(&object)) | | 113 | + return MaybeLocal<Object>(); | | 114 | + CHECK(object->IsObject()); | | 115 | + return scope.Escape(object.As<Object>()); | 103 | 116 | } | 104 | 117 |
| 105 | 118 | MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId( | @@ -293,6 +306,21 @@ class SerializerDelegate : public ValueSerializer::Delegate { | 293 | 306 | BaseObjectPtr<BaseObject> { Unwrap<BaseObject>(object) }); | 294 | 307 | } | 295 | 308 |
| | 309 | + // Convert process.env to a regular object. | | 310 | + auto env_proxy_ctor_template = env_->env_proxy_ctor_template(); | | 311 | + if (!env_proxy_ctor_template.IsEmpty() && | | 312 | + env_proxy_ctor_template->HasInstance(object)) { | | 313 | + HandleScope scope(isolate); | | 314 | + // TODO(bnoordhuis) Prototype-less object in case process.env contains | | 315 | + // a "__proto__" key? process.env has a prototype with concomitant | | 316 | + // methods like toString(). It's probably confusing if that gets lost | | 317 | + // in transmission. | | 318 | + Local<Object> normal_object = Object::New(isolate); | | 319 | + env_->env_vars()->AssignToObject(isolate, env_->context(), normal_object); | | 320 | + serializer->WriteUint32(kNormalObject); // Instead of a BaseObject. | | 321 | + return serializer->WriteValue(env_->context(), normal_object); | | 322 | + } | | 323 | + | 296 | 324 | ThrowDataCloneError(env_->clone_unsupported_type_str()); | 297 | 325 | return Nothing<bool>(); | 298 | 326 | } | |
Collapse file Copy file name to clipboardExpand all lines: src/node_process.h +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 10 | 10 | namespace node { | 11 | 11 |
| 12 | 12 | class Environment; | | 13 | +class IsolateData; | 13 | 14 | class MemoryTracker; | 14 | 15 | class ExternalReferenceRegistry; | 15 | 16 | class Realm; | 16 | 17 |
| 17 | | -v8::MaybeLocal<v8::Object> CreateEnvVarProxy(v8::Local<v8::Context> context, | 18 | | - v8::Isolate* isolate); | | 18 | +void CreateEnvProxyTemplate(v8::Isolate* isolate, IsolateData* isolate_data); | 19 | 19 |
| 20 | 20 | // Most of the time, it's best to use `console.error` to write | 21 | 21 | // to the process.stderr stream. However, in some cases, such as | |
Collapse file Copy file name to clipboardExpand all lines: src/node_realm.cc +4-3Lines changed: 4 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -275,9 +275,10 @@ MaybeLocal<Value> Realm::BootstrapNode() { | 275 | 275 | } | 276 | 276 |
| 277 | 277 | Local<String> env_string = FIXED_ONE_BYTE_STRING(isolate_, "env"); | 278 | | - Local<Object> env_var_proxy; | 279 | | - if (!CreateEnvVarProxy(context(), isolate_).ToLocal(&env_var_proxy) || | 280 | | - process_object()->Set(context(), env_string, env_var_proxy).IsNothing()) { | | 278 | + Local<Object> env_proxy; | | 279 | + CreateEnvProxyTemplate(isolate_, env_->isolate_data()); | | 280 | + if (!env_->env_proxy_template()->NewInstance(context()).ToLocal(&env_proxy) || | | 281 | + process_object()->Set(context(), env_string, env_proxy).IsNothing()) { | 281 | 282 | return MaybeLocal<Value>(); | 282 | 283 | } | 283 | 284 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/util.h +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -297,6 +297,9 @@ class KVStore { | 297 | 297 | virtual std::shared_ptr<KVStore> Clone(v8::Isolate* isolate) const; | 298 | 298 | virtual v8::Maybe<bool> AssignFromObject(v8::Local<v8::Context> context, | 299 | 299 | v8::Local<v8::Object> entries); | | 300 | + v8::Maybe<bool> AssignToObject(v8::Isolate* isolate, | | 301 | + v8::Local<v8::Context> context, | | 302 | + v8::Local<v8::Object> object); | 300 | 303 |
| 301 | 304 | static std::shared_ptr<KVStore> CreateMapKVStore(); | 302 | 305 | }; | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-process-env.js +8Lines changed: 8 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -105,6 +105,14 @@ if (common.isWindows) { | 105 | 105 | assert.ok(keys.length > 0); | 106 | 106 | } | 107 | 107 |
| | 108 | +// https://github.com/nodejs/node/issues/45380 | | 109 | +{ | | 110 | + const env = structuredClone(process.env); | | 111 | + // deepEqual(), not deepStrictEqual(), because of different prototypes. | | 112 | + // eslint-disable-next-line no-restricted-properties | | 113 | + assert.deepEqual(env, process.env); | | 114 | +} | | 115 | + | 108 | 116 | // Setting environment variables on Windows with empty names should not cause | 109 | 117 | // an assertion failure. | 110 | 118 | // https://github.com/nodejs/node/issues/32920 | |
|