Skip to content

Commit 3f82144

Browse files
joyeecheungMylesBorins
authored andcommittedDec 25, 2018
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: #25067 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 9ad6bc2 commit 3f82144

File tree

5 files changed

+223
-217
lines changed

5 files changed

+223
-217
lines changed
 

‎node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@
350350
'src/node_contextify.cc',
351351
'src/node_domain.cc',
352352
'src/node_encoding.cc',
353+
'src/node_env_var.cc',
353354
'src/node_errors.cc',
354355
'src/node_file.cc',
355356
'src/node_http_parser_llhttp.cc',

‎src/node.cc

+5-16
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ using v8::Maybe;
133133
using v8::MaybeLocal;
134134
using v8::Message;
135135
using v8::MicrotasksPolicy;
136-
using v8::NamedPropertyHandlerConfiguration;
137136
using v8::NewStringType;
138137
using v8::None;
139138
using v8::Nothing;
@@ -1024,21 +1023,11 @@ void SetupProcessObject(Environment* env,
10241023
exec_arguments).FromJust();
10251024

10261025
// create process.env
1027-
Local<ObjectTemplate> process_env_template =
1028-
ObjectTemplate::New(env->isolate());
1029-
process_env_template->SetHandler(NamedPropertyHandlerConfiguration(
1030-
EnvGetter,
1031-
EnvSetter,
1032-
EnvQuery,
1033-
EnvDeleter,
1034-
EnvEnumerator,
1035-
env->as_external()));
1036-
1037-
Local<Object> process_env =
1038-
process_env_template->NewInstance(env->context()).ToLocalChecked();
1039-
process->Set(env->context(),
1040-
FIXED_ONE_BYTE_STRING(env->isolate(), "env"),
1041-
process_env).FromJust();
1026+
process
1027+
->Set(env->context(),
1028+
FIXED_ONE_BYTE_STRING(env->isolate(), "env"),
1029+
CreateEnvVarProxy(context, isolate, env->as_external()))
1030+
.FromJust();
10421031

10431032
READONLY_PROPERTY(process, "pid",
10441033
Integer::New(env->isolate(), uv_os_getpid()));

