summaryrefslogtreecommitdiff
path: root/src/node_env_var.cc
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2018-12-16 01:32:39 +0800
committerRich Trott <rtrott@gmail.com>2018-12-17 16:21:25 -0800
commit19a920564585f1c223fb137408d615932609da0b (patch)
tree36caaa8ccf156b25a98db745eee542c0629ac220 /src/node_env_var.cc
parent47ecf2060343705a26eaa7f7d0be242cb6d84cf8 (diff)
downloadandroid-node-v8-19a920564585f1c223fb137408d615932609da0b.tar.gz
android-node-v8-19a920564585f1c223fb137408d615932609da0b.tar.bz2
android-node-v8-19a920564585f1c223fb137408d615932609da0b.zip
process: move environment variable proxy code into node_env_var.cc
Instead of exposing all the NamedPropertyHandlerConfiguration() parameters in node_internals, simply expose a CreateEnvVarProxy() method that returns a Local<Value> that implements process.env, and mark all the property handlers static in node_env_var.cc. This makes the code more encapsulated. PR-URL: https://github.com/nodejs/node/pull/25067 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'src/node_env_var.cc')
-rw-r--r--src/node_env_var.cc214
1 files changed, 214 insertions, 0 deletions
diff --git a/src/node_env_var.cc b/src/node_env_var.cc
new file mode 100644
index 0000000000..de0991bcab
--- /dev/null
+++ b/src/node_env_var.cc
@@ -0,0 +1,214 @@
+#include "node_internals.h"
+#include "node_errors.h"
+
+#ifdef __APPLE__
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+#elif !defined(_MSC_VER)
+extern char** environ;
+#endif
+
+namespace node {
+using v8::Array;
+using v8::Boolean;
+using v8::Context;
+using v8::EscapableHandleScope;
+using v8::Integer;
+using v8::Isolate;
+using v8::Local;
+using v8::Name;
+using v8::NamedPropertyHandlerConfiguration;
+using v8::NewStringType;
+using v8::Object;
+using v8::ObjectTemplate;
+using v8::PropertyCallbackInfo;
+using v8::String;
+using v8::Value;
+
+static void EnvGetter(Local<Name> property,
+ const PropertyCallbackInfo<Value>& info) {
+ Isolate* isolate = info.GetIsolate();
+ if (property->IsSymbol()) {
+ return info.GetReturnValue().SetUndefined();
+ }
+ Mutex::ScopedLock lock(environ_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());
+ }
+#else // _WIN32
+ node::TwoByteValue key(isolate, property);
+ WCHAR buffer[32767]; // The maximum size allowed for environment variables.
+ SetLastError(ERROR_SUCCESS);
+ DWORD result = GetEnvironmentVariableW(
+ reinterpret_cast<WCHAR*>(*key), buffer, arraysize(buffer));
+ // If result >= sizeof buffer the buffer was too small. That should never
+ // happen. If result == 0 and result != ERROR_SUCCESS the variable was not
+ // found.
+ if ((result > 0 || GetLastError() == ERROR_SUCCESS) &&
+ result < arraysize(buffer)) {
+ const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(buffer);
+ v8::MaybeLocal<String> rc = String::NewFromTwoByte(
+ isolate, two_byte_buffer, NewStringType::kNormal);
+ if (rc.IsEmpty()) {
+ isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
+ return;
+ }
+ return info.GetReturnValue().Set(rc.ToLocalChecked());
+ }
+#endif
+}
+
+static void EnvSetter(Local<Name> property,
+ Local<Value> value,
+ const PropertyCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ if (env->options()->pending_deprecation && env->EmitProcessEnvWarning() &&
+ !value->IsString() && !value->IsNumber() && !value->IsBoolean()) {
+ 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;
+ }
+
+ Mutex::ScopedLock lock(environ_mutex);
+#ifdef __POSIX__
+ node::Utf8Value key(info.GetIsolate(), property);
+ node::Utf8Value val(info.GetIsolate(), value);
+ setenv(*key, *val, 1);
+#else // _WIN32
+ node::TwoByteValue key(info.GetIsolate(), property);
+ node::TwoByteValue val(info.GetIsolate(), 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) {
+ Mutex::ScopedLock lock(environ_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;
+#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) |
+ static_cast<int32_t>(v8::DontDelete) |
+ static_cast<int32_t>(v8::DontEnum);
+ }
+ }
+#endif
+ }
+ if (rc != -1) info.GetReturnValue().Set(rc);
+}
+
+static void EnvDeleter(Local<Name> property,
+ const PropertyCallbackInfo<Boolean>& info) {
+ Mutex::ScopedLock lock(environ_mutex);
+ if (property->IsString()) {
+#ifdef __POSIX__
+ node::Utf8Value key(info.GetIsolate(), property);
+ unsetenv(*key);
+#else
+ node::TwoByteValue key(info.GetIsolate(), 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();
+
+ Mutex::ScopedLock lock(environ_mutex);
+ Local<Array> envarr;
+ int env_size = 0;
+#ifdef __POSIX__
+ while (environ[env_size]) {
+ env_size++;
+ }
+ std::vector<Local<Value>> env_v(env_size);
+
+ for (int i = 0; i < env_size; ++i) {
+ const char* var = environ[i];
+ const char* s = strchr(var, '=');
+ const int length = s ? s - var : strlen(var);
+ env_v[i] = String::NewFromUtf8(isolate, var, NewStringType::kNormal, length)
+ .ToLocalChecked();
+ }
+#else // _WIN32
+ std::vector<Local<Value>> env_v;
+ WCHAR* environment = GetEnvironmentStringsW();
+ if (environment == nullptr) return; // This should not happen.
+ WCHAR* p = environment;
+ while (*p) {
+ WCHAR* s;
+ if (*p == L'=') {
+ // If the key starts with '=' it is a hidden environment variable.
+ p += wcslen(p) + 1;
+ continue;
+ } else {
+ s = wcschr(p, L'=');
+ }
+ if (!s) {
+ s = p + wcslen(p);
+ }
+ const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(p);
+ const size_t two_byte_buffer_len = s - p;
+ v8::MaybeLocal<String> rc = String::NewFromTwoByte(
+ isolate, two_byte_buffer, NewStringType::kNormal, two_byte_buffer_len);
+ if (rc.IsEmpty()) {
+ isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
+ FreeEnvironmentStringsW(environment);
+ return;
+ }
+ env_v.push_back(rc.ToLocalChecked());
+ p = s + wcslen(s) + 1;
+ }
+ FreeEnvironmentStringsW(environment);
+#endif
+
+ envarr = Array::New(isolate, env_v.data(), env_v.size());
+ info.GetReturnValue().Set(envarr);
+}
+
+Local<Object> CreateEnvVarProxy(Local<Context> context,
+ Isolate* isolate,
+ Local<Value> data) {
+ EscapableHandleScope scope(isolate);
+ Local<ObjectTemplate> env_proxy_template = ObjectTemplate::New(isolate);
+ env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration(
+ EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, data));
+ Local<Object> env_proxy =
+ env_proxy_template->NewInstance(context).ToLocalChecked();
+ return scope.Escape(env_proxy);
+}
+} // namespace node