GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
// Copyright Joyent, Inc. and other Node contributors. |
||
2 |
// |
||
3 |
// Permission is hereby granted, free of charge, to any person obtaining a |
||
4 |
// copy of this software and associated documentation files (the |
||
5 |
// "Software"), to deal in the Software without restriction, including |
||
6 |
// without limitation the rights to use, copy, modify, merge, publish, |
||
7 |
// distribute, sublicense, and/or sell copies of the Software, and to permit |
||
8 |
// persons to whom the Software is furnished to do so, subject to the |
||
9 |
// following conditions: |
||
10 |
// |
||
11 |
// The above copyright notice and this permission notice shall be included |
||
12 |
// in all copies or substantial portions of the Software. |
||
13 |
// |
||
14 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||
15 |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
16 |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
||
17 |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||
18 |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||
19 |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
||
20 |
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
21 |
|||
22 |
#include "env-inl.h" |
||
23 |
#include "stream_base-inl.h" |
||
24 |
#include "stream_wrap.h" |
||
25 |
#include "util-inl.h" |
||
26 |
|||
27 |
#include <climits> |
||
28 |
#include <cstdlib> |
||
29 |
#include <cstring> |
||
30 |
|||
31 |
namespace node { |
||
32 |
|||
33 |
using v8::Array; |
||
34 |
using v8::Context; |
||
35 |
using v8::FunctionCallbackInfo; |
||
36 |
using v8::FunctionTemplate; |
||
37 |
using v8::HandleScope; |
||
38 |
using v8::Int32; |
||
39 |
using v8::Integer; |
||
40 |
using v8::Isolate; |
||
41 |
using v8::Local; |
||
42 |
using v8::Number; |
||
43 |
using v8::Object; |
||
44 |
using v8::String; |
||
45 |
using v8::Value; |
||
46 |
|||
47 |
namespace { |
||
48 |
|||
49 |
class ProcessWrap : public HandleWrap { |
||
50 |
public: |
||
51 |
5084 |
static void Initialize(Local<Object> target, |
|
52 |
Local<Value> unused, |
||
53 |
Local<Context> context, |
||
54 |
void* priv) { |
||
55 |
5084 |
Environment* env = Environment::GetCurrent(context); |
|
56 |
5084 |
Isolate* isolate = env->isolate(); |
|
57 |
5084 |
Local<FunctionTemplate> constructor = NewFunctionTemplate(isolate, New); |
|
58 |
10168 |
constructor->InstanceTemplate()->SetInternalFieldCount( |
|
59 |
ProcessWrap::kInternalFieldCount); |
||
60 |
|||
61 |
5084 |
constructor->Inherit(HandleWrap::GetConstructorTemplate(env)); |
|
62 |
|||
63 |
5084 |
SetProtoMethod(isolate, constructor, "spawn", Spawn); |
|
64 |
5084 |
SetProtoMethod(isolate, constructor, "kill", Kill); |
|
65 |
|||
66 |
5084 |
SetConstructorFunction(context, target, "Process", constructor); |
|
67 |
5084 |
} |
|
68 |
|||
69 |
1 |
SET_NO_MEMORY_INFO() |
|
70 |
9 |
SET_MEMORY_INFO_NAME(ProcessWrap) |
|
71 |
1 |
SET_SELF_SIZE(ProcessWrap) |
|
72 |
|||
73 |
private: |
||
74 |
2111 |
static void New(const FunctionCallbackInfo<Value>& args) { |
|
75 |
// This constructor should not be exposed to public javascript. |
||
76 |
// Therefore we assert that we are not trying to call this as a |
||
77 |
// normal function. |
||
78 |
✗✓ | 2111 |
CHECK(args.IsConstructCall()); |
79 |
2111 |
Environment* env = Environment::GetCurrent(args); |
|
80 |
2111 |
new ProcessWrap(env, args.This()); |
|
81 |
2111 |
} |
|
82 |
|||
83 |
2111 |
ProcessWrap(Environment* env, Local<Object> object) |
|
84 |
2111 |
: HandleWrap(env, |
|
85 |
object, |
||
86 |
2111 |
reinterpret_cast<uv_handle_t*>(&process_), |
|
87 |
2111 |
AsyncWrap::PROVIDER_PROCESSWRAP) { |
|
88 |
2111 |
MarkAsUninitialized(); |
|
89 |
2111 |
} |
|
90 |
|||
91 |
4563 |
static uv_stream_t* StreamForWrap(Environment* env, Local<Object> stdio) { |
|
92 |
4563 |
Local<String> handle_key = env->handle_string(); |
|
93 |
// This property has always been set by JS land if we are in this code path. |
||
94 |
Local<Object> handle = |
||
95 |
9126 |
stdio->Get(env->context(), handle_key).ToLocalChecked().As<Object>(); |
|
96 |
|||
97 |
4563 |
uv_stream_t* stream = LibuvStreamWrap::From(env, handle)->stream(); |
|
98 |
✗✓ | 4563 |
CHECK_NOT_NULL(stream); |
99 |
4563 |
return stream; |
|
100 |
} |
||
101 |
|||
102 |
2101 |
static void ParseStdioOptions(Environment* env, |
|
103 |
Local<Object> js_options, |
||
104 |
uv_process_options_t* options) { |
||
105 |
2101 |
Local<Context> context = env->context(); |
|
106 |
2101 |
Local<String> stdio_key = env->stdio_string(); |
|
107 |
Local<Array> stdios = |
||
108 |
6303 |
js_options->Get(context, stdio_key).ToLocalChecked().As<Array>(); |
|
109 |
|||
110 |
2101 |
uint32_t len = stdios->Length(); |
|
111 |
✓✗ | 2101 |
options->stdio = new uv_stdio_container_t[len]; |
112 |
2101 |
options->stdio_count = len; |
|
113 |
|||
114 |
✓✓ | 8959 |
for (uint32_t i = 0; i < len; i++) { |
115 |
Local<Object> stdio = |
||
116 |
20574 |
stdios->Get(context, i).ToLocalChecked().As<Object>(); |
|
117 |
Local<Value> type = |
||
118 |
20574 |
stdio->Get(context, env->type_string()).ToLocalChecked(); |
|
119 |
|||
120 |
✓✓ | 13716 |
if (type->StrictEquals(env->ignore_string())) { |
121 |
54 |
options->stdio[i].flags = UV_IGNORE; |
|
122 |
✓✓ | 13608 |
} else if (type->StrictEquals(env->pipe_string())) { |
123 |
4555 |
options->stdio[i].flags = static_cast<uv_stdio_flags>( |
|
124 |
UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE); |
||
125 |
4555 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
126 |
✓✓ | 4498 |
} else if (type->StrictEquals(env->overlapped_string())) { |
127 |
1 |
options->stdio[i].flags = static_cast<uv_stdio_flags>( |
|
128 |
UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE | |
||
129 |
UV_OVERLAPPED_PIPE); |
||
130 |
1 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
131 |
✓✓ | 4496 |
} else if (type->StrictEquals(env->wrap_string())) { |
132 |
7 |
options->stdio[i].flags = UV_INHERIT_STREAM; |
|
133 |
7 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
134 |
} else { |
||
135 |
2241 |
Local<String> fd_key = env->fd_string(); |
|
136 |
4482 |
Local<Value> fd_value = stdio->Get(context, fd_key).ToLocalChecked(); |
|
137 |
✗✓ | 2241 |
CHECK(fd_value->IsNumber()); |
138 |
2241 |
int fd = static_cast<int>(fd_value.As<Integer>()->Value()); |
|
139 |
2241 |
options->stdio[i].flags = UV_INHERIT_FD; |
|
140 |
2241 |
options->stdio[i].data.fd = fd; |
|
141 |
} |
||
142 |
} |
||
143 |
2101 |
} |
|
144 |
|||
145 |
2101 |
static void Spawn(const FunctionCallbackInfo<Value>& args) { |
|
146 |
2101 |
Environment* env = Environment::GetCurrent(args); |
|
147 |
2101 |
Local<Context> context = env->context(); |
|
148 |
ProcessWrap* wrap; |
||
149 |
✗✓ | 2101 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
150 |
|||
151 |
Local<Object> js_options = |
||
152 |
2101 |
args[0]->ToObject(env->context()).ToLocalChecked(); |
|
153 |
|||
154 |
uv_process_options_t options; |
||
155 |
2101 |
memset(&options, 0, sizeof(uv_process_options_t)); |
|
156 |
|||
157 |
2101 |
options.exit_cb = OnExit; |
|
158 |
|||
159 |
// options.uid |
||
160 |
Local<Value> uid_v = |
||
161 |
6303 |
js_options->Get(context, env->uid_string()).ToLocalChecked(); |
|
162 |
✓✓✓✗ ✓✓ |
4204 |
if (!uid_v->IsUndefined() && !uid_v->IsNull()) { |
163 |
✗✓ | 1 |
CHECK(uid_v->IsInt32()); |
164 |
1 |
const int32_t uid = uid_v.As<Int32>()->Value(); |
|
165 |
1 |
options.flags |= UV_PROCESS_SETUID; |
|
166 |
1 |
options.uid = static_cast<uv_uid_t>(uid); |
|
167 |
} |
||
168 |
|||
169 |
// options.gid |
||
170 |
Local<Value> gid_v = |
||
171 |
6303 |
js_options->Get(context, env->gid_string()).ToLocalChecked(); |
|
172 |
✓✓✓✗ ✓✓ |
4204 |
if (!gid_v->IsUndefined() && !gid_v->IsNull()) { |
173 |
✗✓ | 1 |
CHECK(gid_v->IsInt32()); |
174 |
1 |
const int32_t gid = gid_v.As<Int32>()->Value(); |
|
175 |
1 |
options.flags |= UV_PROCESS_SETGID; |
|
176 |
1 |
options.gid = static_cast<uv_gid_t>(gid); |
|
177 |
} |
||
178 |
|||
179 |
// TODO(bnoordhuis) is this possible to do without mallocing ? |
||
180 |
|||
181 |
// options.file |
||
182 |
Local<Value> file_v = |
||
183 |
6303 |
js_options->Get(context, env->file_string()).ToLocalChecked(); |
|
184 |
✗✓ | 4202 |
CHECK(file_v->IsString()); |
185 |
4202 |
node::Utf8Value file(env->isolate(), file_v); |
|
186 |
2101 |
options.file = *file; |
|
187 |
|||
188 |
// options.args |
||
189 |
Local<Value> argv_v = |
||
190 |
6303 |
js_options->Get(context, env->args_string()).ToLocalChecked(); |
|
191 |
✓✗✓✗ ✓✗ |
4202 |
if (!argv_v.IsEmpty() && argv_v->IsArray()) { |
192 |
2101 |
Local<Array> js_argv = argv_v.As<Array>(); |
|
193 |
2101 |
int argc = js_argv->Length(); |
|
194 |
✗✓ | 2101 |
CHECK_LT(argc, INT_MAX); // Check for overflow. |
195 |
|||
196 |
// Heap allocate to detect errors. +1 is for nullptr. |
||
197 |
✓✗ | 2101 |
options.args = new char*[argc + 1]; |
198 |
✓✓ | 11146 |
for (int i = 0; i < argc; i++) { |
199 |
node::Utf8Value arg(env->isolate(), |
||
200 |
27135 |
js_argv->Get(context, i).ToLocalChecked()); |
|
201 |
9045 |
options.args[i] = strdup(*arg); |
|
202 |
✗✓ | 9045 |
CHECK_NOT_NULL(options.args[i]); |
203 |
} |
||
204 |
2101 |
options.args[argc] = nullptr; |
|
205 |
} |
||
206 |
|||
207 |
// options.cwd |
||
208 |
Local<Value> cwd_v = |
||
209 |
6303 |
js_options->Get(context, env->cwd_string()).ToLocalChecked(); |
|
210 |
node::Utf8Value cwd(env->isolate(), |
||
211 |
✓✓ | 4202 |
cwd_v->IsString() ? cwd_v : Local<Value>()); |
212 |
✓✓ | 2101 |
if (cwd.length() > 0) { |
213 |
115 |
options.cwd = *cwd; |
|
214 |
} |
||
215 |
|||
216 |
// options.env |
||
217 |
Local<Value> env_v = |
||
218 |
6303 |
js_options->Get(context, env->env_pairs_string()).ToLocalChecked(); |
|
219 |
✓✗✓✓ ✓✓ |
4202 |
if (!env_v.IsEmpty() && env_v->IsArray()) { |
220 |
2100 |
Local<Array> env_opt = env_v.As<Array>(); |
|
221 |
2100 |
int envc = env_opt->Length(); |
|
222 |
✗✓ | 2100 |
CHECK_LT(envc, INT_MAX); // Check for overflow. |
223 |
✓✗ | 2100 |
options.env = new char*[envc + 1]; // Heap allocated to detect errors. |
224 |
✓✓ | 163223 |
for (int i = 0; i < envc; i++) { |
225 |
node::Utf8Value pair(env->isolate(), |
||
226 |
483369 |
env_opt->Get(context, i).ToLocalChecked()); |
|
227 |
161123 |
options.env[i] = strdup(*pair); |
|
228 |
✗✓ | 161123 |
CHECK_NOT_NULL(options.env[i]); |
229 |
} |
||
230 |
2100 |
options.env[envc] = nullptr; |
|
231 |
} |
||
232 |
|||
233 |
// options.stdio |
||
234 |
2101 |
ParseStdioOptions(env, js_options, &options); |
|
235 |
|||
236 |
// options.windowsHide |
||
237 |
Local<Value> hide_v = |
||
238 |
6303 |
js_options->Get(context, env->windows_hide_string()).ToLocalChecked(); |
|
239 |
|||
240 |
✓✓ | 2101 |
if (hide_v->IsTrue()) { |
241 |
5 |
options.flags |= UV_PROCESS_WINDOWS_HIDE; |
|
242 |
} |
||
243 |
|||
244 |
✗✓ | 2101 |
if (env->hide_console_windows()) { |
245 |
options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE; |
||
246 |
} |
||
247 |
|||
248 |
// options.windows_verbatim_arguments |
||
249 |
Local<Value> wva_v = |
||
250 |
4202 |
js_options->Get(context, env->windows_verbatim_arguments_string()) |
|
251 |
2101 |
.ToLocalChecked(); |
|
252 |
|||
253 |
✗✓ | 2101 |
if (wva_v->IsTrue()) { |
254 |
options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; |
||
255 |
} |
||
256 |
|||
257 |
// options.detached |
||
258 |
Local<Value> detached_v = |
||
259 |
6303 |
js_options->Get(context, env->detached_string()).ToLocalChecked(); |
|
260 |
|||
261 |
✓✓ | 2101 |
if (detached_v->IsTrue()) { |
262 |
5 |
options.flags |= UV_PROCESS_DETACHED; |
|
263 |
} |
||
264 |
|||
265 |
2101 |
int err = uv_spawn(env->event_loop(), &wrap->process_, &options); |
|
266 |
2101 |
wrap->MarkAsInitialized(); |
|
267 |
|||
268 |
✓✓ | 2101 |
if (err == 0) { |
269 |
✗✓ | 2094 |
CHECK_EQ(wrap->process_.data, wrap); |
270 |
2094 |
wrap->object()->Set(context, env->pid_string(), |
|
271 |
Integer::New(env->isolate(), |
||
272 |
8376 |
wrap->process_.pid)).Check(); |
|
273 |
} |
||
274 |
|||
275 |
✓✗ | 2101 |
if (options.args) { |
276 |
✓✓ | 11146 |
for (int i = 0; options.args[i]; i++) free(options.args[i]); |
277 |
✓✗ | 2101 |
delete [] options.args; |
278 |
} |
||
279 |
|||
280 |
✓✓ | 2101 |
if (options.env) { |
281 |
✓✓ | 163223 |
for (int i = 0; options.env[i]; i++) free(options.env[i]); |
282 |
✓✗ | 2100 |
delete [] options.env; |
283 |
} |
||
284 |
|||
285 |
✓✗ | 2101 |
delete[] options.stdio; |
286 |
|||
287 |
4202 |
args.GetReturnValue().Set(err); |
|
288 |
} |
||
289 |
|||
290 |
264 |
static void Kill(const FunctionCallbackInfo<Value>& args) { |
|
291 |
264 |
Environment* env = Environment::GetCurrent(args); |
|
292 |
ProcessWrap* wrap; |
||
293 |
✗✓ | 264 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
294 |
264 |
int signal = args[0]->Int32Value(env->context()).FromJust(); |
|
295 |
264 |
int err = uv_process_kill(&wrap->process_, signal); |
|
296 |
528 |
args.GetReturnValue().Set(err); |
|
297 |
} |
||
298 |
|||
299 |
2047 |
static void OnExit(uv_process_t* handle, |
|
300 |
int64_t exit_status, |
||
301 |
int term_signal) { |
||
302 |
2047 |
ProcessWrap* wrap = ContainerOf(&ProcessWrap::process_, handle); |
|
303 |
✗✓ | 2047 |
CHECK_EQ(&wrap->process_, handle); |
304 |
|||
305 |
2047 |
Environment* env = wrap->env(); |
|
306 |
2047 |
HandleScope handle_scope(env->isolate()); |
|
307 |
2047 |
Context::Scope context_scope(env->context()); |
|
308 |
|||
309 |
Local<Value> argv[] = { |
||
310 |
Number::New(env->isolate(), static_cast<double>(exit_status)), |
||
311 |
OneByteString(env->isolate(), signo_string(term_signal)) |
||
312 |
4094 |
}; |
|
313 |
|||
314 |
2047 |
wrap->MakeCallback(env->onexit_string(), arraysize(argv), argv); |
|
315 |
2033 |
} |
|
316 |
|||
317 |
uv_process_t process_; |
||
318 |
}; |
||
319 |
|||
320 |
|||
321 |
} // anonymous namespace |
||
322 |
} // namespace node |
||
323 |
|||
324 |
5789 |
NODE_BINDING_CONTEXT_AWARE_INTERNAL(process_wrap, node::ProcessWrap::Initialize) |
Generated by: GCOVR (Version 4.2) |