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 "node_contextify.h" |
||
23 |
|||
24 |
#include "memory_tracker-inl.h" |
||
25 |
#include "node_internals.h" |
||
26 |
#include "node_watchdog.h" |
||
27 |
#include "base_object-inl.h" |
||
28 |
#include "node_context_data.h" |
||
29 |
#include "node_errors.h" |
||
30 |
#include "module_wrap.h" |
||
31 |
#include "util-inl.h" |
||
32 |
|||
33 |
namespace node { |
||
34 |
namespace contextify { |
||
35 |
|||
36 |
using errors::TryCatchScope; |
||
37 |
|||
38 |
using v8::Array; |
||
39 |
using v8::ArrayBuffer; |
||
40 |
using v8::ArrayBufferView; |
||
41 |
using v8::Boolean; |
||
42 |
using v8::Context; |
||
43 |
using v8::EscapableHandleScope; |
||
44 |
using v8::External; |
||
45 |
using v8::Function; |
||
46 |
using v8::FunctionCallbackInfo; |
||
47 |
using v8::FunctionTemplate; |
||
48 |
using v8::HandleScope; |
||
49 |
using v8::IndexedPropertyHandlerConfiguration; |
||
50 |
using v8::Integer; |
||
51 |
using v8::Isolate; |
||
52 |
using v8::Local; |
||
53 |
using v8::Maybe; |
||
54 |
using v8::MaybeLocal; |
||
55 |
using v8::Name; |
||
56 |
using v8::NamedPropertyHandlerConfiguration; |
||
57 |
using v8::Number; |
||
58 |
using v8::Object; |
||
59 |
using v8::ObjectTemplate; |
||
60 |
using v8::PrimitiveArray; |
||
61 |
using v8::PropertyAttribute; |
||
62 |
using v8::PropertyCallbackInfo; |
||
63 |
using v8::PropertyDescriptor; |
||
64 |
using v8::PropertyHandlerFlags; |
||
65 |
using v8::Script; |
||
66 |
using v8::ScriptCompiler; |
||
67 |
using v8::ScriptOrigin; |
||
68 |
using v8::ScriptOrModule; |
||
69 |
using v8::String; |
||
70 |
using v8::Uint32; |
||
71 |
using v8::UnboundScript; |
||
72 |
using v8::Value; |
||
73 |
using v8::WeakCallbackInfo; |
||
74 |
using v8::WeakCallbackType; |
||
75 |
|||
76 |
// The vm module executes code in a sandboxed environment with a different |
||
77 |
// global object than the rest of the code. This is achieved by applying |
||
78 |
// every call that changes or queries a property on the global `this` in the |
||
79 |
// sandboxed code, to the sandbox object. |
||
80 |
// |
||
81 |
// The implementation uses V8's interceptors for methods like `set`, `get`, |
||
82 |
// `delete`, `defineProperty`, and for any query of the property attributes. |
||
83 |
// Property handlers with interceptors are set on the object template for |
||
84 |
// the sandboxed code. Handlers for both named properties and for indexed |
||
85 |
// properties are used. Their functionality is almost identical, the indexed |
||
86 |
// interceptors mostly just call the named interceptors. |
||
87 |
// |
||
88 |
// For every `get` of a global property in the sandboxed context, the |
||
89 |
// interceptor callback checks the sandbox object for the property. |
||
90 |
// If the property is defined on the sandbox, that result is returned to |
||
91 |
// the original call instead of finishing the query on the global object. |
||
92 |
// |
||
93 |
// For every `set` of a global property, the interceptor callback defines or |
||
94 |
// changes the property both on the sandbox and the global proxy. |
||
95 |
|||
96 |
namespace { |
||
97 |
|||
98 |
// Convert an int to a V8 Name (String or Symbol). |
||
99 |
3 |
Local<Name> Uint32ToName(Local<Context> context, uint32_t index) { |
|
100 |
12 |
return Uint32::New(context->GetIsolate(), index)->ToString(context) |
|
101 |
9 |
.ToLocalChecked(); |
|
102 |
} |
||
103 |
|||
104 |
} // anonymous namespace |
||
105 |
|||
106 |
544 |
ContextifyContext::ContextifyContext( |
|
107 |
Environment* env, |
||
108 |
544 |
Local<Object> sandbox_obj, const ContextOptions& options) : env_(env) { |
|
109 |
544 |
MaybeLocal<Context> v8_context = CreateV8Context(env, sandbox_obj, options); |
|
110 |
|||
111 |
// Allocation failure, maximum call stack size reached, termination, etc. |
||
112 |
✓✓ | 1088 |
if (v8_context.IsEmpty()) return; |
113 |
|||
114 |
922 |
context_.Reset(env->isolate(), v8_context.ToLocalChecked()); |
|
115 |
461 |
context_.SetWeak(this, WeakCallback, WeakCallbackType::kParameter); |
|
116 |
461 |
env->AddCleanupHook(CleanupHook, this); |
|
117 |
} |
||
118 |
|||
119 |
|||
120 |
1050 |
ContextifyContext::~ContextifyContext() { |
|
121 |
525 |
env()->RemoveCleanupHook(CleanupHook, this); |
|
122 |
525 |
} |
|
123 |
|||
124 |
|||
125 |
442 |
void ContextifyContext::CleanupHook(void* arg) { |
|
126 |
442 |
ContextifyContext* self = static_cast<ContextifyContext*>(arg); |
|
127 |
442 |
self->context_.Reset(); |
|
128 |
✓✗ | 442 |
delete self; |
129 |
442 |
} |
|
130 |
|||
131 |
|||
132 |
// This is an object that just keeps an internal pointer to this |
||
133 |
// ContextifyContext. It's passed to the NamedPropertyHandler. If we |
||
134 |
// pass the main JavaScript context object we're embedded in, then the |
||
135 |
// NamedPropertyHandler will store a reference to it forever and keep it |
||
136 |
// from getting gc'd. |
||
137 |
544 |
MaybeLocal<Object> ContextifyContext::CreateDataWrapper(Environment* env) { |
|
138 |
Local<Object> wrapper; |
||
139 |
✗✓ | 1088 |
if (!env->script_data_constructor_function() |
140 |
1632 |
->NewInstance(env->context()) |
|
141 |
1632 |
.ToLocal(&wrapper)) { |
|
142 |
return MaybeLocal<Object>(); |
||
143 |
} |
||
144 |
|||
145 |
544 |
wrapper->SetAlignedPointerInInternalField(0, this); |
|
146 |
544 |
return wrapper; |
|
147 |
} |
||
148 |
|||
149 |
544 |
MaybeLocal<Context> ContextifyContext::CreateV8Context( |
|
150 |
Environment* env, |
||
151 |
Local<Object> sandbox_obj, |
||
152 |
const ContextOptions& options) { |
||
153 |
544 |
EscapableHandleScope scope(env->isolate()); |
|
154 |
Local<FunctionTemplate> function_template = |
||
155 |
544 |
FunctionTemplate::New(env->isolate()); |
|
156 |
|||
157 |
1088 |
function_template->SetClassName(sandbox_obj->GetConstructorName()); |
|
158 |
|||
159 |
Local<ObjectTemplate> object_template = |
||
160 |
544 |
function_template->InstanceTemplate(); |
|
161 |
|||
162 |
Local<Object> data_wrapper; |
||
163 |
✗✓ | 1088 |
if (!CreateDataWrapper(env).ToLocal(&data_wrapper)) |
164 |
return MaybeLocal<Context>(); |
||
165 |
|||
166 |
NamedPropertyHandlerConfiguration config( |
||
167 |
PropertyGetterCallback, |
||
168 |
PropertySetterCallback, |
||
169 |
PropertyDescriptorCallback, |
||
170 |
PropertyDeleterCallback, |
||
171 |
PropertyEnumeratorCallback, |
||
172 |
PropertyDefinerCallback, |
||
173 |
data_wrapper, |
||
174 |
544 |
PropertyHandlerFlags::kHasNoSideEffect); |
|
175 |
|||
176 |
IndexedPropertyHandlerConfiguration indexed_config( |
||
177 |
IndexedPropertyGetterCallback, |
||
178 |
IndexedPropertySetterCallback, |
||
179 |
IndexedPropertyDescriptorCallback, |
||
180 |
IndexedPropertyDeleterCallback, |
||
181 |
PropertyEnumeratorCallback, |
||
182 |
IndexedPropertyDefinerCallback, |
||
183 |
data_wrapper, |
||
184 |
544 |
PropertyHandlerFlags::kHasNoSideEffect); |
|
185 |
|||
186 |
544 |
object_template->SetHandler(config); |
|
187 |
544 |
object_template->SetHandler(indexed_config); |
|
188 |
|||
189 |
544 |
Local<Context> ctx = NewContext(env->isolate(), object_template); |
|
190 |
|||
191 |
✓✓ | 544 |
if (ctx.IsEmpty()) { |
192 |
83 |
return MaybeLocal<Context>(); |
|
193 |
} |
||
194 |
|||
195 |
1383 |
ctx->SetSecurityToken(env->context()->GetSecurityToken()); |
|
196 |
|||
197 |
// We need to tie the lifetime of the sandbox object with the lifetime of |
||
198 |
// newly created context. We do this by making them hold references to each |
||
199 |
// other. The context can directly hold a reference to the sandbox as an |
||
200 |
// embedder data field. However, we cannot hold a reference to a v8::Context |
||
201 |
// directly in an Object, we instead hold onto the new context's global |
||
202 |
// object instead (which then has a reference to the context). |
||
203 |
461 |
ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj); |
|
204 |
sandbox_obj->SetPrivate(env->context(), |
||
205 |
env->contextify_global_private_symbol(), |
||
206 |
1383 |
ctx->Global()); |
|
207 |
|||
208 |
922 |
Utf8Value name_val(env->isolate(), options.name); |
|
209 |
1383 |
ctx->AllowCodeGenerationFromStrings(options.allow_code_gen_strings->IsTrue()); |
|
210 |
ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, |
||
211 |
461 |
options.allow_code_gen_wasm); |
|
212 |
|||
213 |
922 |
ContextInfo info(*name_val); |
|
214 |
|||
215 |
✗✓ | 922 |
if (!options.origin.IsEmpty()) { |
216 |
Utf8Value origin_val(env->isolate(), options.origin); |
||
217 |
info.origin = *origin_val; |
||
218 |
} |
||
219 |
|||
220 |
461 |
env->AssignToContext(ctx, info); |
|
221 |
|||
222 |
461 |
return scope.Escape(ctx); |
|
223 |
} |
||
224 |
|||
225 |
|||
226 |
5128 |
void ContextifyContext::Init(Environment* env, Local<Object> target) { |
|
227 |
Local<FunctionTemplate> function_template = |
||
228 |
5128 |
FunctionTemplate::New(env->isolate()); |
|
229 |
10256 |
function_template->InstanceTemplate()->SetInternalFieldCount(1); |
|
230 |
env->set_script_data_constructor_function( |
||
231 |
15384 |
function_template->GetFunction(env->context()).ToLocalChecked()); |
|
232 |
|||
233 |
5128 |
env->SetMethod(target, "makeContext", MakeContext); |
|
234 |
5128 |
env->SetMethod(target, "isContext", IsContext); |
|
235 |
5128 |
env->SetMethod(target, "compileFunction", CompileFunction); |
|
236 |
5128 |
} |
|
237 |
|||
238 |
|||
239 |
// makeContext(sandbox, name, origin, strings, wasm); |
||
240 |
544 |
void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) { |
|
241 |
544 |
Environment* env = Environment::GetCurrent(args); |
|
242 |
|||
243 |
✗✓ | 544 |
CHECK_EQ(args.Length(), 5); |
244 |
✗✓ | 1088 |
CHECK(args[0]->IsObject()); |
245 |
1088 |
Local<Object> sandbox = args[0].As<Object>(); |
|
246 |
|||
247 |
// Don't allow contextifying a sandbox multiple times. |
||
248 |
✗✓ | 1632 |
CHECK( |
249 |
!sandbox->HasPrivate( |
||
250 |
env->context(), |
||
251 |
env->contextify_context_private_symbol()).FromJust()); |
||
252 |
|||
253 |
544 |
ContextOptions options; |
|
254 |
|||
255 |
✗✓ | 1632 |
CHECK(args[1]->IsString()); |
256 |
1088 |
options.name = args[1].As<String>(); |
|
257 |
|||
258 |
✓✗✗✓ ✓✗✓✗ ✗✓ |
3808 |
CHECK(args[2]->IsString() || args[2]->IsUndefined()); |
259 |
✗✓ | 1632 |
if (args[2]->IsString()) { |
260 |
options.origin = args[2].As<String>(); |
||
261 |
} |
||
262 |
|||
263 |
✗✓ | 1088 |
CHECK(args[3]->IsBoolean()); |
264 |
1088 |
options.allow_code_gen_strings = args[3].As<Boolean>(); |
|
265 |
|||
266 |
✗✓ | 1088 |
CHECK(args[4]->IsBoolean()); |
267 |
1088 |
options.allow_code_gen_wasm = args[4].As<Boolean>(); |
|
268 |
|||
269 |
544 |
TryCatchScope try_catch(env); |
|
270 |
✓✓ | 1005 |
auto context_ptr = std::make_unique<ContextifyContext>(env, sandbox, options); |
271 |
|||
272 |
✓✓ | 544 |
if (try_catch.HasCaught()) { |
273 |
✓✓ | 83 |
if (!try_catch.HasTerminated()) |
274 |
82 |
try_catch.ReThrow(); |
|
275 |
83 |
return; |
|
276 |
} |
||
277 |
|||
278 |
✗✓ | 922 |
if (context_ptr->context().IsEmpty()) |
279 |
return; |
||
280 |
|||
281 |
sandbox->SetPrivate( |
||
282 |
env->context(), |
||
283 |
env->contextify_context_private_symbol(), |
||
284 |
✓✓ | 1844 |
External::New(env->isolate(), context_ptr.release())); |
285 |
} |
||
286 |
|||
287 |
|||
288 |
2564 |
void ContextifyContext::IsContext(const FunctionCallbackInfo<Value>& args) { |
|
289 |
2564 |
Environment* env = Environment::GetCurrent(args); |
|
290 |
|||
291 |
✗✓ | 5128 |
CHECK(args[0]->IsObject()); |
292 |
5128 |
Local<Object> sandbox = args[0].As<Object>(); |
|
293 |
|||
294 |
Maybe<bool> result = |
||
295 |
sandbox->HasPrivate(env->context(), |
||
296 |
5128 |
env->contextify_context_private_symbol()); |
|
297 |
7692 |
args.GetReturnValue().Set(result.FromJust()); |
|
298 |
2564 |
} |
|
299 |
|||
300 |
|||
301 |
void ContextifyContext::WeakCallback( |
||
302 |
const WeakCallbackInfo<ContextifyContext>& data) { |
||
303 |
ContextifyContext* context = data.GetParameter(); |
||
304 |
delete context; |
||
305 |
} |
||
306 |
|||
307 |
// static |
||
308 |
1948 |
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox( |
|
309 |
Environment* env, |
||
310 |
const Local<Object>& sandbox) { |
||
311 |
MaybeLocal<Value> maybe_value = |
||
312 |
sandbox->GetPrivate(env->context(), |
||
313 |
3896 |
env->contextify_context_private_symbol()); |
|
314 |
Local<Value> context_external_v; |
||
315 |
✓✗✓✗ ✓✗ |
3896 |
if (maybe_value.ToLocal(&context_external_v) && |
316 |
1948 |
context_external_v->IsExternal()) { |
|
317 |
1948 |
Local<External> context_external = context_external_v.As<External>(); |
|
318 |
1948 |
return static_cast<ContextifyContext*>(context_external->Value()); |
|
319 |
} |
||
320 |
return nullptr; |
||
321 |
} |
||
322 |
|||
323 |
// static |
||
324 |
template <typename T> |
||
325 |
14655425 |
ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) { |
|
326 |
14655425 |
Local<Value> data = args.Data(); |
|
327 |
return static_cast<ContextifyContext*>( |
||
328 |
43966275 |
data.As<Object>()->GetAlignedPointerFromInternalField(0)); |
|
329 |
} |
||
330 |
|||
331 |
// static |
||
332 |
14651809 |
void ContextifyContext::PropertyGetterCallback( |
|
333 |
Local<Name> property, |
||
334 |
const PropertyCallbackInfo<Value>& args) { |
||
335 |
14651809 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
336 |
|||
337 |
// Still initializing |
||
338 |
✓✓ | 29303618 |
if (ctx->context_.IsEmpty()) |
339 |
15294927 |
return; |
|
340 |
|||
341 |
14008691 |
Local<Context> context = ctx->context(); |
|
342 |
14008691 |
Local<Object> sandbox = ctx->sandbox(); |
|
343 |
MaybeLocal<Value> maybe_rv = |
||
344 |
14008691 |
sandbox->GetRealNamedProperty(context, property); |
|
345 |
✓✓ | 14008691 |
if (maybe_rv.IsEmpty()) { |
346 |
maybe_rv = |
||
347 |
13232568 |
ctx->global_proxy()->GetRealNamedProperty(context, property); |
|
348 |
} |
||
349 |
|||
350 |
Local<Value> rv; |
||
351 |
✓✓ | 14008691 |
if (maybe_rv.ToLocal(&rv)) { |
352 |
✓✓ | 13996647 |
if (rv == sandbox) |
353 |
150 |
rv = ctx->global_proxy(); |
|
354 |
|||
355 |
27993294 |
args.GetReturnValue().Set(rv); |
|
356 |
} |
||
357 |
} |
||
358 |
|||
359 |
// static |
||
360 |
3557 |
void ContextifyContext::PropertySetterCallback( |
|
361 |
Local<Name> property, |
||
362 |
Local<Value> value, |
||
363 |
const PropertyCallbackInfo<Value>& args) { |
||
364 |
3557 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
365 |
|||
366 |
// Still initializing |
||
367 |
✗✓ | 7114 |
if (ctx->context_.IsEmpty()) |
368 |
14 |
return; |
|
369 |
|||
370 |
3557 |
auto attributes = PropertyAttribute::None; |
|
371 |
bool is_declared_on_global_proxy = ctx->global_proxy() |
||
372 |
10671 |
->GetRealNamedPropertyAttributes(ctx->context(), property) |
|
373 |
7114 |
.To(&attributes); |
|
374 |
bool read_only = |
||
375 |
3557 |
static_cast<int>(attributes) & |
|
376 |
3557 |
static_cast<int>(PropertyAttribute::ReadOnly); |
|
377 |
|||
378 |
bool is_declared_on_sandbox = ctx->sandbox() |
||
379 |
10671 |
->GetRealNamedPropertyAttributes(ctx->context(), property) |
|
380 |
7114 |
.To(&attributes); |
|
381 |
✓✓✓✓ |
7110 |
read_only = read_only || |
382 |
3553 |
(static_cast<int>(attributes) & |
|
383 |
3557 |
static_cast<int>(PropertyAttribute::ReadOnly)); |
|
384 |
|||
385 |
✓✓ | 3557 |
if (read_only) |
386 |
13 |
return; |
|
387 |
|||
388 |
// true for x = 5 |
||
389 |
// false for this.x = 5 |
||
390 |
// false for Object.defineProperty(this, 'foo', ...) |
||
391 |
// false for vmResult.x = 5 where vmResult = vm.runInContext(); |
||
392 |
7088 |
bool is_contextual_store = ctx->global_proxy() != args.This(); |
|
393 |
|||
394 |
// Indicator to not return before setting (undeclared) function declarations |
||
395 |
// on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true. |
||
396 |
// True for 'function f() {}', 'this.f = function() {}', |
||
397 |
// 'var f = function()'. |
||
398 |
// In effect only for 'function f() {}' because |
||
399 |
// var f = function(), is_declared = true |
||
400 |
// this.f = function() {}, is_contextual_store = false. |
||
401 |
3544 |
bool is_function = value->IsFunction(); |
|
402 |
|||
403 |
✓✓✓✓ |
3544 |
bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox; |
404 |
✓✓✓✓ ✓✓✓✓ ✓✓ |
6037 |
if (!is_declared && args.ShouldThrowOnError() && is_contextual_store && |
405 |
55 |
!is_function) |
|
406 |
1 |
return; |
|
407 |
|||
408 |
✓✓✓✓ ✓✓ |
9555 |
if (!is_declared_on_global_proxy && is_declared_on_sandbox && |
409 |
✓✓✓✓ ✓✗ |
3564 |
args.ShouldThrowOnError() && is_contextual_store && !is_function) { |
410 |
// The property exists on the sandbox but not on the global |
||
411 |
// proxy. Setting it would throw because we are in strict mode. |
||
412 |
// Don't attempt to set it by signaling that the call was |
||
413 |
// intercepted. Only change the value on the sandbox. |
||
414 |
8 |
args.GetReturnValue().Set(false); |
|
415 |
} |
||
416 |
|||
417 |
10629 |
ctx->sandbox()->Set(ctx->context(), property, value).Check(); |
|
418 |
} |
||
419 |
|||
420 |
// static |
||
421 |
29 |
void ContextifyContext::PropertyDescriptorCallback( |
|
422 |
Local<Name> property, |
||
423 |
const PropertyCallbackInfo<Value>& args) { |
||
424 |
29 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
425 |
|||
426 |
// Still initializing |
||
427 |
✗✓ | 58 |
if (ctx->context_.IsEmpty()) |
428 |
29 |
return; |
|
429 |
|||
430 |
29 |
Local<Context> context = ctx->context(); |
|
431 |
|||
432 |
29 |
Local<Object> sandbox = ctx->sandbox(); |
|
433 |
|||
434 |
✓✓ | 87 |
if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) { |
435 |
args.GetReturnValue().Set( |
||
436 |
14 |
sandbox->GetOwnPropertyDescriptor(context, property) |
|
437 |
42 |
.ToLocalChecked()); |
|
438 |
} |
||
439 |
} |
||
440 |
|||
441 |
// static |
||
442 |
15 |
void ContextifyContext::PropertyDefinerCallback( |
|
443 |
Local<Name> property, |
||
444 |
const PropertyDescriptor& desc, |
||
445 |
const PropertyCallbackInfo<Value>& args) { |
||
446 |
15 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
447 |
|||
448 |
// Still initializing |
||
449 |
✗✓ | 30 |
if (ctx->context_.IsEmpty()) |
450 |
return; |
||
451 |
|||
452 |
15 |
Local<Context> context = ctx->context(); |
|
453 |
15 |
Isolate* isolate = context->GetIsolate(); |
|
454 |
|||
455 |
15 |
auto attributes = PropertyAttribute::None; |
|
456 |
bool is_declared = |
||
457 |
ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(), |
||
458 |
45 |
property) |
|
459 |
30 |
.To(&attributes); |
|
460 |
bool read_only = |
||
461 |
15 |
static_cast<int>(attributes) & |
|
462 |
15 |
static_cast<int>(PropertyAttribute::ReadOnly); |
|
463 |
|||
464 |
// If the property is set on the global as read_only, don't change it on |
||
465 |
// the global or sandbox. |
||
466 |
✓✓✗✓ |
15 |
if (is_declared && read_only) |
467 |
return; |
||
468 |
|||
469 |
15 |
Local<Object> sandbox = ctx->sandbox(); |
|
470 |
|||
471 |
auto define_prop_on_sandbox = |
||
472 |
15 |
[&] (PropertyDescriptor* desc_for_sandbox) { |
|
473 |
✓✓ | 15 |
if (desc.has_enumerable()) { |
474 |
2 |
desc_for_sandbox->set_enumerable(desc.enumerable()); |
|
475 |
} |
||
476 |
✓✓ | 15 |
if (desc.has_configurable()) { |
477 |
1 |
desc_for_sandbox->set_configurable(desc.configurable()); |
|
478 |
} |
||
479 |
// Set the property on the sandbox. |
||
480 |
30 |
sandbox->DefineProperty(context, property, *desc_for_sandbox) |
|
481 |
30 |
.Check(); |
|
482 |
30 |
}; |
|
483 |
|||
484 |
✓✓✗✓ ✓✓ |
15 |
if (desc.has_get() || desc.has_set()) { |
485 |
PropertyDescriptor desc_for_sandbox( |
||
486 |
✗✓ | 10 |
desc.has_get() ? desc.get() : Undefined(isolate).As<Value>(), |
487 |
✓✓✓✗ ✓✓ |
23 |
desc.has_set() ? desc.set() : Undefined(isolate).As<Value>()); |
488 |
|||
489 |
5 |
define_prop_on_sandbox(&desc_for_sandbox); |
|
490 |
} else { |
||
491 |
Local<Value> value = |
||
492 |
✓✓✓✓ |
12 |
desc.has_value() ? desc.value() : Undefined(isolate).As<Value>(); |
493 |
|||
494 |
✗✓ | 10 |
if (desc.has_writable()) { |
495 |
PropertyDescriptor desc_for_sandbox(value, desc.writable()); |
||
496 |
define_prop_on_sandbox(&desc_for_sandbox); |
||
497 |
} else { |
||
498 |
10 |
PropertyDescriptor desc_for_sandbox(value); |
|
499 |
10 |
define_prop_on_sandbox(&desc_for_sandbox); |
|
500 |
} |
||
501 |
} |
||
502 |
} |
||
503 |
|||
504 |
// static |
||
505 |
2 |
void ContextifyContext::PropertyDeleterCallback( |
|
506 |
Local<Name> property, |
||
507 |
const PropertyCallbackInfo<Boolean>& args) { |
||
508 |
2 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
509 |
|||
510 |
// Still initializing |
||
511 |
✗✓ | 4 |
if (ctx->context_.IsEmpty()) |
512 |
1 |
return; |
|
513 |
|||
514 |
4 |
Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property); |
|
515 |
|||
516 |
✓✓ | 4 |
if (success.FromMaybe(false)) |
517 |
1 |
return; |
|
518 |
|||
519 |
// Delete failed on the sandbox, intercept and do not delete on |
||
520 |
// the global object. |
||
521 |
2 |
args.GetReturnValue().Set(false); |
|
522 |
} |
||
523 |
|||
524 |
// static |
||
525 |
10 |
void ContextifyContext::PropertyEnumeratorCallback( |
|
526 |
const PropertyCallbackInfo<Array>& args) { |
||
527 |
10 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
528 |
|||
529 |
// Still initializing |
||
530 |
✗✓ | 20 |
if (ctx->context_.IsEmpty()) |
531 |
return; |
||
532 |
|||
533 |
Local<Array> properties; |
||
534 |
|||
535 |
✗✓ | 30 |
if (!ctx->sandbox()->GetPropertyNames(ctx->context()).ToLocal(&properties)) |
536 |
return; |
||
537 |
|||
538 |
20 |
args.GetReturnValue().Set(properties); |
|
539 |
} |
||
540 |
|||
541 |
// static |
||
542 |
void ContextifyContext::IndexedPropertyGetterCallback( |
||
543 |
uint32_t index, |
||
544 |
const PropertyCallbackInfo<Value>& args) { |
||
545 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
||
546 |
|||
547 |
// Still initializing |
||
548 |
if (ctx->context_.IsEmpty()) |
||
549 |
return; |
||
550 |
|||
551 |
ContextifyContext::PropertyGetterCallback( |
||
552 |
Uint32ToName(ctx->context(), index), args); |
||
553 |
} |
||
554 |
|||
555 |
|||
556 |
1 |
void ContextifyContext::IndexedPropertySetterCallback( |
|
557 |
uint32_t index, |
||
558 |
Local<Value> value, |
||
559 |
const PropertyCallbackInfo<Value>& args) { |
||
560 |
1 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
561 |
|||
562 |
// Still initializing |
||
563 |
✗✓ | 2 |
if (ctx->context_.IsEmpty()) |
564 |
1 |
return; |
|
565 |
|||
566 |
ContextifyContext::PropertySetterCallback( |
||
567 |
1 |
Uint32ToName(ctx->context(), index), value, args); |
|
568 |
} |
||
569 |
|||
570 |
// static |
||
571 |
1 |
void ContextifyContext::IndexedPropertyDescriptorCallback( |
|
572 |
uint32_t index, |
||
573 |
const PropertyCallbackInfo<Value>& args) { |
||
574 |
1 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
575 |
|||
576 |
// Still initializing |
||
577 |
✗✓ | 2 |
if (ctx->context_.IsEmpty()) |
578 |
1 |
return; |
|
579 |
|||
580 |
ContextifyContext::PropertyDescriptorCallback( |
||
581 |
1 |
Uint32ToName(ctx->context(), index), args); |
|
582 |
} |
||
583 |
|||
584 |
|||
585 |
1 |
void ContextifyContext::IndexedPropertyDefinerCallback( |
|
586 |
uint32_t index, |
||
587 |
const PropertyDescriptor& desc, |
||
588 |
const PropertyCallbackInfo<Value>& args) { |
||
589 |
1 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
|
590 |
|||
591 |
// Still initializing |
||
592 |
✗✓ | 2 |
if (ctx->context_.IsEmpty()) |
593 |
1 |
return; |
|
594 |
|||
595 |
ContextifyContext::PropertyDefinerCallback( |
||
596 |
1 |
Uint32ToName(ctx->context(), index), desc, args); |
|
597 |
} |
||
598 |
|||
599 |
// static |
||
600 |
void ContextifyContext::IndexedPropertyDeleterCallback( |
||
601 |
uint32_t index, |
||
602 |
const PropertyCallbackInfo<Boolean>& args) { |
||
603 |
ContextifyContext* ctx = ContextifyContext::Get(args); |
||
604 |
|||
605 |
// Still initializing |
||
606 |
if (ctx->context_.IsEmpty()) |
||
607 |
return; |
||
608 |
|||
609 |
Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), index); |
||
610 |
|||
611 |
if (success.FromMaybe(false)) |
||
612 |
return; |
||
613 |
|||
614 |
// Delete failed on the sandbox, intercept and do not delete on |
||
615 |
// the global object. |
||
616 |
args.GetReturnValue().Set(false); |
||
617 |
} |
||
618 |
|||
619 |
5128 |
void ContextifyScript::Init(Environment* env, Local<Object> target) { |
|
620 |
5128 |
HandleScope scope(env->isolate()); |
|
621 |
Local<String> class_name = |
||
622 |
5128 |
FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript"); |
|
623 |
|||
624 |
5128 |
Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New); |
|
625 |
10256 |
script_tmpl->InstanceTemplate()->SetInternalFieldCount(1); |
|
626 |
5128 |
script_tmpl->SetClassName(class_name); |
|
627 |
5128 |
env->SetProtoMethod(script_tmpl, "createCachedData", CreateCachedData); |
|
628 |
5128 |
env->SetProtoMethod(script_tmpl, "runInContext", RunInContext); |
|
629 |
5128 |
env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext); |
|
630 |
|||
631 |
target->Set(env->context(), class_name, |
||
632 |
25640 |
script_tmpl->GetFunction(env->context()).ToLocalChecked()).Check(); |
|
633 |
5128 |
env->set_script_context_constructor_template(script_tmpl); |
|
634 |
5128 |
} |
|
635 |
|||
636 |
1923 |
void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) { |
|
637 |
1923 |
Environment* env = Environment::GetCurrent(args); |
|
638 |
1923 |
Isolate* isolate = env->isolate(); |
|
639 |
1923 |
Local<Context> context = env->context(); |
|
640 |
|||
641 |
✗✓ | 1923 |
CHECK(args.IsConstructCall()); |
642 |
|||
643 |
1923 |
const int argc = args.Length(); |
|
644 |
✗✓ | 1923 |
CHECK_GE(argc, 2); |
645 |
|||
646 |
✗✓ | 5769 |
CHECK(args[0]->IsString()); |
647 |
3846 |
Local<String> code = args[0].As<String>(); |
|
648 |
|||
649 |
✗✓ | 5769 |
CHECK(args[1]->IsString()); |
650 |
3846 |
Local<String> filename = args[1].As<String>(); |
|
651 |
|||
652 |
Local<Integer> line_offset; |
||
653 |
Local<Integer> column_offset; |
||
654 |
Local<ArrayBufferView> cached_data_buf; |
||
655 |
1923 |
bool produce_cached_data = false; |
|
656 |
1923 |
Local<Context> parsing_context = context; |
|
657 |
|||
658 |
✓✗ | 1923 |
if (argc > 2) { |
659 |
// new ContextifyScript(code, filename, lineOffset, columnOffset, |
||
660 |
// cachedData, produceCachedData, parsingContext) |
||
661 |
✗✓ | 1923 |
CHECK_EQ(argc, 7); |
662 |
✗✓ | 3846 |
CHECK(args[2]->IsNumber()); |
663 |
3846 |
line_offset = args[2].As<Integer>(); |
|
664 |
✗✓ | 3846 |
CHECK(args[3]->IsNumber()); |
665 |
3846 |
column_offset = args[3].As<Integer>(); |
|
666 |
✓✓ | 5769 |
if (!args[4]->IsUndefined()) { |
667 |
✗✓ | 46 |
CHECK(args[4]->IsArrayBufferView()); |
668 |
46 |
cached_data_buf = args[4].As<ArrayBufferView>(); |
|
669 |
} |
||
670 |
✗✓ | 3846 |
CHECK(args[5]->IsBoolean()); |
671 |
3846 |
produce_cached_data = args[5]->IsTrue(); |
|
672 |
✓✓ | 5769 |
if (!args[6]->IsUndefined()) { |
673 |
✗✓ | 662 |
CHECK(args[6]->IsObject()); |
674 |
ContextifyContext* sandbox = |
||
675 |
ContextifyContext::ContextFromContextifiedSandbox( |
||
676 |
662 |
env, args[6].As<Object>()); |
|
677 |
✗✓ | 331 |
CHECK_NOT_NULL(sandbox); |
678 |
331 |
parsing_context = sandbox->context(); |
|
679 |
} |
||
680 |
} else { |
||
681 |
line_offset = Integer::New(isolate, 0); |
||
682 |
column_offset = Integer::New(isolate, 0); |
||
683 |
} |
||
684 |
|||
685 |
ContextifyScript* contextify_script = |
||
686 |
1923 |
new ContextifyScript(env, args.This()); |
|
687 |
|||
688 |
✓✓ | 3846 |
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( |
689 |
1923 |
TRACING_CATEGORY_NODE2(vm, script)) != 0) { |
|
690 |
7 |
Utf8Value fn(isolate, filename); |
|
691 |
✓✗✓✗ |
14 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( |
692 |
TRACING_CATEGORY_NODE2(vm, script), |
||
693 |
"ContextifyScript::New", |
||
694 |
contextify_script, |
||
695 |
7 |
"filename", TRACE_STR_COPY(*fn)); |
|
696 |
} |
||
697 |
|||
698 |
1923 |
ScriptCompiler::CachedData* cached_data = nullptr; |
|
699 |
✓✓ | 1923 |
if (!cached_data_buf.IsEmpty()) { |
700 |
46 |
ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents(); |
|
701 |
23 |
uint8_t* data = static_cast<uint8_t*>(contents.Data()); |
|
702 |
cached_data = new ScriptCompiler::CachedData( |
||
703 |
46 |
data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength()); |
|
704 |
} |
||
705 |
|||
706 |
Local<PrimitiveArray> host_defined_options = |
||
707 |
1923 |
PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength); |
|
708 |
host_defined_options->Set(isolate, loader::HostDefinedOptions::kType, |
||
709 |
3846 |
Number::New(isolate, loader::ScriptType::kScript)); |
|
710 |
host_defined_options->Set(isolate, loader::HostDefinedOptions::kID, |
||
711 |
3846 |
Number::New(isolate, contextify_script->id())); |
|
712 |
|||
713 |
ScriptOrigin origin(filename, |
||
714 |
line_offset, // line offset |
||
715 |
column_offset, // column offset |
||
716 |
True(isolate), // is cross origin |
||
717 |
Local<Integer>(), // script id |
||
718 |
Local<Value>(), // source map URL |
||
719 |
False(isolate), // is opaque (?) |
||
720 |
False(isolate), // is WASM |
||
721 |
False(isolate), // is ES Module |
||
722 |
1923 |
host_defined_options); |
|
723 |
ScriptCompiler::Source source(code, origin, cached_data); |
||
724 |
ScriptCompiler::CompileOptions compile_options = |
||
725 |
1923 |
ScriptCompiler::kNoCompileOptions; |
|
726 |
|||
727 |
✓✓ | 1923 |
if (source.GetCachedData() != nullptr) |
728 |
23 |
compile_options = ScriptCompiler::kConsumeCodeCache; |
|
729 |
|||
730 |
3645 |
TryCatchScope try_catch(env); |
|
731 |
✓✓ | 3645 |
ShouldNotAbortOnUncaughtScope no_abort_scope(env); |
732 |
✓✓ | 1722 |
Context::Scope scope(parsing_context); |
733 |
|||
734 |
MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript( |
||
735 |
isolate, |
||
736 |
&source, |
||
737 |
1923 |
compile_options); |
|
738 |
|||
739 |
✓✓ | 1923 |
if (v8_script.IsEmpty()) { |
740 |
201 |
errors::DecorateErrorStack(env, try_catch); |
|
741 |
201 |
no_abort_scope.Close(); |
|
742 |
✓✗ | 201 |
if (!try_catch.HasTerminated()) |
743 |
201 |
try_catch.ReThrow(); |
|
744 |
✓✓✗✓ |
402 |
TRACE_EVENT_NESTABLE_ASYNC_END0( |
745 |
TRACING_CATEGORY_NODE2(vm, script), |
||
746 |
"ContextifyScript::New", |
||
747 |
contextify_script); |
||
748 |
2124 |
return; |
|
749 |
} |
||
750 |
3444 |
contextify_script->script_.Reset(isolate, v8_script.ToLocalChecked()); |
|
751 |
|||
752 |
✓✓ | 1722 |
if (compile_options == ScriptCompiler::kConsumeCodeCache) { |
753 |
args.This()->Set( |
||
754 |
env->context(), |
||
755 |
env->cached_data_rejected_string(), |
||
756 |
138 |
Boolean::New(isolate, source.GetCachedData()->rejected)).Check(); |
|
757 |
✓✓ | 1699 |
} else if (produce_cached_data) { |
758 |
const ScriptCompiler::CachedData* cached_data = |
||
759 |
6 |
ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked()); |
|
760 |
6 |
bool cached_data_produced = cached_data != nullptr; |
|
761 |
✓✗ | 6 |
if (cached_data_produced) { |
762 |
MaybeLocal<Object> buf = Buffer::Copy( |
||
763 |
env, |
||
764 |
reinterpret_cast<const char*>(cached_data->data), |
||
765 |
6 |
cached_data->length); |
|
766 |
args.This()->Set(env->context(), |
||
767 |
env->cached_data_string(), |
||
768 |
30 |
buf.ToLocalChecked()).Check(); |
|
769 |
} |
||
770 |
args.This()->Set( |
||
771 |
env->context(), |
||
772 |
env->cached_data_produced_string(), |
||
773 |
36 |
Boolean::New(isolate, cached_data_produced)).Check(); |
|
774 |
} |
||
775 |
✓✓✓✓ |
3444 |
TRACE_EVENT_NESTABLE_ASYNC_END0( |
776 |
TRACING_CATEGORY_NODE2(vm, script), |
||
777 |
"ContextifyScript::New", |
||
778 |
1722 |
contextify_script); |
|
779 |
} |
||
780 |
|||
781 |
2699 |
bool ContextifyScript::InstanceOf(Environment* env, |
|
782 |
const Local<Value>& value) { |
||
783 |
✓✗✓✗ |
10796 |
return !value.IsEmpty() && |
784 |
✓✗ | 8097 |
env->script_context_constructor_template()->HasInstance(value); |
785 |
} |
||
786 |
|||
787 |
11 |
void ContextifyScript::CreateCachedData( |
|
788 |
const FunctionCallbackInfo<Value>& args) { |
||
789 |
11 |
Environment* env = Environment::GetCurrent(args); |
|
790 |
ContextifyScript* wrapped_script; |
||
791 |
✗✓ | 22 |
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder()); |
792 |
Local<UnboundScript> unbound_script = |
||
793 |
11 |
PersistentToLocal::Default(env->isolate(), wrapped_script->script_); |
|
794 |
std::unique_ptr<ScriptCompiler::CachedData> cached_data( |
||
795 |
11 |
ScriptCompiler::CreateCodeCache(unbound_script)); |
|
796 |
✗✓ | 11 |
if (!cached_data) { |
797 |
args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked()); |
||
798 |
} else { |
||
799 |
MaybeLocal<Object> buf = Buffer::Copy( |
||
800 |
env, |
||
801 |
11 |
reinterpret_cast<const char*>(cached_data->data), |
|
802 |
22 |
cached_data->length); |
|
803 |
22 |
args.GetReturnValue().Set(buf.ToLocalChecked()); |
|
804 |
11 |
} |
|
805 |
} |
||
806 |
|||
807 |
1092 |
void ContextifyScript::RunInThisContext( |
|
808 |
const FunctionCallbackInfo<Value>& args) { |
||
809 |
1092 |
Environment* env = Environment::GetCurrent(args); |
|
810 |
|||
811 |
ContextifyScript* wrapped_script; |
||
812 |
✗✓ | 2170 |
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder()); |
813 |
|||
814 |
✓✓✓✓ |
2184 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( |
815 |
TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script); |
||
816 |
|||
817 |
// TODO(addaleax): Use an options object or otherwise merge this with |
||
818 |
// RunInContext(). |
||
819 |
✗✓ | 1092 |
CHECK_EQ(args.Length(), 4); |
820 |
|||
821 |
✗✓ | 2184 |
CHECK(args[0]->IsNumber()); |
822 |
4368 |
int64_t timeout = args[0]->IntegerValue(env->context()).FromJust(); |
|
823 |
|||
824 |
✗✓ | 2184 |
CHECK(args[1]->IsBoolean()); |
825 |
2184 |
bool display_errors = args[1]->IsTrue(); |
|
826 |
|||
827 |
✗✓ | 2184 |
CHECK(args[2]->IsBoolean()); |
828 |
2184 |
bool break_on_sigint = args[2]->IsTrue(); |
|
829 |
|||
830 |
✗✓ | 2184 |
CHECK(args[3]->IsBoolean()); |
831 |
2184 |
bool break_on_first_line = args[3]->IsTrue(); |
|
832 |
|||
833 |
// Do the eval within this context |
||
834 |
EvalMachine(env, |
||
835 |
timeout, |
||
836 |
display_errors, |
||
837 |
break_on_sigint, |
||
838 |
break_on_first_line, |
||
839 |
1092 |
args); |
|
840 |
|||
841 |
✓✓✓✓ |
2156 |
TRACE_EVENT_NESTABLE_ASYNC_END0( |
842 |
TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script); |
||
843 |
} |
||
844 |
|||
845 |
1607 |
void ContextifyScript::RunInContext(const FunctionCallbackInfo<Value>& args) { |
|
846 |
1607 |
Environment* env = Environment::GetCurrent(args); |
|
847 |
|||
848 |
ContextifyScript* wrapped_script; |
||
849 |
✗✓ | 1607 |
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder()); |
850 |
|||
851 |
✗✓ | 1607 |
CHECK_EQ(args.Length(), 5); |
852 |
|||
853 |
✗✓ | 3214 |
CHECK(args[0]->IsObject()); |
854 |
3214 |
Local<Object> sandbox = args[0].As<Object>(); |
|
855 |
// Get the context from the sandbox |
||
856 |
ContextifyContext* contextify_context = |
||
857 |
1607 |
ContextifyContext::ContextFromContextifiedSandbox(env, sandbox); |
|
858 |
✗✓ | 1607 |
CHECK_NOT_NULL(contextify_context); |
859 |
|||
860 |
✗✓ | 3214 |
if (contextify_context->context().IsEmpty()) |
861 |
return; |
||
862 |
|||
863 |
✓✓✓✓ |
3214 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( |
864 |
TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script); |
||
865 |
|||
866 |
✗✓ | 3214 |
CHECK(args[1]->IsNumber()); |
867 |
6428 |
int64_t timeout = args[1]->IntegerValue(env->context()).FromJust(); |
|
868 |
|||
869 |
✗✓ | 3214 |
CHECK(args[2]->IsBoolean()); |
870 |
3214 |
bool display_errors = args[2]->IsTrue(); |
|
871 |
|||
872 |
✗✓ | 3214 |
CHECK(args[3]->IsBoolean()); |
873 |
3214 |
bool break_on_sigint = args[3]->IsTrue(); |
|
874 |
|||
875 |
✗✓ | 3214 |
CHECK(args[4]->IsBoolean()); |
876 |
3214 |
bool break_on_first_line = args[4]->IsTrue(); |
|
877 |
|||
878 |
// Do the eval within the context |
||
879 |
1607 |
Context::Scope context_scope(contextify_context->context()); |
|
880 |
EvalMachine(contextify_context->env(), |
||
881 |
timeout, |
||
882 |
display_errors, |
||
883 |
break_on_sigint, |
||
884 |
break_on_first_line, |
||
885 |
1607 |
args); |
|
886 |
|||
887 |
✓✓✓✓ |
3214 |
TRACE_EVENT_NESTABLE_ASYNC_END0( |
888 |
TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script); |
||
889 |
} |
||
890 |
|||
891 |
2699 |
bool ContextifyScript::EvalMachine(Environment* env, |
|
892 |
const int64_t timeout, |
||
893 |
const bool display_errors, |
||
894 |
const bool break_on_sigint, |
||
895 |
const bool break_on_first_line, |
||
896 |
const FunctionCallbackInfo<Value>& args) { |
||
897 |
✗✓ | 2699 |
if (!env->can_call_into_js()) |
898 |
return false; |
||
899 |
✗✓ | 2699 |
if (!ContextifyScript::InstanceOf(env, args.Holder())) { |
900 |
env->ThrowTypeError( |
||
901 |
"Script methods can only be called on script instances."); |
||
902 |
return false; |
||
903 |
} |
||
904 |
2699 |
TryCatchScope try_catch(env); |
|
905 |
ContextifyScript* wrapped_script; |
||
906 |
✗✓ | 2699 |
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false); |
907 |
Local<UnboundScript> unbound_script = |
||
908 |
2699 |
PersistentToLocal::Default(env->isolate(), wrapped_script->script_); |
|
909 |
2699 |
Local<Script> script = unbound_script->BindToCurrentContext(); |
|
910 |
|||
911 |
#if HAVE_INSPECTOR |
||
912 |
✓✓ | 2699 |
if (break_on_first_line) { |
913 |
9 |
env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start"); |
|
914 |
} |
||
915 |
#endif |
||
916 |
|||
917 |
MaybeLocal<Value> result; |
||
918 |
2699 |
bool timed_out = false; |
|
919 |
2699 |
bool received_signal = false; |
|
920 |
✓✓✗✓ |
2699 |
if (break_on_sigint && timeout != -1) { |
921 |
Watchdog wd(env->isolate(), timeout, &timed_out); |
||
922 |
SigintWatchdog swd(env->isolate(), &received_signal); |
||
923 |
result = script->Run(env->context()); |
||
924 |
✓✓ | 2699 |
} else if (break_on_sigint) { |
925 |
96 |
SigintWatchdog swd(env->isolate(), &received_signal); |
|
926 |
192 |
result = script->Run(env->context()); |
|
927 |
✓✓ | 2603 |
} else if (timeout != -1) { |
928 |
1015 |
Watchdog wd(env->isolate(), timeout, &timed_out); |
|
929 |
2030 |
result = script->Run(env->context()); |
|
930 |
} else { |
||
931 |
3176 |
result = script->Run(env->context()); |
|
932 |
} |
||
933 |
|||
934 |
// Convert the termination exception into a regular exception. |
||
935 |
✓✓✓✓ |
2685 |
if (timed_out || received_signal) { |
936 |
✗✓✗✗ ✗✓ |
521 |
if (!env->is_main_thread() && env->is_stopping()) |
937 |
return false; |
||
938 |
521 |
env->isolate()->CancelTerminateExecution(); |
|
939 |
// It is possible that execution was terminated by another timeout in |
||
940 |
// which this timeout is nested, so check whether one of the watchdogs |
||
941 |
// from this invocation is responsible for termination. |
||
942 |
✓✓ | 521 |
if (timed_out) { |
943 |
510 |
node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout); |
|
944 |
✓✗ | 11 |
} else if (received_signal) { |
945 |
11 |
node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env); |
|
946 |
} |
||
947 |
} |
||
948 |
|||
949 |
✓✓ | 2685 |
if (try_catch.HasCaught()) { |
950 |
✓✓✓✓ ✓✓ |
626 |
if (!timed_out && !received_signal && display_errors) { |
951 |
// We should decorate non-termination exceptions |
||
952 |
64 |
errors::DecorateErrorStack(env, try_catch); |
|
953 |
} |
||
954 |
|||
955 |
// If there was an exception thrown during script execution, re-throw it. |
||
956 |
// If one of the above checks threw, re-throw the exception instead of |
||
957 |
// letting try_catch catch it. |
||
958 |
// If execution has been terminated, but not by one of the watchdogs from |
||
959 |
// this invocation, this will re-throw a `null` value. |
||
960 |
✓✓ | 626 |
if (!try_catch.HasTerminated()) |
961 |
623 |
try_catch.ReThrow(); |
|
962 |
|||
963 |
626 |
return false; |
|
964 |
} |
||
965 |
|||
966 |
4118 |
args.GetReturnValue().Set(result.ToLocalChecked()); |
|
967 |
2059 |
return true; |
|
968 |
} |
||
969 |
|||
970 |
|||
971 |
1923 |
ContextifyScript::ContextifyScript(Environment* env, Local<Object> object) |
|
972 |
: BaseObject(env, object), |
||
973 |
3846 |
id_(env->get_next_script_id()) { |
|
974 |
1923 |
MakeWeak(); |
|
975 |
1923 |
env->id_to_script_map.emplace(id_, this); |
|
976 |
1923 |
} |
|
977 |
|||
978 |
|||
979 |
7400 |
ContextifyScript::~ContextifyScript() { |
|
980 |
1850 |
env()->id_to_script_map.erase(id_); |
|
981 |
✗✓ | 3700 |
} |
982 |
|||
983 |
|||
984 |
40148 |
void ContextifyContext::CompileFunction( |
|
985 |
const FunctionCallbackInfo<Value>& args) { |
||
986 |
40148 |
Environment* env = Environment::GetCurrent(args); |
|
987 |
40148 |
Isolate* isolate = env->isolate(); |
|
988 |
40148 |
Local<Context> context = env->context(); |
|
989 |
|||
990 |
// Argument 1: source code |
||
991 |
✗✓ | 120444 |
CHECK(args[0]->IsString()); |
992 |
80296 |
Local<String> code = args[0].As<String>(); |
|
993 |
|||
994 |
// Argument 2: filename |
||
995 |
✗✓ | 120444 |
CHECK(args[1]->IsString()); |
996 |
80296 |
Local<String> filename = args[1].As<String>(); |
|
997 |
|||
998 |
// Argument 3: line offset |
||
999 |
✗✓ | 80296 |
CHECK(args[2]->IsNumber()); |
1000 |
80296 |
Local<Integer> line_offset = args[2].As<Integer>(); |
|
1001 |
|||
1002 |
// Argument 4: column offset |
||
1003 |
✗✓ | 80296 |
CHECK(args[3]->IsNumber()); |
1004 |
80296 |
Local<Integer> column_offset = args[3].As<Integer>(); |
|
1005 |
|||
1006 |
// Argument 5: cached data (optional) |
||
1007 |
Local<ArrayBufferView> cached_data_buf; |
||
1008 |
✗✓ | 120444 |
if (!args[4]->IsUndefined()) { |
1009 |
CHECK(args[4]->IsArrayBufferView()); |
||
1010 |
cached_data_buf = args[4].As<ArrayBufferView>(); |
||
1011 |
} |
||
1012 |
|||
1013 |
// Argument 6: produce cache data |
||
1014 |
✗✓ | 80296 |
CHECK(args[5]->IsBoolean()); |
1015 |
80296 |
bool produce_cached_data = args[5]->IsTrue(); |
|
1016 |
|||
1017 |
// Argument 7: parsing context (optional) |
||
1018 |
Local<Context> parsing_context; |
||
1019 |
✓✓ | 120444 |
if (!args[6]->IsUndefined()) { |
1020 |
✗✓ | 4 |
CHECK(args[6]->IsObject()); |
1021 |
ContextifyContext* sandbox = |
||
1022 |
ContextifyContext::ContextFromContextifiedSandbox( |
||
1023 |
4 |
env, args[6].As<Object>()); |
|
1024 |
✗✓ | 2 |
CHECK_NOT_NULL(sandbox); |
1025 |
2 |
parsing_context = sandbox->context(); |
|
1026 |
} else { |
||
1027 |
40146 |
parsing_context = context; |
|
1028 |
} |
||
1029 |
|||
1030 |
// Argument 8: context extensions (optional) |
||
1031 |
Local<Array> context_extensions_buf; |
||
1032 |
✓✗ | 120444 |
if (!args[7]->IsUndefined()) { |
1033 |
✗✓ | 80296 |
CHECK(args[7]->IsArray()); |
1034 |
80296 |
context_extensions_buf = args[7].As<Array>(); |
|
1035 |
} |
||
1036 |
|||
1037 |
// Argument 9: params for the function (optional) |
||
1038 |
Local<Array> params_buf; |
||
1039 |
✓✓ | 120444 |
if (!args[8]->IsUndefined()) { |
1040 |
✗✓ | 80284 |
CHECK(args[8]->IsArray()); |
1041 |
80284 |
params_buf = args[8].As<Array>(); |
|
1042 |
} |
||
1043 |
|||
1044 |
// Read cache from cached data buffer |
||
1045 |
40148 |
ScriptCompiler::CachedData* cached_data = nullptr; |
|
1046 |
✗✓ | 40148 |
if (!cached_data_buf.IsEmpty()) { |
1047 |
ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents(); |
||
1048 |
uint8_t* data = static_cast<uint8_t*>(contents.Data()); |
||
1049 |
cached_data = new ScriptCompiler::CachedData( |
||
1050 |
data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength()); |
||
1051 |
} |
||
1052 |
|||
1053 |
// Get the function id |
||
1054 |
40148 |
uint32_t id = env->get_next_function_id(); |
|
1055 |
|||
1056 |
// Set host_defined_options |
||
1057 |
Local<PrimitiveArray> host_defined_options = |
||
1058 |
40148 |
PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength); |
|
1059 |
host_defined_options->Set( |
||
1060 |
isolate, |
||
1061 |
loader::HostDefinedOptions::kType, |
||
1062 |
80296 |
Number::New(isolate, loader::ScriptType::kFunction)); |
|
1063 |
host_defined_options->Set( |
||
1064 |
80296 |
isolate, loader::HostDefinedOptions::kID, Number::New(isolate, id)); |
|
1065 |
|||
1066 |
ScriptOrigin origin(filename, |
||
1067 |
line_offset, // line offset |
||
1068 |
column_offset, // column offset |
||
1069 |
True(isolate), // is cross origin |
||
1070 |
Local<Integer>(), // script id |
||
1071 |
Local<Value>(), // source map URL |
||
1072 |
False(isolate), // is opaque (?) |
||
1073 |
False(isolate), // is WASM |
||
1074 |
False(isolate), // is ES Module |
||
1075 |
40148 |
host_defined_options); |
|
1076 |
|||
1077 |
ScriptCompiler::Source source(code, origin, cached_data); |
||
1078 |
ScriptCompiler::CompileOptions options; |
||
1079 |
✓✗ | 40148 |
if (source.GetCachedData() == nullptr) { |
1080 |
40148 |
options = ScriptCompiler::kNoCompileOptions; |
|
1081 |
} else { |
||
1082 |
options = ScriptCompiler::kConsumeCodeCache; |
||
1083 |
} |
||
1084 |
|||
1085 |
80266 |
TryCatchScope try_catch(env); |
|
1086 |
✓✓ | 40118 |
Context::Scope scope(parsing_context); |
1087 |
|||
1088 |
// Read context extensions from buffer |
||
1089 |
80266 |
std::vector<Local<Object>> context_extensions; |
|
1090 |
✓✗ | 40148 |
if (!context_extensions_buf.IsEmpty()) { |
1091 |
✓✓ | 80298 |
for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) { |
1092 |
Local<Value> val; |
||
1093 |
✗✓ | 2 |
if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return; |
1094 |
✗✓ | 1 |
CHECK(val->IsObject()); |
1095 |
1 |
context_extensions.push_back(val.As<Object>()); |
|
1096 |
} |
||
1097 |
} |
||
1098 |
|||
1099 |
// Read params from params buffer |
||
1100 |
✓✓ | 80266 |
std::vector<Local<String>> params; |
1101 |
✓✓ | 40148 |
if (!params_buf.IsEmpty()) { |
1102 |
✓✓ | 481644 |
for (uint32_t n = 0; n < params_buf->Length(); n++) { |
1103 |
Local<Value> val; |
||
1104 |
✗✓ | 401360 |
if (!params_buf->Get(context, n).ToLocal(&val)) return; |
1105 |
✗✓ | 401360 |
CHECK(val->IsString()); |
1106 |
200680 |
params.push_back(val.As<String>()); |
|
1107 |
} |
||
1108 |
} |
||
1109 |
|||
1110 |
Local<ScriptOrModule> script; |
||
1111 |
MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext( |
||
1112 |
parsing_context, &source, params.size(), params.data(), |
||
1113 |
context_extensions.size(), context_extensions.data(), options, |
||
1114 |
40148 |
v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason, &script); |
|
1115 |
|||
1116 |
✓✓ | 40148 |
if (maybe_fn.IsEmpty()) { |
1117 |
✓✗✓✗ ✓✗ |
30 |
if (try_catch.HasCaught() && !try_catch.HasTerminated()) { |
1118 |
30 |
errors::DecorateErrorStack(env, try_catch); |
|
1119 |
30 |
try_catch.ReThrow(); |
|
1120 |
} |
||
1121 |
30 |
return; |
|
1122 |
} |
||
1123 |
40118 |
Local<Function> fn = maybe_fn.ToLocalChecked(); |
|
1124 |
|||
1125 |
Local<Object> cache_key; |
||
1126 |
✗✓ | 80236 |
if (!env->compiled_fn_entry_template()->NewInstance( |
1127 |
160472 |
context).ToLocal(&cache_key)) { |
|
1128 |
return; |
||
1129 |
} |
||
1130 |
40118 |
CompiledFnEntry* entry = new CompiledFnEntry(env, cache_key, id, script); |
|
1131 |
40118 |
env->id_to_function_map.emplace(id, entry); |
|
1132 |
|||
1133 |
40118 |
Local<Object> result = Object::New(isolate); |
|
1134 |
✗✓ | 120354 |
if (result->Set(parsing_context, env->function_string(), fn).IsNothing()) |
1135 |
return; |
||
1136 |
✗✓ | 160472 |
if (result->Set(parsing_context, env->cache_key_string(), cache_key) |
1137 |
80236 |
.IsNothing()) |
|
1138 |
return; |
||
1139 |
|||
1140 |
✗✓ | 40118 |
if (produce_cached_data) { |
1141 |
const std::unique_ptr<ScriptCompiler::CachedData> cached_data( |
||
1142 |
ScriptCompiler::CreateCodeCacheForFunction(fn)); |
||
1143 |
bool cached_data_produced = cached_data != nullptr; |
||
1144 |
if (cached_data_produced) { |
||
1145 |
MaybeLocal<Object> buf = Buffer::Copy( |
||
1146 |
env, |
||
1147 |
reinterpret_cast<const char*>(cached_data->data), |
||
1148 |
cached_data->length); |
||
1149 |
if (result |
||
1150 |
->Set(parsing_context, |
||
1151 |
env->cached_data_string(), |
||
1152 |
buf.ToLocalChecked()) |
||
1153 |
.IsNothing()) |
||
1154 |
return; |
||
1155 |
} |
||
1156 |
if (result |
||
1157 |
->Set(parsing_context, |
||
1158 |
env->cached_data_produced_string(), |
||
1159 |
Boolean::New(isolate, cached_data_produced)) |
||
1160 |
.IsNothing()) |
||
1161 |
return; |
||
1162 |
} |
||
1163 |
|||
1164 |
✓✓ | 120354 |
args.GetReturnValue().Set(result); |
1165 |
} |
||
1166 |
|||
1167 |
66 |
void CompiledFnEntry::WeakCallback( |
|
1168 |
const WeakCallbackInfo<CompiledFnEntry>& data) { |
||
1169 |
66 |
CompiledFnEntry* entry = data.GetParameter(); |
|
1170 |
✓✗ | 66 |
delete entry; |
1171 |
66 |
} |
|
1172 |
|||
1173 |
40118 |
CompiledFnEntry::CompiledFnEntry(Environment* env, |
|
1174 |
Local<Object> object, |
||
1175 |
uint32_t id, |
||
1176 |
Local<ScriptOrModule> script) |
||
1177 |
: BaseObject(env, object), |
||
1178 |
id_(id), |
||
1179 |
40118 |
script_(env->isolate(), script) { |
|
1180 |
40118 |
script_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); |
|
1181 |
40118 |
} |
|
1182 |
|||
1183 |
151360 |
CompiledFnEntry::~CompiledFnEntry() { |
|
1184 |
37840 |
env()->id_to_function_map.erase(id_); |
|
1185 |
37840 |
script_.ClearWeak(); |
|
1186 |
✗✓ | 75680 |
} |
1187 |
|||
1188 |
93 |
static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) { |
|
1189 |
93 |
int ret = SigintWatchdogHelper::GetInstance()->Start(); |
|
1190 |
279 |
args.GetReturnValue().Set(ret == 0); |
|
1191 |
93 |
} |
|
1192 |
|||
1193 |
91 |
static void StopSigintWatchdog(const FunctionCallbackInfo<Value>& args) { |
|
1194 |
91 |
bool had_pending_signals = SigintWatchdogHelper::GetInstance()->Stop(); |
|
1195 |
273 |
args.GetReturnValue().Set(had_pending_signals); |
|
1196 |
91 |
} |
|
1197 |
|||
1198 |
3 |
static void WatchdogHasPendingSigint(const FunctionCallbackInfo<Value>& args) { |
|
1199 |
3 |
bool ret = SigintWatchdogHelper::GetInstance()->HasPendingSignal(); |
|
1200 |
9 |
args.GetReturnValue().Set(ret); |
|
1201 |
3 |
} |
|
1202 |
|||
1203 |
5128 |
void Initialize(Local<Object> target, |
|
1204 |
Local<Value> unused, |
||
1205 |
Local<Context> context, |
||
1206 |
void* priv) { |
||
1207 |
5128 |
Environment* env = Environment::GetCurrent(context); |
|
1208 |
5128 |
ContextifyContext::Init(env, target); |
|
1209 |
5128 |
ContextifyScript::Init(env, target); |
|
1210 |
|||
1211 |
5128 |
env->SetMethod(target, "startSigintWatchdog", StartSigintWatchdog); |
|
1212 |
5128 |
env->SetMethod(target, "stopSigintWatchdog", StopSigintWatchdog); |
|
1213 |
// Used in tests. |
||
1214 |
env->SetMethodNoSideEffect( |
||
1215 |
5128 |
target, "watchdogHasPendingSigint", WatchdogHasPendingSigint); |
|
1216 |
|||
1217 |
{ |
||
1218 |
5128 |
Local<FunctionTemplate> tpl = FunctionTemplate::New(env->isolate()); |
|
1219 |
10256 |
tpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "CompiledFnEntry")); |
|
1220 |
10256 |
tpl->InstanceTemplate()->SetInternalFieldCount(1); |
|
1221 |
|||
1222 |
5128 |
env->set_compiled_fn_entry_template(tpl->InstanceTemplate()); |
|
1223 |
} |
||
1224 |
5128 |
} |
|
1225 |
|||
1226 |
} // namespace contextify |
||
1227 |
} // namespace node |
||
1228 |
|||
1229 |
4981 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize) |
Generated by: GCOVR (Version 3.4) |