diff options
author | Anna Henningsen <anna@addaleax.net> | 2019-03-13 15:24:27 +0300 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2019-03-31 00:25:33 +0300 |
commit | f5b5fe3937ec353e43d30ae976725e0773e14c6f (patch) | |
tree | 57e532e5d82dbf3c553c83bfaa41bb50c76c4b71 /src/node_env_var.cc | |
parent | e4e2b0ce134ce781847272ae0b4bb889e75f223f (diff) |
src: allow per-Environment set of env vars
Abstract the `process.env` backing mechanism in C++ to allow
different kinds of backing stores for `process.env` for different
Environments.
PR-URL: https://github.com/nodejs/node/pull/26544
Fixes: https://github.com/nodejs/node/issues/24947
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Diffstat (limited to 'src/node_env_var.cc')
-rw-r--r-- | src/node_env_var.cc | 202 |
1 files changed, 123 insertions, 79 deletions
diff --git a/src/node_env_var.cc b/src/node_env_var.cc index 7e4913702b8..6bd799e19f8 100644 --- a/src/node_env_var.cc +++ b/src/node_env_var.cc @@ -27,24 +27,29 @@ using v8::PropertyCallbackInfo; using v8::String; using v8::Value; +class RealEnvStore final : public KVStore { + public: + Local<String> Get(Isolate* isolate, Local<String> key) const override; + void Set(Isolate* isolate, Local<String> key, Local<String> value) override; + int32_t Query(Isolate* isolate, Local<String> key) const override; + void Delete(Isolate* isolate, Local<String> key) override; + Local<Array> Enumerate(Isolate* isolate) const override; +}; + namespace per_process { Mutex env_var_mutex; +std::shared_ptr<KVStore> real_environment = std::make_shared<RealEnvStore>(); } // namespace per_process -static void EnvGetter(Local<Name> property, - const PropertyCallbackInfo<Value>& info) { - Isolate* isolate = info.GetIsolate(); - if (property->IsSymbol()) { - return info.GetReturnValue().SetUndefined(); - } +Local<String> RealEnvStore::Get(Isolate* isolate, + Local<String> property) const { Mutex::ScopedLock lock(per_process::env_var_mutex); #ifdef __POSIX__ node::Utf8Value key(isolate, property); const char* val = getenv(*key); if (val) { - return info.GetReturnValue().Set( - String::NewFromUtf8(isolate, val, NewStringType::kNormal) - .ToLocalChecked()); + return String::NewFromUtf8(isolate, val, NewStringType::kNormal) + .ToLocalChecked(); } #else // _WIN32 node::TwoByteValue key(isolate, property); @@ -62,106 +67,72 @@ static void EnvGetter(Local<Name> property, isolate, two_byte_buffer, NewStringType::kNormal); if (rc.IsEmpty()) { isolate->ThrowException(ERR_STRING_TOO_LONG(isolate)); - return; + return Local<String>(); } - return info.GetReturnValue().Set(rc.ToLocalChecked()); + return rc.ToLocalChecked(); } #endif + return Local<String>(); } -static void EnvSetter(Local<Name> property, - Local<Value> value, - const PropertyCallbackInfo<Value>& info) { - Environment* env = Environment::GetCurrent(info); - // calling env->EmitProcessEnvWarning() sets a variable indicating that - // warnings have been emitted. It should be called last after other - // conditions leading to a warning have been met. - if (env->options()->pending_deprecation && !value->IsString() && - !value->IsNumber() && !value->IsBoolean() && - env->EmitProcessEnvWarning()) { - if (ProcessEmitDeprecationWarning( - env, - "Assigning any value other than a string, number, or boolean to a " - "process.env property is deprecated. Please make sure to convert " - "the " - "value to a string before setting process.env with it.", - "DEP0104") - .IsNothing()) - return; - } - +void RealEnvStore::Set(Isolate* isolate, + Local<String> property, + Local<String> value) { Mutex::ScopedLock lock(per_process::env_var_mutex); #ifdef __POSIX__ - node::Utf8Value key(info.GetIsolate(), property); - node::Utf8Value val(info.GetIsolate(), value); + node::Utf8Value key(isolate, property); + node::Utf8Value val(isolate, value); setenv(*key, *val, 1); #else // _WIN32 - node::TwoByteValue key(info.GetIsolate(), property); - node::TwoByteValue val(info.GetIsolate(), value); + node::TwoByteValue key(isolate, property); + node::TwoByteValue val(isolate, value); WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key); // Environment variables that start with '=' are read-only. if (key_ptr[0] != L'=') { SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val)); } #endif - // Whether it worked or not, always return value. - info.GetReturnValue().Set(value); } -static void EnvQuery(Local<Name> property, - const PropertyCallbackInfo<Integer>& info) { +int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const { Mutex::ScopedLock lock(per_process::env_var_mutex); - int32_t rc = -1; // Not found unless proven otherwise. - if (property->IsString()) { #ifdef __POSIX__ - node::Utf8Value key(info.GetIsolate(), property); - if (getenv(*key)) rc = 0; + node::Utf8Value key(isolate, property); + if (getenv(*key)) return 0; #else // _WIN32 - node::TwoByteValue key(info.GetIsolate(), property); - WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key); - SetLastError(ERROR_SUCCESS); - if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 || - GetLastError() == ERROR_SUCCESS) { - rc = 0; - if (key_ptr[0] == L'=') { - // Environment variables that start with '=' are hidden and read-only. - rc = static_cast<int32_t>(v8::ReadOnly) | + node::TwoByteValue key(isolate, property); + WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key); + SetLastError(ERROR_SUCCESS); + if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 || + GetLastError() == ERROR_SUCCESS) { + if (key_ptr[0] == L'=') { + // Environment variables that start with '=' are hidden and read-only. + return static_cast<int32_t>(v8::ReadOnly) | static_cast<int32_t>(v8::DontDelete) | static_cast<int32_t>(v8::DontEnum); - } } -#endif + return 0; } - if (rc != -1) info.GetReturnValue().Set(rc); +#endif + return -1; } -static void EnvDeleter(Local<Name> property, - const PropertyCallbackInfo<Boolean>& info) { +void RealEnvStore::Delete(Isolate* isolate, Local<String> property) { Mutex::ScopedLock lock(per_process::env_var_mutex); - if (property->IsString()) { #ifdef __POSIX__ - node::Utf8Value key(info.GetIsolate(), property); - unsetenv(*key); + node::Utf8Value key(isolate, property); + unsetenv(*key); #else - node::TwoByteValue key(info.GetIsolate(), property); - WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key); - SetEnvironmentVariableW(key_ptr, nullptr); + node::TwoByteValue key(isolate, property); + WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key); + SetEnvironmentVariableW(key_ptr, nullptr); #endif - } - - // process.env never has non-configurable properties, so always - // return true like the tc39 delete operator. - info.GetReturnValue().Set(true); } -static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) { - Environment* env = Environment::GetCurrent(info); - Isolate* isolate = env->isolate(); - +Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const { Mutex::ScopedLock lock(per_process::env_var_mutex); - Local<Array> envarr; - int env_size = 0; #ifdef __POSIX__ + int env_size = 0; while (environ[env_size]) { env_size++; } @@ -177,7 +148,8 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) { #else // _WIN32 std::vector<Local<Value>> env_v; WCHAR* environment = GetEnvironmentStringsW(); - if (environment == nullptr) return; // This should not happen. + if (environment == nullptr) + return Array::New(isolate); // This should not happen. WCHAR* p = environment; while (*p) { WCHAR* s; @@ -198,7 +170,7 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) { if (rc.IsEmpty()) { isolate->ThrowException(ERR_STRING_TOO_LONG(isolate)); FreeEnvironmentStringsW(environment); - return; + return Local<Array>(); } env_v.push_back(rc.ToLocalChecked()); p = s + wcslen(s) + 1; @@ -206,8 +178,80 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) { FreeEnvironmentStringsW(environment); #endif - envarr = Array::New(isolate, env_v.data(), env_v.size()); - info.GetReturnValue().Set(envarr); + return Array::New(isolate, env_v.data(), env_v.size()); +} + +static void EnvGetter(Local<Name> property, + const PropertyCallbackInfo<Value>& info) { + Environment* env = Environment::GetCurrent(info); + if (property->IsSymbol()) { + return info.GetReturnValue().SetUndefined(); + } + CHECK(property->IsString()); + info.GetReturnValue().Set( + env->envvars()->Get(env->isolate(), property.As<String>())); +} + +static void EnvSetter(Local<Name> property, + Local<Value> value, + const PropertyCallbackInfo<Value>& info) { + Environment* env = Environment::GetCurrent(info); + // calling env->EmitProcessEnvWarning() sets a variable indicating that + // warnings have been emitted. It should be called last after other + // conditions leading to a warning have been met. + if (env->options()->pending_deprecation && !value->IsString() && + !value->IsNumber() && !value->IsBoolean() && + env->EmitProcessEnvWarning()) { + if (ProcessEmitDeprecationWarning( + env, + "Assigning any value other than a string, number, or boolean to a " + "process.env property is deprecated. Please make sure to convert " + "the " + "value to a string before setting process.env with it.", + "DEP0104") + .IsNothing()) + return; + } + + Local<String> key; + Local<String> value_string; + if (!property->ToString(env->context()).ToLocal(&key) || + !value->ToString(env->context()).ToLocal(&value_string)) { + return; + } + + env->envvars()->Set(env->isolate(), key, value_string); + + // Whether it worked or not, always return value. + info.GetReturnValue().Set(value); +} + +static void EnvQuery(Local<Name> property, + const PropertyCallbackInfo<Integer>& info) { + Environment* env = Environment::GetCurrent(info); + if (property->IsString()) { + int32_t rc = env->envvars()->Query(env->isolate(), property.As<String>()); + if (rc != -1) info.GetReturnValue().Set(rc); + } +} + +static void EnvDeleter(Local<Name> property, + const PropertyCallbackInfo<Boolean>& info) { + Environment* env = Environment::GetCurrent(info); + if (property->IsString()) { + env->envvars()->Delete(env->isolate(), property.As<String>()); + } + + // process.env never has non-configurable properties, so always + // return true like the tc39 delete operator. + info.GetReturnValue().Set(true); +} + +static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) { + Environment* env = Environment::GetCurrent(info); + + info.GetReturnValue().Set( + env->envvars()->Enumerate(env->isolate())); } MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context, |