mini_racer 0.4.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -2
- data/.gitignore +1 -0
- data/.travis.yml +0 -8
- data/CHANGELOG +30 -0
- data/README.md +16 -3
- data/Rakefile +1 -1
- data/ext/mini_racer_extension/extconf.rb +25 -13
- data/ext/mini_racer_extension/mini_racer_extension.cc +381 -160
- data/ext/mini_racer_loader/extconf.rb +1 -1
- data/lib/mini_racer/version.rb +2 -2
- data/lib/mini_racer.rb +15 -6
- data/mini_racer.gemspec +1 -3
- metadata +10 -10
@@ -2,6 +2,7 @@
|
|
2
2
|
#include <ruby.h>
|
3
3
|
#include <ruby/thread.h>
|
4
4
|
#include <ruby/io.h>
|
5
|
+
#include <ruby/version.h>
|
5
6
|
#include <v8.h>
|
6
7
|
#include <v8-profiler.h>
|
7
8
|
#include <libplatform/libplatform.h>
|
@@ -11,6 +12,15 @@
|
|
11
12
|
#include <mutex>
|
12
13
|
#include <atomic>
|
13
14
|
#include <math.h>
|
15
|
+
#include <errno.h>
|
16
|
+
|
17
|
+
/* workaround C Ruby <= 2.x problems w/ clang in C++ mode */
|
18
|
+
#if defined(ENGINE_IS_CRUBY) && \
|
19
|
+
RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR <= 6
|
20
|
+
# define MR_METHOD_FUNC(fn) RUBY_METHOD_FUNC(fn)
|
21
|
+
#else
|
22
|
+
# define MR_METHOD_FUNC(fn) fn
|
23
|
+
#endif
|
14
24
|
|
15
25
|
using namespace v8;
|
16
26
|
|
@@ -115,6 +125,7 @@ typedef struct {
|
|
115
125
|
useconds_t timeout;
|
116
126
|
EvalResult* result;
|
117
127
|
size_t max_memory;
|
128
|
+
size_t marshal_stackdepth;
|
118
129
|
} EvalParams;
|
119
130
|
|
120
131
|
typedef struct {
|
@@ -126,13 +137,152 @@ typedef struct {
|
|
126
137
|
Local<Value> *argv;
|
127
138
|
EvalResult result;
|
128
139
|
size_t max_memory;
|
140
|
+
size_t marshal_stackdepth;
|
129
141
|
} FunctionCall;
|
130
142
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
143
|
+
class IsolateData {
|
144
|
+
public:
|
145
|
+
enum Flag {
|
146
|
+
// first flags are bitfield
|
147
|
+
// max count: sizeof(uintptr_t) * 8
|
148
|
+
IN_GVL, // whether we are inside of ruby gvl or not
|
149
|
+
DO_TERMINATE, // terminate as soon as possible
|
150
|
+
MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit
|
151
|
+
MEM_SOFTLIMIT_MAX, // maximum memory value
|
152
|
+
MARSHAL_STACKDEPTH_REACHED, // we've hit our max stack depth
|
153
|
+
MARSHAL_STACKDEPTH_VALUE, // current stackdepth
|
154
|
+
MARSHAL_STACKDEPTH_MAX, // maximum stack depth during marshal
|
155
|
+
};
|
156
|
+
|
157
|
+
static void Init(Isolate *isolate) {
|
158
|
+
// zero out all fields in the bitfield
|
159
|
+
isolate->SetData(0, 0);
|
160
|
+
}
|
161
|
+
|
162
|
+
static uintptr_t Get(Isolate *isolate, Flag flag) {
|
163
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
164
|
+
switch (flag) {
|
165
|
+
case IN_GVL: return u.IN_GVL;
|
166
|
+
case DO_TERMINATE: return u.DO_TERMINATE;
|
167
|
+
case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED;
|
168
|
+
case MEM_SOFTLIMIT_MAX: return static_cast<uintptr_t>(u.MEM_SOFTLIMIT_MAX) << 10;
|
169
|
+
case MARSHAL_STACKDEPTH_REACHED: return u.MARSHAL_STACKDEPTH_REACHED;
|
170
|
+
case MARSHAL_STACKDEPTH_VALUE: return u.MARSHAL_STACKDEPTH_VALUE;
|
171
|
+
case MARSHAL_STACKDEPTH_MAX: return u.MARSHAL_STACKDEPTH_MAX;
|
172
|
+
}
|
173
|
+
|
174
|
+
// avoid compiler warning
|
175
|
+
return u.IN_GVL;
|
176
|
+
}
|
177
|
+
|
178
|
+
static void Set(Isolate *isolate, Flag flag, uintptr_t value) {
|
179
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
180
|
+
switch (flag) {
|
181
|
+
case IN_GVL: u.IN_GVL = value; break;
|
182
|
+
case DO_TERMINATE: u.DO_TERMINATE = value; break;
|
183
|
+
case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break;
|
184
|
+
// drop least significant 10 bits 'store memory amount in kb'
|
185
|
+
case MEM_SOFTLIMIT_MAX: u.MEM_SOFTLIMIT_MAX = value >> 10; break;
|
186
|
+
case MARSHAL_STACKDEPTH_REACHED: u.MARSHAL_STACKDEPTH_REACHED = value; break;
|
187
|
+
case MARSHAL_STACKDEPTH_VALUE: u.MARSHAL_STACKDEPTH_VALUE = value; break;
|
188
|
+
case MARSHAL_STACKDEPTH_MAX: u.MARSHAL_STACKDEPTH_MAX = value; break;
|
189
|
+
}
|
190
|
+
isolate->SetData(0, reinterpret_cast<void*>(u.dataPtr));
|
191
|
+
}
|
192
|
+
|
193
|
+
private:
|
194
|
+
struct Bitfield {
|
195
|
+
// WARNING: this would explode on platforms below 64 bit ptrs
|
196
|
+
// compiler will fail here, making it clear for them.
|
197
|
+
// Additionally, using the other part of the union to reinterpret the
|
198
|
+
// memory is undefined behavior according to spec, but is / has been stable
|
199
|
+
// across major compilers for decades.
|
200
|
+
static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit.");
|
201
|
+
union {
|
202
|
+
uint64_t dataPtr: 64;
|
203
|
+
// order in this struct matters. For cpu performance keep larger subobjects
|
204
|
+
// aligned on their boundaries (8 16 32), try not to straddle
|
205
|
+
struct {
|
206
|
+
size_t MEM_SOFTLIMIT_MAX:22;
|
207
|
+
bool IN_GVL:1;
|
208
|
+
bool DO_TERMINATE:1;
|
209
|
+
bool MEM_SOFTLIMIT_REACHED:1;
|
210
|
+
bool MARSHAL_STACKDEPTH_REACHED:1;
|
211
|
+
uint8_t :0; // align to next 8bit bound
|
212
|
+
size_t MARSHAL_STACKDEPTH_VALUE:10;
|
213
|
+
uint8_t :0; // align to next 8bit bound
|
214
|
+
size_t MARSHAL_STACKDEPTH_MAX:10;
|
215
|
+
};
|
216
|
+
};
|
217
|
+
};
|
218
|
+
};
|
219
|
+
|
220
|
+
struct StackCounter {
|
221
|
+
static void Reset(Isolate* isolate) {
|
222
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0) {
|
223
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
224
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
static void SetMax(Isolate* isolate, size_t marshalMaxStackDepth) {
|
229
|
+
if (marshalMaxStackDepth > 0) {
|
230
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX, marshalMaxStackDepth);
|
231
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
232
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
StackCounter(Isolate* isolate) {
|
237
|
+
this->isActive = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0;
|
238
|
+
|
239
|
+
if (this->isActive) {
|
240
|
+
this->isolate = isolate;
|
241
|
+
this->IncDepth(1);
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
bool IsTooDeep() {
|
246
|
+
if (!this->IsActive()) {
|
247
|
+
return false;
|
248
|
+
}
|
249
|
+
|
250
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
251
|
+
size_t maxDepth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_MAX);
|
252
|
+
if (depth > maxDepth) {
|
253
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, true);
|
254
|
+
return true;
|
255
|
+
}
|
256
|
+
|
257
|
+
return false;
|
258
|
+
}
|
259
|
+
|
260
|
+
bool IsActive() {
|
261
|
+
return this->isActive && !IsolateData::Get(this->isolate, IsolateData::DO_TERMINATE);
|
262
|
+
}
|
263
|
+
|
264
|
+
~StackCounter() {
|
265
|
+
if (this->IsActive()) {
|
266
|
+
this->IncDepth(-1);
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
private:
|
271
|
+
Isolate* isolate;
|
272
|
+
bool isActive;
|
273
|
+
|
274
|
+
void IncDepth(int direction) {
|
275
|
+
int inc = direction > 0 ? 1 : -1;
|
276
|
+
|
277
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
278
|
+
|
279
|
+
// don't decrement past 0
|
280
|
+
if (inc > 0 || depth > 0) {
|
281
|
+
depth += inc;
|
282
|
+
}
|
283
|
+
|
284
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, depth);
|
285
|
+
}
|
136
286
|
};
|
137
287
|
|
138
288
|
static VALUE rb_cContext;
|
@@ -159,6 +309,29 @@ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
|
159
309
|
static bool ruby_exiting = false; // guarded by exit_lock
|
160
310
|
static bool single_threaded = false;
|
161
311
|
|
312
|
+
static void mark_context(void *);
|
313
|
+
static void deallocate(void *);
|
314
|
+
static size_t context_memsize(const void *);
|
315
|
+
static const rb_data_type_t context_type = {
|
316
|
+
"mini_racer/context_info",
|
317
|
+
{ mark_context, deallocate, context_memsize }
|
318
|
+
};
|
319
|
+
|
320
|
+
static void deallocate_snapshot(void *);
|
321
|
+
static size_t snapshot_memsize(const void *);
|
322
|
+
static const rb_data_type_t snapshot_type = {
|
323
|
+
"mini_racer/snapshot_info",
|
324
|
+
{ NULL, deallocate_snapshot, snapshot_memsize }
|
325
|
+
};
|
326
|
+
|
327
|
+
static void mark_isolate(void *);
|
328
|
+
static void deallocate_isolate(void *);
|
329
|
+
static size_t isolate_memsize(const void *);
|
330
|
+
static const rb_data_type_t isolate_type = {
|
331
|
+
"mini_racer/isolate_info",
|
332
|
+
{ mark_isolate, deallocate_isolate, isolate_memsize }
|
333
|
+
};
|
334
|
+
|
162
335
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
163
336
|
bool platform_already_initialized = false;
|
164
337
|
|
@@ -173,7 +346,7 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
173
346
|
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
174
347
|
single_threaded = true;
|
175
348
|
}
|
176
|
-
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (
|
349
|
+
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
|
177
350
|
} else {
|
178
351
|
platform_already_initialized = true;
|
179
352
|
}
|
@@ -196,7 +369,11 @@ static void init_v8() {
|
|
196
369
|
|
197
370
|
if (current_platform == NULL) {
|
198
371
|
V8::InitializeICU();
|
199
|
-
|
372
|
+
if (single_threaded) {
|
373
|
+
current_platform = platform::NewSingleThreadedDefaultPlatform();
|
374
|
+
} else {
|
375
|
+
current_platform = platform::NewDefaultPlatform();
|
376
|
+
}
|
200
377
|
V8::InitializePlatform(current_platform.get());
|
201
378
|
V8::Initialize();
|
202
379
|
}
|
@@ -205,16 +382,18 @@ static void init_v8() {
|
|
205
382
|
}
|
206
383
|
|
207
384
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
208
|
-
if((
|
385
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
386
|
+
return;
|
387
|
+
}
|
209
388
|
|
210
|
-
size_t softlimit =
|
389
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
211
390
|
|
212
391
|
HeapStatistics stats;
|
213
392
|
isolate->GetHeapStatistics(&stats);
|
214
393
|
size_t used = stats.used_heap_size();
|
215
394
|
|
216
395
|
if(used > softlimit) {
|
217
|
-
isolate
|
396
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
218
397
|
isolate->TerminateExecution();
|
219
398
|
}
|
220
399
|
}
|
@@ -239,28 +418,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
239
418
|
Local<Value> local_value = v8res.ToLocalChecked();
|
240
419
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
241
420
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
242
|
-
|
243
|
-
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
244
|
-
.ToLocalChecked().As<Object>();
|
421
|
+
MaybeLocal<v8::Value> ml = context->Global()->Get(
|
422
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"));
|
245
423
|
|
246
|
-
|
247
|
-
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
248
|
-
.ToLocalChecked().As<Function>();
|
249
|
-
|
250
|
-
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
251
|
-
const unsigned argc = 1;
|
252
|
-
Local<Value> argv[argc] = { object };
|
253
|
-
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
254
|
-
|
255
|
-
if (json.IsEmpty()) {
|
424
|
+
if (ml.IsEmpty()) { // exception
|
256
425
|
evalRes.executed = false;
|
257
426
|
} else {
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
427
|
+
Local<Object> JSON = ml.ToLocalChecked().As<Object>();
|
428
|
+
|
429
|
+
Local<Function> stringify = JSON->Get(
|
430
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
431
|
+
.ToLocalChecked().As<Function>();
|
432
|
+
|
433
|
+
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
434
|
+
const unsigned argc = 1;
|
435
|
+
Local<Value> argv[argc] = { object };
|
436
|
+
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
437
|
+
|
438
|
+
if (json.IsEmpty()) {
|
439
|
+
evalRes.executed = false;
|
440
|
+
} else {
|
441
|
+
evalRes.json = true;
|
442
|
+
Persistent<Value>* persistent = new Persistent<Value>();
|
443
|
+
persistent->Reset(isolate, json.ToLocalChecked());
|
444
|
+
evalRes.value = persistent;
|
445
|
+
}
|
262
446
|
}
|
263
|
-
|
264
447
|
} else {
|
265
448
|
Persistent<Value>* persistent = new Persistent<Value>();
|
266
449
|
persistent->Reset(isolate, local_value);
|
@@ -311,7 +494,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
311
494
|
}
|
312
495
|
}
|
313
496
|
|
314
|
-
void*
|
497
|
+
static void*
|
315
498
|
nogvl_context_eval(void* arg) {
|
316
499
|
|
317
500
|
EvalParams* eval_params = (EvalParams*)arg;
|
@@ -326,14 +509,15 @@ nogvl_context_eval(void* arg) {
|
|
326
509
|
Context::Scope context_scope(context);
|
327
510
|
v8::ScriptOrigin *origin = NULL;
|
328
511
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
512
|
+
IsolateData::Init(isolate);
|
513
|
+
|
514
|
+
if (eval_params->max_memory > 0) {
|
515
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
|
516
|
+
if (!isolate_info->added_gc_cb) {
|
517
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
518
|
+
isolate_info->added_gc_cb = true;
|
519
|
+
}
|
520
|
+
}
|
337
521
|
|
338
522
|
MaybeLocal<Script> parsed_script;
|
339
523
|
|
@@ -359,12 +543,8 @@ nogvl_context_eval(void* arg) {
|
|
359
543
|
result->message->Reset(isolate, trycatch.Exception());
|
360
544
|
} else {
|
361
545
|
// parsing successful
|
362
|
-
if (eval_params->
|
363
|
-
isolate
|
364
|
-
if (!isolate_info->added_gc_cb) {
|
365
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
366
|
-
isolate_info->added_gc_cb = true;
|
367
|
-
}
|
546
|
+
if (eval_params->marshal_stackdepth > 0) {
|
547
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
368
548
|
}
|
369
549
|
|
370
550
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -372,7 +552,7 @@ nogvl_context_eval(void* arg) {
|
|
372
552
|
|
373
553
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
374
554
|
|
375
|
-
isolate
|
555
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
376
556
|
|
377
557
|
return NULL;
|
378
558
|
}
|
@@ -390,6 +570,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
390
570
|
Isolate::Scope isolate_scope(isolate);
|
391
571
|
HandleScope scope(isolate);
|
392
572
|
|
573
|
+
StackCounter stackCounter(isolate);
|
574
|
+
|
575
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
|
576
|
+
return Qnil;
|
577
|
+
}
|
578
|
+
|
579
|
+
if (stackCounter.IsTooDeep()) {
|
580
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
581
|
+
isolate->TerminateExecution();
|
582
|
+
return Qnil;
|
583
|
+
}
|
584
|
+
|
393
585
|
if (value->IsNull() || value->IsUndefined()){
|
394
586
|
return Qnil;
|
395
587
|
}
|
@@ -478,7 +670,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
478
670
|
rb_enc_find("utf-8")
|
479
671
|
);
|
480
672
|
|
481
|
-
return
|
673
|
+
return rb_str_intern(str_symbol);
|
482
674
|
}
|
483
675
|
|
484
676
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
@@ -532,7 +724,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
532
724
|
case T_FLOAT:
|
533
725
|
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
534
726
|
case T_STRING:
|
535
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (
|
727
|
+
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
536
728
|
case T_NIL:
|
537
729
|
return scope.Escape(Null(isolate));
|
538
730
|
case T_TRUE:
|
@@ -543,7 +735,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
543
735
|
length = RARRAY_LEN(value);
|
544
736
|
array = Array::New(isolate, (int)length);
|
545
737
|
for(i=0; i<length; i++) {
|
546
|
-
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
738
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
739
|
+
(void)(success);
|
547
740
|
}
|
548
741
|
return scope.Escape(array);
|
549
742
|
case T_HASH:
|
@@ -552,13 +745,14 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
552
745
|
length = RARRAY_LEN(hash_as_array);
|
553
746
|
for(i=0; i<length; i++) {
|
554
747
|
pair = rb_ary_entry(hash_as_array, i);
|
555
|
-
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
748
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
556
749
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
750
|
+
(void)(success);
|
557
751
|
}
|
558
752
|
return scope.Escape(object);
|
559
753
|
case T_SYMBOL:
|
560
754
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
561
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (
|
755
|
+
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
562
756
|
case T_DATA:
|
563
757
|
klass = rb_funcall(value, rb_intern("class"), 0);
|
564
758
|
if (klass == rb_cTime || klass == rb_cDateTime)
|
@@ -633,6 +827,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
|
633
827
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
634
828
|
}
|
635
829
|
|
830
|
+
static
|
636
831
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
637
832
|
const char *warmup_source) {
|
638
833
|
// Use following steps to create a warmed up snapshot blob from a cold one:
|
@@ -668,14 +863,14 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
668
863
|
|
669
864
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
670
865
|
SnapshotInfo* snapshot_info;
|
671
|
-
|
866
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
672
867
|
|
673
868
|
return INT2NUM(snapshot_info->raw_size);
|
674
869
|
}
|
675
870
|
|
676
871
|
static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
677
872
|
SnapshotInfo* snapshot_info;
|
678
|
-
|
873
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
679
874
|
|
680
875
|
if(TYPE(str) != T_STRING) {
|
681
876
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
@@ -698,14 +893,14 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
698
893
|
|
699
894
|
static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
|
700
895
|
SnapshotInfo* snapshot_info;
|
701
|
-
|
896
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
702
897
|
|
703
898
|
return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
|
704
899
|
}
|
705
900
|
|
706
901
|
static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
707
902
|
SnapshotInfo* snapshot_info;
|
708
|
-
|
903
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
709
904
|
|
710
905
|
if(TYPE(str) != T_STRING) {
|
711
906
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
@@ -752,13 +947,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
|
|
752
947
|
|
753
948
|
static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
754
949
|
IsolateInfo* isolate_info;
|
755
|
-
|
950
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
756
951
|
|
757
952
|
init_v8();
|
758
953
|
|
759
954
|
SnapshotInfo* snapshot_info = nullptr;
|
760
955
|
if (!NIL_P(snapshot)) {
|
761
|
-
|
956
|
+
TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
|
762
957
|
}
|
763
958
|
|
764
959
|
isolate_info->init(snapshot_info);
|
@@ -769,7 +964,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
|
769
964
|
|
770
965
|
static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
771
966
|
IsolateInfo* isolate_info;
|
772
|
-
|
967
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
773
968
|
|
774
969
|
if (current_platform == NULL) return Qfalse;
|
775
970
|
|
@@ -780,7 +975,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
780
975
|
|
781
976
|
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
782
977
|
IsolateInfo* isolate_info;
|
783
|
-
|
978
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
784
979
|
|
785
980
|
if (current_platform == NULL) return Qfalse;
|
786
981
|
|
@@ -790,7 +985,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
|
790
985
|
|
791
986
|
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
792
987
|
IsolateInfo* isolate_info;
|
793
|
-
|
988
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
794
989
|
|
795
990
|
if (current_platform == NULL) return Qfalse;
|
796
991
|
|
@@ -803,7 +998,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
|
803
998
|
|
804
999
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
805
1000
|
ContextInfo* context_info;
|
806
|
-
|
1001
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
807
1002
|
|
808
1003
|
init_v8();
|
809
1004
|
|
@@ -814,11 +1009,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
814
1009
|
|
815
1010
|
SnapshotInfo *snapshot_info = nullptr;
|
816
1011
|
if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
|
817
|
-
|
1012
|
+
TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
|
818
1013
|
}
|
819
1014
|
isolate_info->init(snapshot_info);
|
820
1015
|
} else { // given isolate or snapshot
|
821
|
-
|
1016
|
+
TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
|
822
1017
|
}
|
823
1018
|
|
824
1019
|
context_info->isolate_info = isolate_info;
|
@@ -848,7 +1043,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
848
1043
|
static VALUE convert_result_to_ruby(VALUE self /* context */,
|
849
1044
|
EvalResult& result) {
|
850
1045
|
ContextInfo *context_info;
|
851
|
-
|
1046
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
852
1047
|
|
853
1048
|
Isolate *isolate = context_info->isolate_info->isolate;
|
854
1049
|
Persistent<Context> *p_ctx = context_info->context;
|
@@ -875,7 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
875
1070
|
// a v8 scope, if we do the scope is never cleaned up properly and we leak
|
876
1071
|
if (!result.parsed) {
|
877
1072
|
if(TYPE(message) == T_STRING) {
|
878
|
-
rb_raise(rb_eParseError, "%
|
1073
|
+
rb_raise(rb_eParseError, "%" PRIsVALUE, message);
|
879
1074
|
} else {
|
880
1075
|
rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
|
881
1076
|
}
|
@@ -884,9 +1079,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
884
1079
|
if (!result.executed) {
|
885
1080
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
886
1081
|
if (ruby_exception == Qnil) {
|
887
|
-
bool mem_softlimit_reached = (
|
1082
|
+
bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
|
1083
|
+
bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
|
888
1084
|
// If we were terminated or have the memory softlimit flag set
|
889
|
-
if (
|
1085
|
+
if (marshal_stack_maxdepth_reached) {
|
1086
|
+
ruby_exception = rb_eScriptRuntimeError;
|
1087
|
+
std::string msg = std::string("Marshal object depth too deep. Script terminated.");
|
1088
|
+
message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
|
1089
|
+
} else if (result.terminated || mem_softlimit_reached) {
|
890
1090
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
891
1091
|
} else {
|
892
1092
|
ruby_exception = rb_eScriptRuntimeError;
|
@@ -894,15 +1094,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
894
1094
|
|
895
1095
|
// exception report about what happened
|
896
1096
|
if (TYPE(backtrace) == T_STRING) {
|
897
|
-
rb_raise(ruby_exception, "%
|
1097
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
|
898
1098
|
} else if(TYPE(message) == T_STRING) {
|
899
|
-
rb_raise(ruby_exception, "%
|
1099
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, message);
|
900
1100
|
} else {
|
901
1101
|
rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
|
902
1102
|
}
|
1103
|
+
} else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
|
1104
|
+
rb_exc_raise(ruby_exception);
|
903
1105
|
} else {
|
904
1106
|
VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
|
905
|
-
rb_raise(CLASS_OF(ruby_exception), "%
|
1107
|
+
rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
|
906
1108
|
}
|
907
1109
|
}
|
908
1110
|
|
@@ -921,6 +1123,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
921
1123
|
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
922
1124
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
923
1125
|
} else {
|
1126
|
+
StackCounter::Reset(isolate);
|
924
1127
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
925
1128
|
}
|
926
1129
|
|
@@ -943,7 +1146,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
943
1146
|
EvalResult eval_result;
|
944
1147
|
ContextInfo* context_info;
|
945
1148
|
|
946
|
-
|
1149
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
947
1150
|
Isolate* isolate = context_info->isolate_info->isolate;
|
948
1151
|
|
949
1152
|
if(TYPE(str) != T_STRING) {
|
@@ -961,13 +1164,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
961
1164
|
HandleScope handle_scope(isolate);
|
962
1165
|
|
963
1166
|
Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
|
964
|
-
NewStringType::kNormal, (
|
1167
|
+
NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
|
965
1168
|
|
966
1169
|
Local<String> local_filename;
|
967
1170
|
|
968
1171
|
if (filename != Qnil) {
|
969
1172
|
local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
|
970
|
-
NewStringType::kNormal, (
|
1173
|
+
NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
|
971
1174
|
eval_params.filename = &local_filename;
|
972
1175
|
} else {
|
973
1176
|
eval_params.filename = NULL;
|
@@ -978,6 +1181,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
978
1181
|
eval_params.result = &eval_result;
|
979
1182
|
eval_params.timeout = 0;
|
980
1183
|
eval_params.max_memory = 0;
|
1184
|
+
eval_params.marshal_stackdepth = 0;
|
981
1185
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
982
1186
|
if (timeout != Qnil) {
|
983
1187
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
@@ -988,6 +1192,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
988
1192
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
989
1193
|
}
|
990
1194
|
|
1195
|
+
VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
|
1196
|
+
if (stack_depth != Qnil) {
|
1197
|
+
eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
|
1198
|
+
}
|
1199
|
+
|
991
1200
|
eval_result.message = NULL;
|
992
1201
|
eval_result.backtrace = NULL;
|
993
1202
|
|
@@ -1025,7 +1234,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
|
|
1025
1234
|
return exception;
|
1026
1235
|
}
|
1027
1236
|
|
1028
|
-
void*
|
1237
|
+
static void*
|
1029
1238
|
gvl_ruby_callback(void* data) {
|
1030
1239
|
|
1031
1240
|
FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
|
@@ -1041,7 +1250,7 @@ gvl_ruby_callback(void* data) {
|
|
1041
1250
|
HandleScope scope(args->GetIsolate());
|
1042
1251
|
Local<External> external = Local<External>::Cast(args->Data());
|
1043
1252
|
|
1044
|
-
self =
|
1253
|
+
self = (VALUE)(external->Value());
|
1045
1254
|
callback = rb_iv_get(self, "@callback");
|
1046
1255
|
|
1047
1256
|
parent = rb_iv_get(self, "@parent");
|
@@ -1049,7 +1258,7 @@ gvl_ruby_callback(void* data) {
|
|
1049
1258
|
return NULL;
|
1050
1259
|
}
|
1051
1260
|
|
1052
|
-
|
1261
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1053
1262
|
|
1054
1263
|
if (length > 0) {
|
1055
1264
|
ruby_args = rb_ary_tmp_new(length);
|
@@ -1057,6 +1266,7 @@ gvl_ruby_callback(void* data) {
|
|
1057
1266
|
|
1058
1267
|
for (int i = 0; i < length; i++) {
|
1059
1268
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1269
|
+
StackCounter::Reset(args->GetIsolate());
|
1060
1270
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1061
1271
|
*context_info->context, value);
|
1062
1272
|
rb_ary_push(ruby_args, tmp);
|
@@ -1070,20 +1280,22 @@ gvl_ruby_callback(void* data) {
|
|
1070
1280
|
callback_data.ruby_args = ruby_args;
|
1071
1281
|
callback_data.failed = false;
|
1072
1282
|
|
1073
|
-
if ((
|
1283
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1074
1284
|
args->GetIsolate()->ThrowException(
|
1075
1285
|
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
1286
|
"Terminated execution during transition from Ruby to JS"));
|
1077
1287
|
args->GetIsolate()->TerminateExecution();
|
1078
1288
|
if (length > 0) {
|
1079
1289
|
rb_ary_clear(ruby_args);
|
1080
|
-
rb_gc_force_recycle(ruby_args);
|
1081
1290
|
}
|
1082
1291
|
return NULL;
|
1083
1292
|
}
|
1084
1293
|
|
1085
|
-
|
1086
|
-
|
1294
|
+
VALUE callback_data_value = (VALUE)&callback_data;
|
1295
|
+
|
1296
|
+
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1297
|
+
result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
|
1298
|
+
MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
|
1087
1299
|
|
1088
1300
|
if(callback_data.failed) {
|
1089
1301
|
rb_iv_set(parent, "@current_exception", result);
|
@@ -1097,26 +1309,24 @@ gvl_ruby_callback(void* data) {
|
|
1097
1309
|
|
1098
1310
|
if (length > 0) {
|
1099
1311
|
rb_ary_clear(ruby_args);
|
1100
|
-
rb_gc_force_recycle(ruby_args);
|
1101
1312
|
}
|
1102
1313
|
|
1103
|
-
if ((
|
1104
|
-
|
1105
|
-
isolate->TerminateExecution();
|
1314
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1315
|
+
args->GetIsolate()->TerminateExecution();
|
1106
1316
|
}
|
1107
1317
|
|
1108
1318
|
return NULL;
|
1109
1319
|
}
|
1110
1320
|
|
1111
1321
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1112
|
-
bool has_gvl = (
|
1322
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1113
1323
|
|
1114
1324
|
if(has_gvl) {
|
1115
1325
|
gvl_ruby_callback((void*)&args);
|
1116
1326
|
} else {
|
1117
|
-
args.GetIsolate()
|
1327
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1118
1328
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1119
|
-
args.GetIsolate()
|
1329
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1120
1330
|
}
|
1121
1331
|
}
|
1122
1332
|
|
@@ -1133,7 +1343,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1133
1343
|
bool parse_error = false;
|
1134
1344
|
bool attach_error = false;
|
1135
1345
|
|
1136
|
-
|
1346
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1137
1347
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1138
1348
|
|
1139
1349
|
{
|
@@ -1146,29 +1356,27 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1146
1356
|
|
1147
1357
|
Local<String> v8_str =
|
1148
1358
|
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1149
|
-
NewStringType::kNormal, (
|
1359
|
+
NewStringType::kNormal, RSTRING_LENINT(name))
|
1150
1360
|
.ToLocalChecked();
|
1151
1361
|
|
1152
|
-
//
|
1153
|
-
|
1154
|
-
|
1155
|
-
*self_copy = self;
|
1156
|
-
|
1157
|
-
Local<Value> external = External::New(isolate, self_copy);
|
1362
|
+
// Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
|
1363
|
+
// not a T_DATA type like most other classes in this file
|
1364
|
+
Local<Value> external = External::New(isolate, (void *)self);
|
1158
1365
|
|
1159
1366
|
if (parent_object == Qnil) {
|
1160
|
-
context->Global()->Set(
|
1367
|
+
Maybe<bool> success = context->Global()->Set(
|
1161
1368
|
context,
|
1162
1369
|
v8_str,
|
1163
1370
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1164
1371
|
->GetFunction(context)
|
1165
1372
|
.ToLocalChecked());
|
1373
|
+
(void)success;
|
1166
1374
|
|
1167
1375
|
} else {
|
1168
1376
|
Local<String> eval =
|
1169
1377
|
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
1170
1378
|
NewStringType::kNormal,
|
1171
|
-
(
|
1379
|
+
RSTRING_LENINT(parent_object_eval))
|
1172
1380
|
.ToLocalChecked();
|
1173
1381
|
|
1174
1382
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
@@ -1182,12 +1390,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1182
1390
|
if (!maybe_value.IsEmpty()) {
|
1183
1391
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1184
1392
|
if (value->IsObject()) {
|
1185
|
-
value.As<Object>()->Set(
|
1393
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
1186
1394
|
context,
|
1187
1395
|
v8_str,
|
1188
1396
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1189
1397
|
->GetFunction(context)
|
1190
1398
|
.ToLocalChecked());
|
1399
|
+
(void)success;
|
1191
1400
|
attach_error = false;
|
1192
1401
|
}
|
1193
1402
|
}
|
@@ -1209,7 +1418,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1209
1418
|
|
1210
1419
|
static VALUE rb_context_isolate_mutex(VALUE self) {
|
1211
1420
|
ContextInfo* context_info;
|
1212
|
-
|
1421
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1213
1422
|
|
1214
1423
|
if (!context_info->isolate_info) {
|
1215
1424
|
rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
|
@@ -1319,6 +1528,11 @@ static void mark_isolate(void* data) {
|
|
1319
1528
|
isolate_info->mark();
|
1320
1529
|
}
|
1321
1530
|
|
1531
|
+
static size_t isolate_memsize(const void *ptr) {
|
1532
|
+
const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
|
1533
|
+
return sizeof(*isolate_info);
|
1534
|
+
}
|
1535
|
+
|
1322
1536
|
static void deallocate(void* data) {
|
1323
1537
|
ContextInfo* context_info = (ContextInfo*)data;
|
1324
1538
|
|
@@ -1327,6 +1541,11 @@ static void deallocate(void* data) {
|
|
1327
1541
|
xfree(data);
|
1328
1542
|
}
|
1329
1543
|
|
1544
|
+
static size_t context_memsize(const void *ptr)
|
1545
|
+
{
|
1546
|
+
return sizeof(ContextInfo);
|
1547
|
+
}
|
1548
|
+
|
1330
1549
|
static void mark_context(void* data) {
|
1331
1550
|
ContextInfo* context_info = (ContextInfo*)data;
|
1332
1551
|
if (context_info->isolate_info) {
|
@@ -1334,48 +1553,39 @@ static void mark_context(void* data) {
|
|
1334
1553
|
}
|
1335
1554
|
}
|
1336
1555
|
|
1337
|
-
static void deallocate_external_function(void * data) {
|
1338
|
-
xfree(data);
|
1339
|
-
}
|
1340
|
-
|
1341
1556
|
static void deallocate_snapshot(void * data) {
|
1342
1557
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1343
1558
|
delete[] snapshot_info->data;
|
1344
1559
|
xfree(snapshot_info);
|
1345
1560
|
}
|
1346
1561
|
|
1347
|
-
static
|
1348
|
-
|
1349
|
-
return
|
1562
|
+
static size_t snapshot_memsize(const void *data) {
|
1563
|
+
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1564
|
+
return sizeof(*snapshot_info) + snapshot_info->raw_size;
|
1350
1565
|
}
|
1351
1566
|
|
1352
1567
|
static VALUE allocate(VALUE klass) {
|
1353
|
-
ContextInfo* context_info
|
1354
|
-
|
1355
|
-
context_info->context = NULL;
|
1356
|
-
|
1357
|
-
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1568
|
+
ContextInfo* context_info;
|
1569
|
+
return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
|
1358
1570
|
}
|
1359
1571
|
|
1360
1572
|
static VALUE allocate_snapshot(VALUE klass) {
|
1361
|
-
SnapshotInfo* snapshot_info
|
1362
|
-
snapshot_info->data = NULL;
|
1363
|
-
snapshot_info->raw_size = 0;
|
1573
|
+
SnapshotInfo* snapshot_info;
|
1364
1574
|
|
1365
|
-
return
|
1575
|
+
return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
|
1366
1576
|
}
|
1367
1577
|
|
1368
1578
|
static VALUE allocate_isolate(VALUE klass) {
|
1369
1579
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1370
1580
|
|
1371
|
-
return
|
1581
|
+
return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
|
1372
1582
|
}
|
1373
1583
|
|
1374
1584
|
static VALUE
|
1375
1585
|
rb_heap_stats(VALUE self) {
|
1376
1586
|
|
1377
1587
|
ContextInfo* context_info;
|
1378
|
-
|
1588
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1379
1589
|
Isolate* isolate;
|
1380
1590
|
v8::HeapStatistics stats;
|
1381
1591
|
|
@@ -1407,7 +1617,9 @@ rb_heap_stats(VALUE self) {
|
|
1407
1617
|
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1408
1618
|
class FileOutputStream : public OutputStream {
|
1409
1619
|
public:
|
1410
|
-
|
1620
|
+
int err;
|
1621
|
+
|
1622
|
+
FileOutputStream(int fd) : fd(fd) { err = 0; }
|
1411
1623
|
|
1412
1624
|
virtual int GetChunkSize() {
|
1413
1625
|
return 65536;
|
@@ -1416,17 +1628,27 @@ class FileOutputStream : public OutputStream {
|
|
1416
1628
|
virtual void EndOfStream() {}
|
1417
1629
|
|
1418
1630
|
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1631
|
+
size_t len = static_cast<size_t>(size);
|
1632
|
+
|
1633
|
+
while (len) {
|
1634
|
+
ssize_t w = write(fd, data, len);
|
1635
|
+
|
1636
|
+
if (w > 0) {
|
1637
|
+
data += w;
|
1638
|
+
len -= w;
|
1639
|
+
} else if (w < 0) {
|
1640
|
+
err = errno;
|
1641
|
+
return kAbort;
|
1642
|
+
} else { /* w == 0, could be out-of-space */
|
1643
|
+
err = -1;
|
1644
|
+
return kAbort;
|
1645
|
+
}
|
1646
|
+
}
|
1647
|
+
return kContinue;
|
1426
1648
|
}
|
1427
1649
|
|
1428
1650
|
private:
|
1429
|
-
|
1651
|
+
int fd;
|
1430
1652
|
};
|
1431
1653
|
|
1432
1654
|
|
@@ -1439,13 +1661,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1439
1661
|
|
1440
1662
|
if (!fptr) return Qfalse;
|
1441
1663
|
|
1442
|
-
|
1443
|
-
|
1444
|
-
if (fp == NULL) return Qfalse;
|
1445
|
-
|
1664
|
+
// prepare for unbuffered write(2) below:
|
1665
|
+
rb_funcall(file, rb_intern("flush"), 0);
|
1446
1666
|
|
1447
1667
|
ContextInfo* context_info;
|
1448
|
-
|
1668
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1449
1669
|
Isolate* isolate;
|
1450
1670
|
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1451
1671
|
|
@@ -1459,13 +1679,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1459
1679
|
|
1460
1680
|
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1461
1681
|
|
1462
|
-
FileOutputStream stream(
|
1682
|
+
FileOutputStream stream(fptr->fd);
|
1463
1683
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1464
1684
|
|
1465
|
-
fflush(fp);
|
1466
|
-
|
1467
1685
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1468
1686
|
|
1687
|
+
/* TODO: perhaps rb_sys_fail here */
|
1688
|
+
if (stream.err) return Qfalse;
|
1689
|
+
|
1469
1690
|
return Qtrue;
|
1470
1691
|
}
|
1471
1692
|
|
@@ -1473,12 +1694,12 @@ static VALUE
|
|
1473
1694
|
rb_context_stop(VALUE self) {
|
1474
1695
|
|
1475
1696
|
ContextInfo* context_info;
|
1476
|
-
|
1697
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1477
1698
|
|
1478
1699
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1479
1700
|
|
1480
1701
|
// flag for termination
|
1481
|
-
isolate
|
1702
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1482
1703
|
|
1483
1704
|
isolate->TerminateExecution();
|
1484
1705
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
@@ -1490,7 +1711,7 @@ static VALUE
|
|
1490
1711
|
rb_context_dispose(VALUE self) {
|
1491
1712
|
|
1492
1713
|
ContextInfo* context_info;
|
1493
|
-
|
1714
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1494
1715
|
|
1495
1716
|
free_context(context_info);
|
1496
1717
|
|
@@ -1507,18 +1728,20 @@ nogvl_context_call(void *args) {
|
|
1507
1728
|
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
1729
|
Isolate* isolate = isolate_info->isolate;
|
1509
1730
|
|
1510
|
-
|
1511
|
-
isolate
|
1512
|
-
// terminate ASAP
|
1513
|
-
isolate->SetData(DO_TERMINATE, (void*)false);
|
1731
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1732
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1514
1733
|
|
1515
1734
|
if (call->max_memory > 0) {
|
1516
|
-
isolate
|
1517
|
-
isolate
|
1735
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1736
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1518
1737
|
if (!isolate_info->added_gc_cb) {
|
1519
|
-
|
1738
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1520
1739
|
isolate_info->added_gc_cb = true;
|
1740
|
+
}
|
1521
1741
|
}
|
1742
|
+
|
1743
|
+
if (call->marshal_stackdepth > 0) {
|
1744
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1522
1745
|
}
|
1523
1746
|
|
1524
1747
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1536,7 +1759,7 @@ nogvl_context_call(void *args) {
|
|
1536
1759
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1537
1760
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
1538
1761
|
|
1539
|
-
isolate
|
1762
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1540
1763
|
|
1541
1764
|
return NULL;
|
1542
1765
|
}
|
@@ -1551,7 +1774,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1551
1774
|
FunctionCall call;
|
1552
1775
|
VALUE *call_argv = NULL;
|
1553
1776
|
|
1554
|
-
|
1777
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1555
1778
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1556
1779
|
|
1557
1780
|
if (argc < 1) {
|
@@ -1584,6 +1807,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1584
1807
|
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1585
1808
|
call.max_memory = (size_t)sl_int;
|
1586
1809
|
}
|
1810
|
+
|
1811
|
+
call.marshal_stackdepth = 0;
|
1812
|
+
VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
|
1813
|
+
if (marshal_stackdepth != Qnil) {
|
1814
|
+
unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
|
1815
|
+
call.marshal_stackdepth = (size_t)sl_int;
|
1816
|
+
}
|
1587
1817
|
|
1588
1818
|
bool missingFunction = false;
|
1589
1819
|
{
|
@@ -1605,23 +1835,15 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1605
1835
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1606
1836
|
missingFunction = true;
|
1607
1837
|
} else {
|
1608
|
-
|
1609
1838
|
Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
|
1839
|
+
VALUE tmp;
|
1610
1840
|
call.fun = fun;
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
|
1615
|
-
if (!call.argv) {
|
1616
|
-
return Qnil;
|
1617
|
-
}
|
1618
|
-
for(int i=0; i < fun_argc; i++) {
|
1619
|
-
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1620
|
-
}
|
1841
|
+
call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
|
1842
|
+
for(int i=0; i < call.argc; i++) {
|
1843
|
+
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1621
1844
|
}
|
1622
1845
|
rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
|
1623
|
-
|
1624
|
-
|
1846
|
+
RB_ALLOCV_END(tmp);
|
1625
1847
|
}
|
1626
1848
|
}
|
1627
1849
|
|
@@ -1634,7 +1856,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1634
1856
|
|
1635
1857
|
static VALUE rb_context_create_isolate_value(VALUE self) {
|
1636
1858
|
ContextInfo* context_info;
|
1637
|
-
|
1859
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1638
1860
|
IsolateInfo *isolate_info = context_info->isolate_info;
|
1639
1861
|
|
1640
1862
|
if (!isolate_info) {
|
@@ -1642,7 +1864,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1642
1864
|
}
|
1643
1865
|
|
1644
1866
|
isolate_info->hold();
|
1645
|
-
return
|
1867
|
+
return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
|
1646
1868
|
}
|
1647
1869
|
|
1648
1870
|
static void set_ruby_exiting(VALUE value) {
|
@@ -1698,7 +1920,6 @@ extern "C" {
|
|
1698
1920
|
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
1699
1921
|
|
1700
1922
|
rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
|
1701
|
-
rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
|
1702
1923
|
|
1703
1924
|
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
1704
1925
|
rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
|