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 <cstring> |
||
28 |
#include <cstdlib> |
||
29 |
|||
30 |
namespace node { |
||
31 |
|||
32 |
using v8::Array; |
||
33 |
using v8::Context; |
||
34 |
using v8::FunctionCallbackInfo; |
||
35 |
using v8::FunctionTemplate; |
||
36 |
using v8::HandleScope; |
||
37 |
using v8::Int32; |
||
38 |
using v8::Integer; |
||
39 |
using v8::Isolate; |
||
40 |
using v8::Local; |
||
41 |
using v8::Number; |
||
42 |
using v8::Object; |
||
43 |
using v8::String; |
||
44 |
using v8::Value; |
||
45 |
|||
46 |
namespace { |
||
47 |
|||
48 |
class ProcessWrap : public HandleWrap { |
||
49 |
public: |
||
50 |
4752 |
static void Initialize(Local<Object> target, |
|
51 |
Local<Value> unused, |
||
52 |
Local<Context> context, |
||
53 |
void* priv) { |
||
54 |
4752 |
Environment* env = Environment::GetCurrent(context); |
|
55 |
4752 |
Isolate* isolate = env->isolate(); |
|
56 |
4752 |
Local<FunctionTemplate> constructor = NewFunctionTemplate(isolate, New); |
|
57 |
9504 |
constructor->InstanceTemplate()->SetInternalFieldCount( |
|
58 |
ProcessWrap::kInternalFieldCount); |
||
59 |
|||
60 |
4752 |
constructor->Inherit(HandleWrap::GetConstructorTemplate(env)); |
|
61 |
|||
62 |
4752 |
SetProtoMethod(isolate, constructor, "spawn", Spawn); |
|
63 |
4752 |
SetProtoMethod(isolate, constructor, "kill", Kill); |
|
64 |
|||
65 |
4752 |
SetConstructorFunction(context, target, "Process", constructor); |
|
66 |
4752 |
} |
|
67 |
|||
68 |
1 |
SET_NO_MEMORY_INFO() |
|
69 |
9 |
SET_MEMORY_INFO_NAME(ProcessWrap) |
|
70 |
1 |
SET_SELF_SIZE(ProcessWrap) |
|
71 |
|||
72 |
private: |
||
73 |
1960 |
static void New(const FunctionCallbackInfo<Value>& args) { |
|
74 |
// This constructor should not be exposed to public javascript. |
||
75 |
// Therefore we assert that we are not trying to call this as a |
||
76 |
// normal function. |
||
77 |
✗✓ | 1960 |
CHECK(args.IsConstructCall()); |
78 |
1960 |
Environment* env = Environment::GetCurrent(args); |
|
79 |
1960 |
new ProcessWrap(env, args.This()); |
|
80 |
1960 |
} |
|
81 |
|||
82 |
1960 |
ProcessWrap(Environment* env, Local<Object> object) |
|
83 |
1960 |
: HandleWrap(env, |
|
84 |
object, |
||
85 |
1960 |
reinterpret_cast<uv_handle_t*>(&process_), |
|
86 |
1960 |
AsyncWrap::PROVIDER_PROCESSWRAP) { |
|
87 |
1960 |
MarkAsUninitialized(); |
|
88 |
1960 |
} |
|
89 |
|||
90 |
4192 |
static uv_stream_t* StreamForWrap(Environment* env, Local<Object> stdio) { |
|
91 |
4192 |
Local<String> handle_key = env->handle_string(); |
|
92 |
// This property has always been set by JS land if we are in this code path. |
||
93 |
Local<Object> handle = |
||
94 |
8384 |
stdio->Get(env->context(), handle_key).ToLocalChecked().As<Object>(); |
|
95 |
|||
96 |
4192 |
uv_stream_t* stream = LibuvStreamWrap::From(env, handle)->stream(); |
|
97 |
✗✓ | 4192 |
CHECK_NOT_NULL(stream); |
98 |
4192 |
return stream; |
|
99 |
} |
||
100 |
|||
101 |
1950 |
static void ParseStdioOptions(Environment* env, |
|
102 |
Local<Object> js_options, |
||
103 |
uv_process_options_t* options) { |
||
104 |
1950 |
Local<Context> context = env->context(); |
|
105 |
1950 |
Local<String> stdio_key = env->stdio_string(); |
|
106 |
Local<Array> stdios = |
||
107 |
5850 |
js_options->Get(context, stdio_key).ToLocalChecked().As<Array>(); |
|
108 |
|||
109 |
1950 |
uint32_t len = stdios->Length(); |
|
110 |
✓✗ | 1950 |
options->stdio = new uv_stdio_container_t[len]; |
111 |
1950 |
options->stdio_count = len; |
|
112 |
|||
113 |
✓✓ | 8314 |
for (uint32_t i = 0; i < len; i++) { |
114 |
Local<Object> stdio = |
||
115 |
19092 |
stdios->Get(context, i).ToLocalChecked().As<Object>(); |
|
116 |
Local<Value> type = |
||
117 |
19092 |
stdio->Get(context, env->type_string()).ToLocalChecked(); |
|
118 |
|||
119 |
✓✓ | 12728 |
if (type->StrictEquals(env->ignore_string())) { |
120 |
56 |
options->stdio[i].flags = UV_IGNORE; |
|
121 |
✓✓ | 12616 |
} else if (type->StrictEquals(env->pipe_string())) { |
122 |
4184 |
options->stdio[i].flags = static_cast<uv_stdio_flags>( |
|
123 |
UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE); |
||
124 |
4184 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
125 |
✓✓ | 4248 |
} else if (type->StrictEquals(env->overlapped_string())) { |
126 |
1 |
options->stdio[i].flags = static_cast<uv_stdio_flags>( |
|
127 |
UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE | |
||
128 |
UV_OVERLAPPED_PIPE); |
||
129 |
1 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
130 |
✓✓ | 4246 |
} else if (type->StrictEquals(env->wrap_string())) { |
131 |
7 |
options->stdio[i].flags = UV_INHERIT_STREAM; |
|
132 |
7 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
133 |
} else { |
||
134 |
2116 |
Local<String> fd_key = env->fd_string(); |
|
135 |
4232 |
Local<Value> fd_value = stdio->Get(context, fd_key).ToLocalChecked(); |
|
136 |
✗✓ | 2116 |
CHECK(fd_value->IsNumber()); |
137 |
2116 |
int fd = static_cast<int>(fd_value.As<Integer>()->Value()); |
|
138 |
2116 |
options->stdio[i].flags = UV_INHERIT_FD; |
|
139 |
2116 |
options->stdio[i].data.fd = fd; |
|
140 |
} |
||
141 |
} |
||
142 |
1950 |
} |
|
143 |
|||
144 |
1950 |
static void Spawn(const FunctionCallbackInfo<Value>& args) { |
|
145 |
1950 |
Environment* env = Environment::GetCurrent(args); |
|
146 |
1950 |
Local<Context> context = env->context(); |
|
147 |
ProcessWrap* wrap; |
||
148 |
✗✓ | 1950 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
149 |
|||
150 |
Local<Object> js_options = |
||
151 |
1950 |
args[0]->ToObject(env->context()).ToLocalChecked(); |
|
152 |
|||
153 |
uv_process_options_t options; |
||
154 |
1950 |
memset(&options, 0, sizeof(uv_process_options_t)); |
|
155 |
|||
156 |
1950 |
options.exit_cb = OnExit; |
|
157 |
|||
158 |
// options.uid |
||
159 |
Local<Value> uid_v = |
||
160 |
5850 |
js_options->Get(context, env->uid_string()).ToLocalChecked(); |
|
161 |
✓✓✓✗ ✓✓ |
3902 |
if (!uid_v->IsUndefined() && !uid_v->IsNull()) { |
162 |
✗✓ | 1 |
CHECK(uid_v->IsInt32()); |
163 |
1 |
const int32_t uid = uid_v.As<Int32>()->Value(); |
|
164 |
1 |
options.flags |= UV_PROCESS_SETUID; |
|
165 |
1 |
options.uid = static_cast<uv_uid_t>(uid); |
|
166 |
} |
||
167 |
|||
168 |
// options.gid |
||
169 |
Local<Value> gid_v = |
||
170 |
5850 |
js_options->Get(context, env->gid_string()).ToLocalChecked(); |
|
171 |
✓✓✓✗ ✓✓ |
3902 |
if (!gid_v->IsUndefined() && !gid_v->IsNull()) { |
172 |
✗✓ | 1 |
CHECK(gid_v->IsInt32()); |
173 |
1 |
const int32_t gid = gid_v.As<Int32>()->Value(); |
|
174 |
1 |
options.flags |= UV_PROCESS_SETGID; |
|
175 |
1 |
options.gid = static_cast<uv_gid_t>(gid); |
|
176 |
} |
||
177 |
|||
178 |
// TODO(bnoordhuis) is this possible to do without mallocing ? |
||
179 |
|||
180 |
// options.file |
||
181 |
Local<Value> file_v = |
||
182 |
5850 |
js_options->Get(context, env->file_string()).ToLocalChecked(); |
|
183 |
✗✓ | 3900 |
CHECK(file_v->IsString()); |
184 |
3900 |
node::Utf8Value file(env->isolate(), file_v); |
|
185 |
1950 |
options.file = *file; |
|
186 |
|||
187 |
// options.args |
||
188 |
Local<Value> argv_v = |
||
189 |
5850 |
js_options->Get(context, env->args_string()).ToLocalChecked(); |
|
190 |
✓✗✓✗ ✓✗ |
3900 |
if (!argv_v.IsEmpty() && argv_v->IsArray()) { |
191 |
1950 |
Local<Array> js_argv = argv_v.As<Array>(); |
|
192 |
1950 |
int argc = js_argv->Length(); |
|
193 |
✗✓ | 1950 |
CHECK_GT(argc + 1, 0); // Check for overflow. |
194 |
|||
195 |
// Heap allocate to detect errors. +1 is for nullptr. |
||
196 |
✓✗ | 1950 |
options.args = new char*[argc + 1]; |
197 |
✓✓ | 10330 |
for (int i = 0; i < argc; i++) { |
198 |
node::Utf8Value arg(env->isolate(), |
||
199 |
25140 |
js_argv->Get(context, i).ToLocalChecked()); |
|
200 |
8380 |
options.args[i] = strdup(*arg); |
|
201 |
✗✓ | 8380 |
CHECK_NOT_NULL(options.args[i]); |
202 |
} |
||
203 |
1950 |
options.args[argc] = nullptr; |
|
204 |
} |
||
205 |
|||
206 |
// options.cwd |
||
207 |
Local<Value> cwd_v = |
||
208 |
5850 |
js_options->Get(context, env->cwd_string()).ToLocalChecked(); |
|
209 |
node::Utf8Value cwd(env->isolate(), |
||
210 |
✓✓ | 3900 |
cwd_v->IsString() ? cwd_v : Local<Value>()); |
211 |
✓✓ | 1950 |
if (cwd.length() > 0) { |
212 |
112 |
options.cwd = *cwd; |
|
213 |
} |
||
214 |
|||
215 |
// options.env |
||
216 |
Local<Value> env_v = |
||
217 |
5850 |
js_options->Get(context, env->env_pairs_string()).ToLocalChecked(); |
|
218 |
✓✗✓✓ ✓✓ |
3900 |
if (!env_v.IsEmpty() && env_v->IsArray()) { |
219 |
1948 |
Local<Array> env_opt = env_v.As<Array>(); |
|
220 |
1948 |
int envc = env_opt->Length(); |
|
221 |
✗✓ | 1948 |
CHECK_GT(envc + 1, 0); // Check for overflow. |
222 |
✓✗ | 1948 |
options.env = new char*[envc + 1]; // Heap allocated to detect errors. |
223 |
✓✓ | 143583 |
for (int i = 0; i < envc; i++) { |
224 |
node::Utf8Value pair(env->isolate(), |
||
225 |
424905 |
env_opt->Get(context, i).ToLocalChecked()); |
|
226 |
141635 |
options.env[i] = strdup(*pair); |
|
227 |
✗✓ | 141635 |
CHECK_NOT_NULL(options.env[i]); |
228 |
} |
||
229 |
1948 |
options.env[envc] = nullptr; |
|
230 |
} |
||
231 |
|||
232 |
// options.stdio |
||
233 |
1950 |
ParseStdioOptions(env, js_options, &options); |
|
234 |
|||
235 |
// options.windowsHide |
||
236 |
Local<Value> hide_v = |
||
237 |
5850 |
js_options->Get(context, env->windows_hide_string()).ToLocalChecked(); |
|
238 |
|||
239 |
✓✓ | 1950 |
if (hide_v->IsTrue()) { |
240 |
5 |
options.flags |= UV_PROCESS_WINDOWS_HIDE; |
|
241 |
} |
||
242 |
|||
243 |
✗✓ | 1950 |
if (env->hide_console_windows()) { |
244 |
options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE; |
||
245 |
} |
||
246 |
|||
247 |
// options.windows_verbatim_arguments |
||
248 |
Local<Value> wva_v = |
||
249 |
3900 |
js_options->Get(context, env->windows_verbatim_arguments_string()) |
|
250 |
1950 |
.ToLocalChecked(); |
|
251 |
|||
252 |
✗✓ | 1950 |
if (wva_v->IsTrue()) { |
253 |
options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; |
||
254 |
} |
||
255 |
|||
256 |
// options.detached |
||
257 |
Local<Value> detached_v = |
||
258 |
5850 |
js_options->Get(context, env->detached_string()).ToLocalChecked(); |
|
259 |
|||
260 |
✓✓ | 1950 |
if (detached_v->IsTrue()) { |
261 |
5 |
options.flags |= UV_PROCESS_DETACHED; |
|
262 |
} |
||
263 |
|||
264 |
1950 |
int err = uv_spawn(env->event_loop(), &wrap->process_, &options); |
|
265 |
1950 |
wrap->MarkAsInitialized(); |
|
266 |
|||
267 |
✓✓ | 1950 |
if (err == 0) { |
268 |
✗✓ | 1943 |
CHECK_EQ(wrap->process_.data, wrap); |
269 |
1943 |
wrap->object()->Set(context, env->pid_string(), |
|
270 |
Integer::New(env->isolate(), |
||
271 |
7772 |
wrap->process_.pid)).Check(); |
|
272 |
} |
||
273 |
|||
274 |
✓✗ | 1950 |
if (options.args) { |
275 |
✓✓ | 10330 |
for (int i = 0; options.args[i]; i++) free(options.args[i]); |
276 |
✓✗ | 1950 |
delete [] options.args; |
277 |
} |
||
278 |
|||
279 |
✓✓ | 1950 |
if (options.env) { |
280 |
✓✓ | 143583 |
for (int i = 0; options.env[i]; i++) free(options.env[i]); |
281 |
✓✗ | 1948 |
delete [] options.env; |
282 |
} |
||
283 |
|||
284 |
✓✗ | 1950 |
delete[] options.stdio; |
285 |
|||
286 |
3900 |
args.GetReturnValue().Set(err); |
|
287 |
} |
||
288 |
|||
289 |
241 |
static void Kill(const FunctionCallbackInfo<Value>& args) { |
|
290 |
241 |
Environment* env = Environment::GetCurrent(args); |
|
291 |
ProcessWrap* wrap; |
||
292 |
✗✓ | 241 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
293 |
241 |
int signal = args[0]->Int32Value(env->context()).FromJust(); |
|
294 |
241 |
int err = uv_process_kill(&wrap->process_, signal); |
|
295 |
482 |
args.GetReturnValue().Set(err); |
|
296 |
} |
||
297 |
|||
298 |
1900 |
static void OnExit(uv_process_t* handle, |
|
299 |
int64_t exit_status, |
||
300 |
int term_signal) { |
||
301 |
1900 |
ProcessWrap* wrap = ContainerOf(&ProcessWrap::process_, handle); |
|
302 |
✗✓ | 1900 |
CHECK_EQ(&wrap->process_, handle); |
303 |
|||
304 |
1900 |
Environment* env = wrap->env(); |
|
305 |
3789 |
HandleScope handle_scope(env->isolate()); |
|
306 |
1900 |
Context::Scope context_scope(env->context()); |
|
307 |
|||
308 |
Local<Value> argv[] = { |
||
309 |
Number::New(env->isolate(), static_cast<double>(exit_status)), |
||
310 |
OneByteString(env->isolate(), signo_string(term_signal)) |
||
311 |
3800 |
}; |
|
312 |
|||
313 |
1900 |
wrap->MakeCallback(env->onexit_string(), arraysize(argv), argv); |
|
314 |
1889 |
} |
|
315 |
|||
316 |
uv_process_t process_; |
||
317 |
}; |
||
318 |
|||
319 |
|||
320 |
} // anonymous namespace |
||
321 |
} // namespace node |
||
322 |
|||
323 |
5417 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_wrap, node::ProcessWrap::Initialize) |
Generated by: GCOVR (Version 4.2) |