From 729e2f242f8f11d7c34f1c22d1e522ccd1c086c1 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 13 Mar 2019 14:32:37 +0000 Subject: [PATCH] src: implement generic backend for process.env Allow a generic string-based backing store, with no significance to the remainder of the process, as a store for `process.env`. PR-URL: https://github.com/nodejs/node/pull/26544 Fixes: https://github.com/nodejs/node/issues/24947 Reviewed-By: Ruben Bridgewater Reviewed-By: Vse Mozhet Byt Reviewed-By: Yongsheng Zhang Reviewed-By: James M Snell Reviewed-By: Benjamin Gruenbaum Reviewed-By: Joyee Cheung Signed-off-by: Beth Griggs --- src/env.h | 6 +++ src/node_env_var.cc | 118 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/src/env.h b/src/env.h index be5970a8a72ea8..2d782634968493 100644 --- a/src/env.h +++ b/src/env.h @@ -543,6 +543,12 @@ class KVStore { v8::Local key) const = 0; virtual void Delete(v8::Isolate* isolate, v8::Local key) = 0; virtual v8::Local Enumerate(v8::Isolate* isolate) const = 0; + + virtual std::shared_ptr Clone(v8::Isolate* isolate) const; + virtual v8::Maybe AssignFromObject(v8::Local context, + v8::Local entries); + + static std::shared_ptr CreateGenericKVStore(); }; namespace per_process { diff --git a/src/node_env_var.cc b/src/node_env_var.cc index 6bd799e19f8fc8..2df7275b17520a 100644 --- a/src/node_env_var.cc +++ b/src/node_env_var.cc @@ -14,13 +14,17 @@ using v8::Array; using v8::Boolean; using v8::Context; using v8::EscapableHandleScope; +using v8::HandleScope; using v8::Integer; using v8::Isolate; +using v8::Just; using v8::Local; +using v8::Maybe; using v8::MaybeLocal; using v8::Name; using v8::NamedPropertyHandlerConfiguration; using v8::NewStringType; +using v8::Nothing; using v8::Object; using v8::ObjectTemplate; using v8::PropertyCallbackInfo; @@ -36,6 +40,24 @@ class RealEnvStore final : public KVStore { Local Enumerate(Isolate* isolate) const override; }; +class GenericKVStore final : public KVStore { + public: + Local Get(Isolate* isolate, Local key) const override; + void Set(Isolate* isolate, Local key, Local value) override; + int32_t Query(Isolate* isolate, Local key) const override; + void Delete(Isolate* isolate, Local key) override; + Local Enumerate(Isolate* isolate) const override; + + std::shared_ptr Clone(Isolate* isolate) const override; + + GenericKVStore() {} + GenericKVStore(const GenericKVStore& other) : map_(other.map_) {} + + private: + mutable Mutex mutex_; + std::unordered_map map_; +}; + namespace per_process { Mutex env_var_mutex; std::shared_ptr real_environment = std::make_shared(); @@ -181,6 +203,102 @@ Local RealEnvStore::Enumerate(Isolate* isolate) const { return Array::New(isolate, env_v.data(), env_v.size()); } +std::shared_ptr KVStore::Clone(v8::Isolate* isolate) const { + HandleScope handle_scope(isolate); + Local context = isolate->GetCurrentContext(); + + std::shared_ptr copy = KVStore::CreateGenericKVStore(); + Local keys = Enumerate(isolate); + uint32_t keys_length = keys->Length(); + for (uint32_t i = 0; i < keys_length; i++) { + Local key = keys->Get(context, i).ToLocalChecked(); + CHECK(key->IsString()); + copy->Set(isolate, key.As(), Get(isolate, key.As())); + } + return copy; +} + +Local GenericKVStore::Get(Isolate* isolate, Local key) const { + Mutex::ScopedLock lock(mutex_); + String::Utf8Value str(isolate, key); + auto it = map_.find(std::string(*str, str.length())); + if (it == map_.end()) return Local(); + return String::NewFromUtf8(isolate, it->second.data(), + NewStringType::kNormal, it->second.size()) + .ToLocalChecked(); +} + +void GenericKVStore::Set(Isolate* isolate, Local key, + Local value) { + Mutex::ScopedLock lock(mutex_); + String::Utf8Value key_str(isolate, key); + String::Utf8Value value_str(isolate, value); + if (*key_str != nullptr && *value_str != nullptr) { + map_[std::string(*key_str, key_str.length())] = + std::string(*value_str, value_str.length()); + } +} + +int32_t GenericKVStore::Query(Isolate* isolate, Local key) const { + Mutex::ScopedLock lock(mutex_); + String::Utf8Value str(isolate, key); + auto it = map_.find(std::string(*str, str.length())); + return it == map_.end() ? -1 : 0; +} + +void GenericKVStore::Delete(Isolate* isolate, Local key) { + Mutex::ScopedLock lock(mutex_); + String::Utf8Value str(isolate, key); + map_.erase(std::string(*str, str.length())); +} + +Local GenericKVStore::Enumerate(Isolate* isolate) const { + Mutex::ScopedLock lock(mutex_); + std::vector> values; + values.reserve(map_.size()); + for (const auto& pair : map_) { + values.emplace_back( + String::NewFromUtf8(isolate, pair.first.data(), + NewStringType::kNormal, pair.first.size()) + .ToLocalChecked()); + } + return Array::New(isolate, values.data(), values.size()); +} + +std::shared_ptr GenericKVStore::Clone(Isolate* isolate) const { + return std::make_shared(*this); +} + +std::shared_ptr KVStore::CreateGenericKVStore() { + return std::make_shared(); +} + +Maybe KVStore::AssignFromObject(Local context, + Local entries) { + Isolate* isolate = context->GetIsolate(); + HandleScope handle_scope(isolate); + Local keys; + if (!entries->GetOwnPropertyNames(context).ToLocal(&keys)) + return Nothing(); + uint32_t keys_length = keys->Length(); + for (uint32_t i = 0; i < keys_length; i++) { + Local key; + if (!keys->Get(context, i).ToLocal(&key)) + return Nothing(); + if (!key->IsString()) continue; + + Local value; + Local value_string; + if (!entries->Get(context, key.As()).ToLocal(&value) || + !value->ToString(context).ToLocal(&value_string)) { + return Nothing(); + } + + Set(isolate, key.As(), value_string); + } + return Just(true); +} + static void EnvGetter(Local property, const PropertyCallbackInfo& info) { Environment* env = Environment::GetCurrent(info);