‎src/node_env_var.cc

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
#include "node_internals.h"
2+
#include "node_errors.h"
3+
4+
#ifdef __APPLE__
5+
#include <crt_externs.h>
6+
#define environ (*_NSGetEnviron())
7+
#elif !defined(_MSC_VER)
8+
extern char** environ;
9+
#endif
10+
11+
namespace node {
12+
using v8::Array;
13+
using v8::Boolean;
14+
using v8::Context;
15+
using v8::EscapableHandleScope;
16+
using v8::Integer;
17+
using v8::Isolate;
18+
using v8::Local;
19+
using v8::Name;
20+
using v8::NamedPropertyHandlerConfiguration;
21+
using v8::NewStringType;
22+
using v8::Object;
23+
using v8::ObjectTemplate;
24+
using v8::PropertyCallbackInfo;
25+
using v8::String;
26+
using v8::Value;
27+
28+
static void EnvGetter(Local<Name> property,
29+
const PropertyCallbackInfo<Value>& info) {
30+
Isolate* isolate = info.GetIsolate();
31+
if (property->IsSymbol()) {
32+
return info.GetReturnValue().SetUndefined();
33+
}
34+
Mutex::ScopedLock lock(environ_mutex);
35+
#ifdef __POSIX__
36+
node::Utf8Value key(isolate, property);
37+
const char* val = getenv(*key);
38+
if (val) {
39+
return info.GetReturnValue().Set(
40+
String::NewFromUtf8(isolate, val, NewStringType::kNormal)
41+
.ToLocalChecked());
42+
}
43+
#else // _WIN32
44+
node::TwoByteValue key(isolate, property);
45+
WCHAR buffer[32767]; // The maximum size allowed for environment variables.
46+
SetLastError(ERROR_SUCCESS);
47+
DWORD result = GetEnvironmentVariableW(
48+
reinterpret_cast<WCHAR*>(*key), buffer, arraysize(buffer));
49+
// If result >= sizeof buffer the buffer was too small. That should never
50+
// happen. If result == 0 and result != ERROR_SUCCESS the variable was not
51+
// found.
52+
if ((result > 0 || GetLastError() == ERROR_SUCCESS) &&
53+
result < arraysize(buffer)) {
54+
const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(buffer);
55+
v8::MaybeLocal<String> rc = String::NewFromTwoByte(
56+
isolate, two_byte_buffer, NewStringType::kNormal);
57+
if (rc.IsEmpty()) {
58+
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
59+
return;
60+
}
61+
return info.GetReturnValue().Set(rc.ToLocalChecked());
62+
}
63+
#endif
64+
}
65+
66+
static void EnvSetter(Local<Name> property,
67+
Local<Value> value,
68+
const PropertyCallbackInfo<Value>& info) {
69+
Environment* env = Environment::GetCurrent(info);
70+
if (env->options()->pending_deprecation && env->EmitProcessEnvWarning() &&
71+
!value->IsString() && !value->IsNumber() && !value->IsBoolean()) {
72+
if (ProcessEmitDeprecationWarning(
73+
env,
74+
"Assigning any value other than a string, number, or boolean to a "
75+
"process.env property is deprecated. Please make sure to convert "
76+
"the "
77+
"value to a string before setting process.env with it.",
78+
"DEP0104")
79+
.IsNothing())
80+
return;
81+
}
82+
83+
Mutex::ScopedLock lock(environ_mutex);
84+
#ifdef __POSIX__
85+
node::Utf8Value key(info.GetIsolate(), property);
86+
node::Utf8Value val(info.GetIsolate(), value);
87+
setenv(*key, *val, 1);
88+
#else // _WIN32
89+
node::TwoByteValue key(info.GetIsolate(), property);
90+
node::TwoByteValue val(info.GetIsolate(), value);
91+
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
92+
// Environment variables that start with '=' are read-only.
93+
if (key_ptr[0] != L'=') {
94+
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
95+
}
96+
#endif
97+
// Whether it worked or not, always return value.
98+
info.GetReturnValue().Set(value);
99+
}
100+
101+
static void EnvQuery(Local<Name> property,
102+
const PropertyCallbackInfo<Integer>& info) {
103+
Mutex::ScopedLock lock(environ_mutex);
104+
int32_t rc = -1; // Not found unless proven otherwise.
105+
if (property->IsString()) {
106+
#ifdef __POSIX__
107+
node::Utf8Value key(info.GetIsolate(), property);
108+
if (getenv(*key)) rc = 0;
109+
#else // _WIN32
110+
node::TwoByteValue key(info.GetIsolate(), property);
111+
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
112+
SetLastError(ERROR_SUCCESS);
113+
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
114+
GetLastError() == ERROR_SUCCESS) {
115+
rc = 0;
116+
if (key_ptr[0] == L'=') {
117+
// Environment variables that start with '=' are hidden and read-only.
118+
rc = static_cast<int32_t>(v8::ReadOnly) |
119+
static_cast<int32_t>(v8::DontDelete) |
120+
static_cast<int32_t>(v8::DontEnum);
121+
}
122+
}
123+
#endif
124+
}
125+
if (rc != -1) info.GetReturnValue().Set(rc);
126+
}
127+
128+
static void EnvDeleter(Local<Name> property,
129+
const PropertyCallbackInfo<Boolean>& info) {
130+
Mutex::ScopedLock lock(environ_mutex);
131+
if (property->IsString()) {
132+
#ifdef __POSIX__
133+
node::Utf8Value key(info.GetIsolate(), property);
134+
unsetenv(*key);
135+
#else
136+
node::TwoByteValue key(info.GetIsolate(), property);
137+
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
138+
SetEnvironmentVariableW(key_ptr, nullptr);
139+
#endif
140+
}
141+
142+
// process.env never has non-configurable properties, so always
143+
// return true like the tc39 delete operator.
144+
info.GetReturnValue().Set(true);
145+
}
146+
147+
static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
148+
Environment* env = Environment::GetCurrent(info);
149+
Isolate* isolate = env->isolate();
150+
151+
Mutex::ScopedLock lock(environ_mutex);
152+
Local<Array> envarr;
153+
int env_size = 0;
154+
#ifdef __POSIX__
155+
while (environ[env_size]) {
156+
env_size++;
157+
}
158+
std::vector<Local<Value>> env_v(env_size);
159+
160+
for (int i = 0; i < env_size; ++i) {
161+
const char* var = environ[i];
162+
const char* s = strchr(var, '=');
163+
const int length = s ? s - var : strlen(var);
164+
env_v[i] = String::NewFromUtf8(isolate, var, NewStringType::kNormal, length)
165+
.ToLocalChecked();
166+
}
167+
#else // _WIN32
168+
std::vector<Local<Value>> env_v;
169+
WCHAR* environment = GetEnvironmentStringsW();
170+
if (environment == nullptr) return; // This should not happen.
171+
WCHAR* p = environment;
172+
while (*p) {
173+
WCHAR* s;
174+
if (*p == L'=') {
175+
// If the key starts with '=' it is a hidden environment variable.
176+
p += wcslen(p) + 1;
177+
continue;
178+
} else {
179+
s = wcschr(p, L'=');
180+
}
181+
if (!s) {
182+
s = p + wcslen(p);
183+
}
184+
const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(p);
185+
const size_t two_byte_buffer_len = s - p;
186+
v8::MaybeLocal<String> rc = String::NewFromTwoByte(
187+
isolate, two_byte_buffer, NewStringType::kNormal, two_byte_buffer_len);
188+
if (rc.IsEmpty()) {
189+
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
190+
FreeEnvironmentStringsW(environment);
191+
return;
192+
}
193+
env_v.push_back(rc.ToLocalChecked());
194+
p = s + wcslen(s) + 1;
195+
}
196+
FreeEnvironmentStringsW(environment);
197+
#endif
198+
199+
envarr = Array::New(isolate, env_v.data(), env_v.size());
200+
info.GetReturnValue().Set(envarr);
201+
}
202+
203+
Local<Object> CreateEnvVarProxy(Local<Context> context,
204+
Isolate* isolate,
205+
Local<Value> data) {
206+
EscapableHandleScope scope(isolate);
207+
Local<ObjectTemplate> env_proxy_template = ObjectTemplate::New(isolate);
208+
env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration(
209+
EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, data));
210+
Local<Object> env_proxy =
211+
env_proxy_template->NewInstance(context).ToLocalChecked();
212+
return scope.Escape(env_proxy);
213+
}
214+
} // namespace node

‎src/node_internals.h

+3-10
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ void RegisterSignalHandler(int signal,
128128
#endif
129129

130130
bool SafeGetenv(const char* key, std::string* text);
131+
v8::Local<v8::Object> CreateEnvVarProxy(v8::Local<v8::Context> context,
132+
v8::Isolate* isolate,
133+
v8::Local<v8::Value> data);
131134

132135
std::string GetHumanReadableProcessName();
133136
void GetHumanReadableProcessName(char (*name)[1024]);
@@ -716,16 +719,6 @@ void StopProfilerIdleNotifier(const v8::FunctionCallbackInfo<v8::Value>& args);
716719
void Umask(const v8::FunctionCallbackInfo<v8::Value>& args);
717720
void Uptime(const v8::FunctionCallbackInfo<v8::Value>& args);
718721

719-
void EnvDeleter(v8::Local<v8::Name> property,
720-
const v8::PropertyCallbackInfo<v8::Boolean>& info);
721-
void EnvGetter(v8::Local<v8::Name> property,
722-
const v8::PropertyCallbackInfo<v8::Value>& info);
723-
void EnvSetter(v8::Local<v8::Name> property,
724-
v8::Local<v8::Value> value,
725-
const v8::PropertyCallbackInfo<v8::Value>& info);
726-
void EnvQuery(v8::Local<v8::Name> property,
727-
const v8::PropertyCallbackInfo<v8::Integer>& info);
728-
void EnvEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info);
729722
void DebugPortGetter(v8::Local<v8::Name> property,
730723
const v8::PropertyCallbackInfo<v8::Value>& info);
731724
void DebugPortSetter(v8::Local<v8::Name> property,

‎src/node_process.cc

-191
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,11 @@ typedef int mode_t;
3232
#include <grp.h> // getgrnam()
3333
#endif
3434

35-
#ifdef __APPLE__
36-
#include <crt_externs.h>
37-
#define environ (*_NSGetEnviron())
38-
#elif !defined(_MSC_VER)
39-
extern char **environ;
40-
#endif
41-
4235
namespace node {
4336

4437
using v8::Array;
4538
using v8::ArrayBuffer;
4639
using v8::BigUint64Array;
47-
using v8::Boolean;
4840
using v8::Context;
4941
using v8::Float64Array;
5042
using v8::Function;
@@ -605,189 +597,6 @@ void ProcessTitleSetter(Local<Name> property,
605597
uv_set_process_title(*title);
606598
}
607599

608-
void EnvGetter(Local<Name> property,
609-
const PropertyCallbackInfo<Value>& info) {
610-
Isolate* isolate = info.GetIsolate();
611-
if (property->IsSymbol()) {
612-
return info.GetReturnValue().SetUndefined();
613-
}
614-
Mutex::ScopedLock lock(environ_mutex);
615-
#ifdef __POSIX__
616-
node::Utf8Value key(isolate, property);
617-
const char* val = getenv(*key);
618-
if (val) {
619-
return info.GetReturnValue().Set(String::NewFromUtf8(isolate, val,
620-
NewStringType::kNormal).ToLocalChecked());
621-
}
622-
#else // _WIN32
623-
node::TwoByteValue key(isolate, property);
624-
WCHAR buffer[32767]; // The maximum size allowed for environment variables.
625-
SetLastError(ERROR_SUCCESS);
626-
DWORD result = GetEnvironmentVariableW(reinterpret_cast<WCHAR*>(*key),
627-
buffer,
628-
arraysize(buffer));
629-
// If result >= sizeof buffer the buffer was too small. That should never
630-
// happen. If result == 0 and result != ERROR_SUCCESS the variable was not
631-
// found.
632-
if ((result > 0 || GetLastError() == ERROR_SUCCESS) &&
633-
result < arraysize(buffer)) {
634-
const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(buffer);
635-
v8::MaybeLocal<String> rc = String::NewFromTwoByte(
636-
isolate, two_byte_buffer, NewStringType::kNormal);
637-
if (rc.IsEmpty()) {
638-
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
639-
return;
640-
}
641-
return info.GetReturnValue().Set(rc.ToLocalChecked());
642-
}
643-
#endif
644-
}
645-
646-
647-
void EnvSetter(Local<Name> property,
648-
Local<Value> value,
649-
const PropertyCallbackInfo<Value>& info) {
650-
Environment* env = Environment::GetCurrent(info);
651-
if (env->options()->pending_deprecation && env->EmitProcessEnvWarning() &&
652-
!value->IsString() && !value->IsNumber() && !value->IsBoolean()) {
653-
if (ProcessEmitDeprecationWarning(
654-
env,
655-
"Assigning any value other than a string, number, or boolean to a "
656-
"process.env property is deprecated. Please make sure to convert the "
657-
"value to a string before setting process.env with it.",
658-
"DEP0104").IsNothing())
659-
return;
660-
}
661-
662-
Mutex::ScopedLock lock(environ_mutex);
663-
#ifdef __POSIX__
664-
node::Utf8Value key(info.GetIsolate(), property);
665-
node::Utf8Value val(info.GetIsolate(), value);
666-
setenv(*key, *val, 1);
667-
#else // _WIN32
668-
node::TwoByteValue key(info.GetIsolate(), property);
669-
node::TwoByteValue val(info.GetIsolate(), value);
670-
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
671-
// Environment variables that start with '=' are read-only.
672-
if (key_ptr[0] != L'=') {
673-
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
674-
}
675-
#endif
676-
// Whether it worked or not, always return value.
677-
info.GetReturnValue().Set(value);
678-
}
679-
680-
681-
void EnvQuery(Local<Name> property, const PropertyCallbackInfo<Integer>& info) {
682-
Mutex::ScopedLock lock(environ_mutex);
683-
int32_t rc = -1; // Not found unless proven otherwise.
684-
if (property->IsString()) {
685-
#ifdef __POSIX__
686-
node::Utf8Value key(info.GetIsolate(), property);
687-
if (getenv(*key))
688-
rc = 0;
689-
#else // _WIN32
690-
node::TwoByteValue key(info.GetIsolate(), property);
691-
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
692-
SetLastError(ERROR_SUCCESS);
693-
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
694-
GetLastError() == ERROR_SUCCESS) {
695-
rc = 0;
696-
if (key_ptr[0] == L'=') {
697-
// Environment variables that start with '=' are hidden and read-only.
698-
rc = static_cast<int32_t>(v8::ReadOnly) |
699-
static_cast<int32_t>(v8::DontDelete) |
700-
static_cast<int32_t>(v8::DontEnum);
701-
}
702-
}
703-
#endif
704-
}
705-
if (rc != -1)
706-
info.GetReturnValue().Set(rc);
707-
}
708-
709-
710-
void EnvDeleter(Local<Name> property,
711-
const PropertyCallbackInfo<Boolean>& info) {
712-
Mutex::ScopedLock lock(environ_mutex);
713-
if (property->IsString()) {
714-
#ifdef __POSIX__
715-
node::Utf8Value key(info.GetIsolate(), property);
716-
unsetenv(*key);
717-
#else
718-
node::TwoByteValue key(info.GetIsolate(), property);
719-
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
720-
SetEnvironmentVariableW(key_ptr, nullptr);
721-
#endif
722-
}
723-
724-
// process.env never has non-configurable properties, so always
725-
// return true like the tc39 delete operator.
726-
info.GetReturnValue().Set(true);
727-
}
728-
729-
730-
void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
731-
Environment* env = Environment::GetCurrent(info);
732-
Isolate* isolate = env->isolate();
733-
734-
Mutex::ScopedLock lock(environ_mutex);
735-
Local<Array> envarr;
736-
int env_size = 0;
737-
#ifdef __POSIX__
738-
while (environ[env_size]) {
739-
env_size++;
740-
}
741-
std::vector<Local<Value>> env_v(env_size);
742-
743-
for (int i = 0; i < env_size; ++i) {
744-
const char* var = environ[i];
745-
const char* s = strchr(var, '=');
746-
const int length = s ? s - var : strlen(var);
747-
env_v[i] =
748-
String::NewFromUtf8(isolate, var, NewStringType::kNormal, length)
749-
.ToLocalChecked();
750-
}
751-
#else // _WIN32
752-
std::vector<Local<Value>> env_v;
753-
WCHAR* environment = GetEnvironmentStringsW();
754-
if (environment == nullptr)
755-
return; // This should not happen.
756-
WCHAR* p = environment;
757-
while (*p) {
758-
WCHAR* s;
759-
if (*p == L'=') {
760-
// If the key starts with '=' it is a hidden environment variable.
761-
p += wcslen(p) + 1;
762-
continue;
763-
} else {
764-
s = wcschr(p, L'=');
765-
}
766-
if (!s) {
767-
s = p + wcslen(p);
768-
}
769-
const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(p);
770-
const size_t two_byte_buffer_len = s - p;
771-
v8::MaybeLocal<String> rc =
772-
String::NewFromTwoByte(isolate,
773-
two_byte_buffer,
774-
NewStringType::kNormal,
775-
two_byte_buffer_len);
776-
if (rc.IsEmpty()) {
777-
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
778-
FreeEnvironmentStringsW(environment);
779-
return;
780-
}
781-
env_v.push_back(rc.ToLocalChecked());
782-
p = s + wcslen(s) + 1;
783-
}
784-
FreeEnvironmentStringsW(environment);
785-
#endif
786-
787-
envarr = Array::New(isolate, env_v.data(), env_v.size());
788-
info.GetReturnValue().Set(envarr);
789-
}
790-
791600
void GetParentProcessId(Local<Name> property,
792601
const PropertyCallbackInfo<Value>& info) {
793602
info.GetReturnValue().Set(uv_os_getppid());

0 commit comments

Comments
 (0)
Please sign in to comment.