mini_racer 0.4.0 → 0.6.3
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 +72 -28
- data/.gitignore +1 -0
- data/CHANGELOG +40 -0
- data/README.md +32 -17
- data/Rakefile +16 -4
- data/ext/mini_racer_extension/extconf.rb +34 -13
- data/ext/mini_racer_extension/mini_racer_extension.cc +404 -221
- data/ext/mini_racer_loader/extconf.rb +6 -1
- data/lib/mini_racer/truffleruby.rb +353 -0
- data/lib/mini_racer/version.rb +2 -2
- data/lib/mini_racer.rb +34 -23
- data/mini_racer.gemspec +1 -3
- metadata +11 -11
- data/.travis.yml +0 -31
@@ -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;
|
@@ -155,17 +305,36 @@ static std::unique_ptr<Platform> current_platform = NULL;
|
|
155
305
|
static std::mutex platform_lock;
|
156
306
|
|
157
307
|
static pthread_attr_t *thread_attr_p;
|
158
|
-
static
|
159
|
-
static bool ruby_exiting = false; // guarded by exit_lock
|
308
|
+
static std::atomic_int ruby_exiting(0);
|
160
309
|
static bool single_threaded = false;
|
161
310
|
|
311
|
+
static void mark_context(void *);
|
312
|
+
static void deallocate(void *);
|
313
|
+
static size_t context_memsize(const void *);
|
314
|
+
static const rb_data_type_t context_type = {
|
315
|
+
"mini_racer/context_info",
|
316
|
+
{ mark_context, deallocate, context_memsize }
|
317
|
+
};
|
318
|
+
|
319
|
+
static void deallocate_snapshot(void *);
|
320
|
+
static size_t snapshot_memsize(const void *);
|
321
|
+
static const rb_data_type_t snapshot_type = {
|
322
|
+
"mini_racer/snapshot_info",
|
323
|
+
{ NULL, deallocate_snapshot, snapshot_memsize }
|
324
|
+
};
|
325
|
+
|
326
|
+
static void mark_isolate(void *);
|
327
|
+
static void deallocate_isolate(void *);
|
328
|
+
static size_t isolate_memsize(const void *);
|
329
|
+
static const rb_data_type_t isolate_type = {
|
330
|
+
"mini_racer/isolate_info",
|
331
|
+
{ mark_isolate, deallocate_isolate, isolate_memsize }
|
332
|
+
};
|
333
|
+
|
162
334
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
163
335
|
bool platform_already_initialized = false;
|
164
336
|
|
165
|
-
|
166
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
|
167
|
-
rb_obj_class(flag_as_str));
|
168
|
-
}
|
337
|
+
Check_Type(flag_as_str, T_STRING);
|
169
338
|
|
170
339
|
platform_lock.lock();
|
171
340
|
|
@@ -173,7 +342,7 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
173
342
|
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
174
343
|
single_threaded = true;
|
175
344
|
}
|
176
|
-
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (
|
345
|
+
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
|
177
346
|
} else {
|
178
347
|
platform_already_initialized = true;
|
179
348
|
}
|
@@ -196,7 +365,11 @@ static void init_v8() {
|
|
196
365
|
|
197
366
|
if (current_platform == NULL) {
|
198
367
|
V8::InitializeICU();
|
199
|
-
|
368
|
+
if (single_threaded) {
|
369
|
+
current_platform = platform::NewSingleThreadedDefaultPlatform();
|
370
|
+
} else {
|
371
|
+
current_platform = platform::NewDefaultPlatform();
|
372
|
+
}
|
200
373
|
V8::InitializePlatform(current_platform.get());
|
201
374
|
V8::Initialize();
|
202
375
|
}
|
@@ -205,16 +378,18 @@ static void init_v8() {
|
|
205
378
|
}
|
206
379
|
|
207
380
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
208
|
-
if((
|
381
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
382
|
+
return;
|
383
|
+
}
|
209
384
|
|
210
|
-
size_t softlimit =
|
385
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
211
386
|
|
212
387
|
HeapStatistics stats;
|
213
388
|
isolate->GetHeapStatistics(&stats);
|
214
389
|
size_t used = stats.used_heap_size();
|
215
390
|
|
216
391
|
if(used > softlimit) {
|
217
|
-
isolate
|
392
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
218
393
|
isolate->TerminateExecution();
|
219
394
|
}
|
220
395
|
}
|
@@ -239,28 +414,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
239
414
|
Local<Value> local_value = v8res.ToLocalChecked();
|
240
415
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
241
416
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
242
|
-
|
243
|
-
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
244
|
-
.ToLocalChecked().As<Object>();
|
417
|
+
MaybeLocal<v8::Value> ml = context->Global()->Get(
|
418
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"));
|
245
419
|
|
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()) {
|
420
|
+
if (ml.IsEmpty()) { // exception
|
256
421
|
evalRes.executed = false;
|
257
422
|
} else {
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
423
|
+
Local<Object> JSON = ml.ToLocalChecked().As<Object>();
|
424
|
+
|
425
|
+
Local<Function> stringify = JSON->Get(
|
426
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
427
|
+
.ToLocalChecked().As<Function>();
|
428
|
+
|
429
|
+
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
430
|
+
const unsigned argc = 1;
|
431
|
+
Local<Value> argv[argc] = { object };
|
432
|
+
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
433
|
+
|
434
|
+
if (json.IsEmpty()) {
|
435
|
+
evalRes.executed = false;
|
436
|
+
} else {
|
437
|
+
evalRes.json = true;
|
438
|
+
Persistent<Value>* persistent = new Persistent<Value>();
|
439
|
+
persistent->Reset(isolate, json.ToLocalChecked());
|
440
|
+
evalRes.value = persistent;
|
441
|
+
}
|
262
442
|
}
|
263
|
-
|
264
443
|
} else {
|
265
444
|
Persistent<Value>* persistent = new Persistent<Value>();
|
266
445
|
persistent->Reset(isolate, local_value);
|
@@ -311,7 +490,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
311
490
|
}
|
312
491
|
}
|
313
492
|
|
314
|
-
void*
|
493
|
+
static void*
|
315
494
|
nogvl_context_eval(void* arg) {
|
316
495
|
|
317
496
|
EvalParams* eval_params = (EvalParams*)arg;
|
@@ -326,14 +505,15 @@ nogvl_context_eval(void* arg) {
|
|
326
505
|
Context::Scope context_scope(context);
|
327
506
|
v8::ScriptOrigin *origin = NULL;
|
328
507
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
508
|
+
IsolateData::Init(isolate);
|
509
|
+
|
510
|
+
if (eval_params->max_memory > 0) {
|
511
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
|
512
|
+
if (!isolate_info->added_gc_cb) {
|
513
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
514
|
+
isolate_info->added_gc_cb = true;
|
515
|
+
}
|
516
|
+
}
|
337
517
|
|
338
518
|
MaybeLocal<Script> parsed_script;
|
339
519
|
|
@@ -359,12 +539,8 @@ nogvl_context_eval(void* arg) {
|
|
359
539
|
result->message->Reset(isolate, trycatch.Exception());
|
360
540
|
} else {
|
361
541
|
// 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
|
-
}
|
542
|
+
if (eval_params->marshal_stackdepth > 0) {
|
543
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
368
544
|
}
|
369
545
|
|
370
546
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -372,7 +548,7 @@ nogvl_context_eval(void* arg) {
|
|
372
548
|
|
373
549
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
374
550
|
|
375
|
-
isolate
|
551
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
376
552
|
|
377
553
|
return NULL;
|
378
554
|
}
|
@@ -390,6 +566,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
390
566
|
Isolate::Scope isolate_scope(isolate);
|
391
567
|
HandleScope scope(isolate);
|
392
568
|
|
569
|
+
StackCounter stackCounter(isolate);
|
570
|
+
|
571
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
|
572
|
+
return Qnil;
|
573
|
+
}
|
574
|
+
|
575
|
+
if (stackCounter.IsTooDeep()) {
|
576
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
577
|
+
isolate->TerminateExecution();
|
578
|
+
return Qnil;
|
579
|
+
}
|
580
|
+
|
393
581
|
if (value->IsNull() || value->IsUndefined()){
|
394
582
|
return Qnil;
|
395
583
|
}
|
@@ -472,13 +660,9 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
472
660
|
v8::String::Utf8Value symbol_name(isolate,
|
473
661
|
Local<Symbol>::Cast(value)->Name());
|
474
662
|
|
475
|
-
VALUE str_symbol =
|
476
|
-
*symbol_name,
|
477
|
-
symbol_name.length(),
|
478
|
-
rb_enc_find("utf-8")
|
479
|
-
);
|
663
|
+
VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
|
480
664
|
|
481
|
-
return
|
665
|
+
return rb_str_intern(str_symbol);
|
482
666
|
}
|
483
667
|
|
484
668
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
@@ -487,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
487
671
|
return Qnil;
|
488
672
|
} else {
|
489
673
|
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
490
|
-
return
|
674
|
+
return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
491
675
|
}
|
492
676
|
}
|
493
677
|
|
@@ -532,7 +716,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
532
716
|
case T_FLOAT:
|
533
717
|
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
534
718
|
case T_STRING:
|
535
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (
|
719
|
+
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
536
720
|
case T_NIL:
|
537
721
|
return scope.Escape(Null(isolate));
|
538
722
|
case T_TRUE:
|
@@ -543,7 +727,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
543
727
|
length = RARRAY_LEN(value);
|
544
728
|
array = Array::New(isolate, (int)length);
|
545
729
|
for(i=0; i<length; i++) {
|
546
|
-
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
730
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
731
|
+
(void)(success);
|
547
732
|
}
|
548
733
|
return scope.Escape(array);
|
549
734
|
case T_HASH:
|
@@ -552,13 +737,14 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
552
737
|
length = RARRAY_LEN(hash_as_array);
|
553
738
|
for(i=0; i<length; i++) {
|
554
739
|
pair = rb_ary_entry(hash_as_array, i);
|
555
|
-
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
740
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
556
741
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
742
|
+
(void)(success);
|
557
743
|
}
|
558
744
|
return scope.Escape(object);
|
559
745
|
case T_SYMBOL:
|
560
746
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
561
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (
|
747
|
+
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
562
748
|
case T_DATA:
|
563
749
|
klass = rb_funcall(value, rb_intern("class"), 0);
|
564
750
|
if (klass == rb_cTime || klass == rb_cDateTime)
|
@@ -633,6 +819,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
|
633
819
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
634
820
|
}
|
635
821
|
|
822
|
+
static
|
636
823
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
637
824
|
const char *warmup_source) {
|
638
825
|
// Use following steps to create a warmed up snapshot blob from a cold one:
|
@@ -666,21 +853,18 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
666
853
|
return result;
|
667
854
|
}
|
668
855
|
|
669
|
-
static VALUE rb_snapshot_size(VALUE self
|
856
|
+
static VALUE rb_snapshot_size(VALUE self) {
|
670
857
|
SnapshotInfo* snapshot_info;
|
671
|
-
|
858
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
672
859
|
|
673
860
|
return INT2NUM(snapshot_info->raw_size);
|
674
861
|
}
|
675
862
|
|
676
863
|
static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
677
864
|
SnapshotInfo* snapshot_info;
|
678
|
-
|
865
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
679
866
|
|
680
|
-
|
681
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
682
|
-
rb_obj_class(str));
|
683
|
-
}
|
867
|
+
Check_Type(str, T_STRING);
|
684
868
|
|
685
869
|
init_v8();
|
686
870
|
|
@@ -696,21 +880,18 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
696
880
|
return Qnil;
|
697
881
|
}
|
698
882
|
|
699
|
-
static VALUE rb_snapshot_dump(VALUE self
|
883
|
+
static VALUE rb_snapshot_dump(VALUE self) {
|
700
884
|
SnapshotInfo* snapshot_info;
|
701
|
-
|
885
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
702
886
|
|
703
887
|
return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
|
704
888
|
}
|
705
889
|
|
706
890
|
static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
707
891
|
SnapshotInfo* snapshot_info;
|
708
|
-
|
892
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
709
893
|
|
710
|
-
|
711
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
712
|
-
rb_obj_class(str));
|
713
|
-
}
|
894
|
+
Check_Type(str, T_STRING);
|
714
895
|
|
715
896
|
init_v8();
|
716
897
|
|
@@ -752,13 +933,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
|
|
752
933
|
|
753
934
|
static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
754
935
|
IsolateInfo* isolate_info;
|
755
|
-
|
936
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
756
937
|
|
757
938
|
init_v8();
|
758
939
|
|
759
940
|
SnapshotInfo* snapshot_info = nullptr;
|
760
941
|
if (!NIL_P(snapshot)) {
|
761
|
-
|
942
|
+
TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
|
762
943
|
}
|
763
944
|
|
764
945
|
isolate_info->init(snapshot_info);
|
@@ -769,7 +950,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
|
769
950
|
|
770
951
|
static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
771
952
|
IsolateInfo* isolate_info;
|
772
|
-
|
953
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
773
954
|
|
774
955
|
if (current_platform == NULL) return Qfalse;
|
775
956
|
|
@@ -780,7 +961,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
780
961
|
|
781
962
|
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
782
963
|
IsolateInfo* isolate_info;
|
783
|
-
|
964
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
784
965
|
|
785
966
|
if (current_platform == NULL) return Qfalse;
|
786
967
|
|
@@ -790,7 +971,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
|
790
971
|
|
791
972
|
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
792
973
|
IsolateInfo* isolate_info;
|
793
|
-
|
974
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
794
975
|
|
795
976
|
if (current_platform == NULL) return Qfalse;
|
796
977
|
|
@@ -803,7 +984,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
|
803
984
|
|
804
985
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
805
986
|
ContextInfo* context_info;
|
806
|
-
|
987
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
807
988
|
|
808
989
|
init_v8();
|
809
990
|
|
@@ -814,11 +995,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
814
995
|
|
815
996
|
SnapshotInfo *snapshot_info = nullptr;
|
816
997
|
if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
|
817
|
-
|
998
|
+
TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
|
818
999
|
}
|
819
1000
|
isolate_info->init(snapshot_info);
|
820
1001
|
} else { // given isolate or snapshot
|
821
|
-
|
1002
|
+
TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
|
822
1003
|
}
|
823
1004
|
|
824
1005
|
context_info->isolate_info = isolate_info;
|
@@ -848,7 +1029,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
848
1029
|
static VALUE convert_result_to_ruby(VALUE self /* context */,
|
849
1030
|
EvalResult& result) {
|
850
1031
|
ContextInfo *context_info;
|
851
|
-
|
1032
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
852
1033
|
|
853
1034
|
Isolate *isolate = context_info->isolate_info->isolate;
|
854
1035
|
Persistent<Context> *p_ctx = context_info->context;
|
@@ -875,7 +1056,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
875
1056
|
// a v8 scope, if we do the scope is never cleaned up properly and we leak
|
876
1057
|
if (!result.parsed) {
|
877
1058
|
if(TYPE(message) == T_STRING) {
|
878
|
-
rb_raise(rb_eParseError, "%
|
1059
|
+
rb_raise(rb_eParseError, "%" PRIsVALUE, message);
|
879
1060
|
} else {
|
880
1061
|
rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
|
881
1062
|
}
|
@@ -884,9 +1065,13 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
884
1065
|
if (!result.executed) {
|
885
1066
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
886
1067
|
if (ruby_exception == Qnil) {
|
887
|
-
bool mem_softlimit_reached = (
|
1068
|
+
bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
|
1069
|
+
bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
|
888
1070
|
// If we were terminated or have the memory softlimit flag set
|
889
|
-
if (
|
1071
|
+
if (marshal_stack_maxdepth_reached) {
|
1072
|
+
ruby_exception = rb_eScriptRuntimeError;
|
1073
|
+
message = rb_utf8_str_new_literal("Marshal object depth too deep. Script terminated.");
|
1074
|
+
} else if (result.terminated || mem_softlimit_reached) {
|
890
1075
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
891
1076
|
} else {
|
892
1077
|
ruby_exception = rb_eScriptRuntimeError;
|
@@ -894,15 +1079,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
894
1079
|
|
895
1080
|
// exception report about what happened
|
896
1081
|
if (TYPE(backtrace) == T_STRING) {
|
897
|
-
rb_raise(ruby_exception, "%
|
1082
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
|
898
1083
|
} else if(TYPE(message) == T_STRING) {
|
899
|
-
rb_raise(ruby_exception, "%
|
1084
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, message);
|
900
1085
|
} else {
|
901
1086
|
rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
|
902
1087
|
}
|
1088
|
+
} else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
|
1089
|
+
rb_exc_raise(ruby_exception);
|
903
1090
|
} else {
|
904
1091
|
VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
|
905
|
-
rb_raise(CLASS_OF(ruby_exception), "%
|
1092
|
+
rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
|
906
1093
|
}
|
907
1094
|
}
|
908
1095
|
|
@@ -918,9 +1105,10 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
918
1105
|
|
919
1106
|
if (result.json) {
|
920
1107
|
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
921
|
-
VALUE json_string =
|
1108
|
+
VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
922
1109
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
923
1110
|
} else {
|
1111
|
+
StackCounter::Reset(isolate);
|
924
1112
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
925
1113
|
}
|
926
1114
|
|
@@ -943,16 +1131,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
943
1131
|
EvalResult eval_result;
|
944
1132
|
ContextInfo* context_info;
|
945
1133
|
|
946
|
-
|
1134
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
947
1135
|
Isolate* isolate = context_info->isolate_info->isolate;
|
948
1136
|
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
if(filename != Qnil && TYPE(filename) != T_STRING) {
|
954
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
|
955
|
-
rb_obj_class(filename));
|
1137
|
+
Check_Type(str, T_STRING);
|
1138
|
+
|
1139
|
+
if (!NIL_P(filename)) {
|
1140
|
+
Check_Type(filename, T_STRING);
|
956
1141
|
}
|
957
1142
|
|
958
1143
|
{
|
@@ -961,13 +1146,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
961
1146
|
HandleScope handle_scope(isolate);
|
962
1147
|
|
963
1148
|
Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
|
964
|
-
NewStringType::kNormal, (
|
1149
|
+
NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
|
965
1150
|
|
966
1151
|
Local<String> local_filename;
|
967
1152
|
|
968
1153
|
if (filename != Qnil) {
|
969
1154
|
local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
|
970
|
-
NewStringType::kNormal, (
|
1155
|
+
NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
|
971
1156
|
eval_params.filename = &local_filename;
|
972
1157
|
} else {
|
973
1158
|
eval_params.filename = NULL;
|
@@ -978,6 +1163,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
978
1163
|
eval_params.result = &eval_result;
|
979
1164
|
eval_params.timeout = 0;
|
980
1165
|
eval_params.max_memory = 0;
|
1166
|
+
eval_params.marshal_stackdepth = 0;
|
981
1167
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
982
1168
|
if (timeout != Qnil) {
|
983
1169
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
@@ -988,6 +1174,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
988
1174
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
989
1175
|
}
|
990
1176
|
|
1177
|
+
VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
|
1178
|
+
if (stack_depth != Qnil) {
|
1179
|
+
eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
|
1180
|
+
}
|
1181
|
+
|
991
1182
|
eval_result.message = NULL;
|
992
1183
|
eval_result.backtrace = NULL;
|
993
1184
|
|
@@ -1025,7 +1216,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
|
|
1025
1216
|
return exception;
|
1026
1217
|
}
|
1027
1218
|
|
1028
|
-
void*
|
1219
|
+
static void*
|
1029
1220
|
gvl_ruby_callback(void* data) {
|
1030
1221
|
|
1031
1222
|
FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
|
@@ -1041,7 +1232,7 @@ gvl_ruby_callback(void* data) {
|
|
1041
1232
|
HandleScope scope(args->GetIsolate());
|
1042
1233
|
Local<External> external = Local<External>::Cast(args->Data());
|
1043
1234
|
|
1044
|
-
self =
|
1235
|
+
self = (VALUE)(external->Value());
|
1045
1236
|
callback = rb_iv_get(self, "@callback");
|
1046
1237
|
|
1047
1238
|
parent = rb_iv_get(self, "@parent");
|
@@ -1049,7 +1240,7 @@ gvl_ruby_callback(void* data) {
|
|
1049
1240
|
return NULL;
|
1050
1241
|
}
|
1051
1242
|
|
1052
|
-
|
1243
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1053
1244
|
|
1054
1245
|
if (length > 0) {
|
1055
1246
|
ruby_args = rb_ary_tmp_new(length);
|
@@ -1057,6 +1248,7 @@ gvl_ruby_callback(void* data) {
|
|
1057
1248
|
|
1058
1249
|
for (int i = 0; i < length; i++) {
|
1059
1250
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1251
|
+
StackCounter::Reset(args->GetIsolate());
|
1060
1252
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1061
1253
|
*context_info->context, value);
|
1062
1254
|
rb_ary_push(ruby_args, tmp);
|
@@ -1070,20 +1262,22 @@ gvl_ruby_callback(void* data) {
|
|
1070
1262
|
callback_data.ruby_args = ruby_args;
|
1071
1263
|
callback_data.failed = false;
|
1072
1264
|
|
1073
|
-
if ((
|
1265
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1074
1266
|
args->GetIsolate()->ThrowException(
|
1075
1267
|
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
1268
|
"Terminated execution during transition from Ruby to JS"));
|
1077
1269
|
args->GetIsolate()->TerminateExecution();
|
1078
1270
|
if (length > 0) {
|
1079
1271
|
rb_ary_clear(ruby_args);
|
1080
|
-
rb_gc_force_recycle(ruby_args);
|
1081
1272
|
}
|
1082
1273
|
return NULL;
|
1083
1274
|
}
|
1084
1275
|
|
1085
|
-
|
1086
|
-
|
1276
|
+
VALUE callback_data_value = (VALUE)&callback_data;
|
1277
|
+
|
1278
|
+
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1279
|
+
result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
|
1280
|
+
MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
|
1087
1281
|
|
1088
1282
|
if(callback_data.failed) {
|
1089
1283
|
rb_iv_set(parent, "@current_exception", result);
|
@@ -1097,26 +1291,24 @@ gvl_ruby_callback(void* data) {
|
|
1097
1291
|
|
1098
1292
|
if (length > 0) {
|
1099
1293
|
rb_ary_clear(ruby_args);
|
1100
|
-
rb_gc_force_recycle(ruby_args);
|
1101
1294
|
}
|
1102
1295
|
|
1103
|
-
if ((
|
1104
|
-
|
1105
|
-
isolate->TerminateExecution();
|
1296
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1297
|
+
args->GetIsolate()->TerminateExecution();
|
1106
1298
|
}
|
1107
1299
|
|
1108
1300
|
return NULL;
|
1109
1301
|
}
|
1110
1302
|
|
1111
1303
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1112
|
-
bool has_gvl = (
|
1304
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1113
1305
|
|
1114
1306
|
if(has_gvl) {
|
1115
1307
|
gvl_ruby_callback((void*)&args);
|
1116
1308
|
} else {
|
1117
|
-
args.GetIsolate()
|
1309
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1118
1310
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1119
|
-
args.GetIsolate()
|
1311
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1120
1312
|
}
|
1121
1313
|
}
|
1122
1314
|
|
@@ -1133,7 +1325,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1133
1325
|
bool parse_error = false;
|
1134
1326
|
bool attach_error = false;
|
1135
1327
|
|
1136
|
-
|
1328
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1137
1329
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1138
1330
|
|
1139
1331
|
{
|
@@ -1146,29 +1338,27 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1146
1338
|
|
1147
1339
|
Local<String> v8_str =
|
1148
1340
|
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1149
|
-
NewStringType::kNormal, (
|
1341
|
+
NewStringType::kNormal, RSTRING_LENINT(name))
|
1150
1342
|
.ToLocalChecked();
|
1151
1343
|
|
1152
|
-
//
|
1153
|
-
|
1154
|
-
|
1155
|
-
*self_copy = self;
|
1156
|
-
|
1157
|
-
Local<Value> external = External::New(isolate, self_copy);
|
1344
|
+
// Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
|
1345
|
+
// not a T_DATA type like most other classes in this file
|
1346
|
+
Local<Value> external = External::New(isolate, (void *)self);
|
1158
1347
|
|
1159
1348
|
if (parent_object == Qnil) {
|
1160
|
-
context->Global()->Set(
|
1349
|
+
Maybe<bool> success = context->Global()->Set(
|
1161
1350
|
context,
|
1162
1351
|
v8_str,
|
1163
1352
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1164
1353
|
->GetFunction(context)
|
1165
1354
|
.ToLocalChecked());
|
1355
|
+
(void)success;
|
1166
1356
|
|
1167
1357
|
} else {
|
1168
1358
|
Local<String> eval =
|
1169
1359
|
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
1170
1360
|
NewStringType::kNormal,
|
1171
|
-
(
|
1361
|
+
RSTRING_LENINT(parent_object_eval))
|
1172
1362
|
.ToLocalChecked();
|
1173
1363
|
|
1174
1364
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
@@ -1182,12 +1372,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1182
1372
|
if (!maybe_value.IsEmpty()) {
|
1183
1373
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1184
1374
|
if (value->IsObject()) {
|
1185
|
-
value.As<Object>()->Set(
|
1375
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
1186
1376
|
context,
|
1187
1377
|
v8_str,
|
1188
1378
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1189
1379
|
->GetFunction(context)
|
1190
1380
|
.ToLocalChecked());
|
1381
|
+
(void)success;
|
1191
1382
|
attach_error = false;
|
1192
1383
|
}
|
1193
1384
|
}
|
@@ -1209,7 +1400,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1209
1400
|
|
1210
1401
|
static VALUE rb_context_isolate_mutex(VALUE self) {
|
1211
1402
|
ContextInfo* context_info;
|
1212
|
-
|
1403
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1213
1404
|
|
1214
1405
|
if (!context_info->isolate_info) {
|
1215
1406
|
rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
|
@@ -1265,42 +1456,33 @@ static void free_context_raw(void *arg) {
|
|
1265
1456
|
if (isolate_info) {
|
1266
1457
|
isolate_info->release();
|
1267
1458
|
}
|
1268
|
-
|
1269
|
-
xfree(context_info);
|
1270
1459
|
}
|
1271
1460
|
|
1272
1461
|
static void *free_context_thr(void* arg) {
|
1273
|
-
if (
|
1274
|
-
|
1275
|
-
|
1276
|
-
if (ruby_exiting) {
|
1277
|
-
return NULL;
|
1462
|
+
if (ruby_exiting.load() == 0) {
|
1463
|
+
free_context_raw(arg);
|
1464
|
+
xfree(arg);
|
1278
1465
|
}
|
1279
|
-
|
1280
|
-
free_context_raw(arg);
|
1281
|
-
|
1282
|
-
pthread_rwlock_unlock(&exit_lock);
|
1283
|
-
|
1284
1466
|
return NULL;
|
1285
1467
|
}
|
1286
1468
|
|
1287
1469
|
// destroys everything except freeing the ContextInfo struct (see deallocate())
|
1288
1470
|
static void free_context(ContextInfo* context_info) {
|
1289
|
-
|
1290
1471
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1291
1472
|
|
1292
|
-
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
1293
|
-
context_info_copy->isolate_info = context_info->isolate_info;
|
1294
|
-
context_info_copy->context = context_info->context;
|
1295
|
-
|
1296
1473
|
if (isolate_info && isolate_info->refs() > 1) {
|
1297
1474
|
pthread_t free_context_thread;
|
1475
|
+
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
1476
|
+
|
1477
|
+
context_info_copy->isolate_info = context_info->isolate_info;
|
1478
|
+
context_info_copy->context = context_info->context;
|
1298
1479
|
if (pthread_create(&free_context_thread, thread_attr_p,
|
1299
1480
|
free_context_thr, (void*)context_info_copy)) {
|
1300
1481
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1482
|
+
xfree(context_info_copy);
|
1301
1483
|
}
|
1302
1484
|
} else {
|
1303
|
-
free_context_raw(
|
1485
|
+
free_context_raw(context_info);
|
1304
1486
|
}
|
1305
1487
|
|
1306
1488
|
context_info->context = NULL;
|
@@ -1319,6 +1501,11 @@ static void mark_isolate(void* data) {
|
|
1319
1501
|
isolate_info->mark();
|
1320
1502
|
}
|
1321
1503
|
|
1504
|
+
static size_t isolate_memsize(const void *ptr) {
|
1505
|
+
const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
|
1506
|
+
return sizeof(*isolate_info);
|
1507
|
+
}
|
1508
|
+
|
1322
1509
|
static void deallocate(void* data) {
|
1323
1510
|
ContextInfo* context_info = (ContextInfo*)data;
|
1324
1511
|
|
@@ -1327,6 +1514,11 @@ static void deallocate(void* data) {
|
|
1327
1514
|
xfree(data);
|
1328
1515
|
}
|
1329
1516
|
|
1517
|
+
static size_t context_memsize(const void *ptr)
|
1518
|
+
{
|
1519
|
+
return sizeof(ContextInfo);
|
1520
|
+
}
|
1521
|
+
|
1330
1522
|
static void mark_context(void* data) {
|
1331
1523
|
ContextInfo* context_info = (ContextInfo*)data;
|
1332
1524
|
if (context_info->isolate_info) {
|
@@ -1334,48 +1526,39 @@ static void mark_context(void* data) {
|
|
1334
1526
|
}
|
1335
1527
|
}
|
1336
1528
|
|
1337
|
-
static void deallocate_external_function(void * data) {
|
1338
|
-
xfree(data);
|
1339
|
-
}
|
1340
|
-
|
1341
1529
|
static void deallocate_snapshot(void * data) {
|
1342
1530
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1343
1531
|
delete[] snapshot_info->data;
|
1344
1532
|
xfree(snapshot_info);
|
1345
1533
|
}
|
1346
1534
|
|
1347
|
-
static
|
1348
|
-
|
1349
|
-
return
|
1535
|
+
static size_t snapshot_memsize(const void *data) {
|
1536
|
+
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1537
|
+
return sizeof(*snapshot_info) + snapshot_info->raw_size;
|
1350
1538
|
}
|
1351
1539
|
|
1352
1540
|
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);
|
1541
|
+
ContextInfo* context_info;
|
1542
|
+
return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
|
1358
1543
|
}
|
1359
1544
|
|
1360
1545
|
static VALUE allocate_snapshot(VALUE klass) {
|
1361
|
-
SnapshotInfo* snapshot_info
|
1362
|
-
snapshot_info->data = NULL;
|
1363
|
-
snapshot_info->raw_size = 0;
|
1546
|
+
SnapshotInfo* snapshot_info;
|
1364
1547
|
|
1365
|
-
return
|
1548
|
+
return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
|
1366
1549
|
}
|
1367
1550
|
|
1368
1551
|
static VALUE allocate_isolate(VALUE klass) {
|
1369
1552
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1370
1553
|
|
1371
|
-
return
|
1554
|
+
return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
|
1372
1555
|
}
|
1373
1556
|
|
1374
1557
|
static VALUE
|
1375
1558
|
rb_heap_stats(VALUE self) {
|
1376
1559
|
|
1377
1560
|
ContextInfo* context_info;
|
1378
|
-
|
1561
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1379
1562
|
Isolate* isolate;
|
1380
1563
|
v8::HeapStatistics stats;
|
1381
1564
|
|
@@ -1407,7 +1590,9 @@ rb_heap_stats(VALUE self) {
|
|
1407
1590
|
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1408
1591
|
class FileOutputStream : public OutputStream {
|
1409
1592
|
public:
|
1410
|
-
|
1593
|
+
int err;
|
1594
|
+
|
1595
|
+
FileOutputStream(int fd) : fd(fd) { err = 0; }
|
1411
1596
|
|
1412
1597
|
virtual int GetChunkSize() {
|
1413
1598
|
return 65536;
|
@@ -1416,17 +1601,27 @@ class FileOutputStream : public OutputStream {
|
|
1416
1601
|
virtual void EndOfStream() {}
|
1417
1602
|
|
1418
1603
|
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1604
|
+
size_t len = static_cast<size_t>(size);
|
1605
|
+
|
1606
|
+
while (len) {
|
1607
|
+
ssize_t w = write(fd, data, len);
|
1608
|
+
|
1609
|
+
if (w > 0) {
|
1610
|
+
data += w;
|
1611
|
+
len -= w;
|
1612
|
+
} else if (w < 0) {
|
1613
|
+
err = errno;
|
1614
|
+
return kAbort;
|
1615
|
+
} else { /* w == 0, could be out-of-space */
|
1616
|
+
err = -1;
|
1617
|
+
return kAbort;
|
1618
|
+
}
|
1619
|
+
}
|
1620
|
+
return kContinue;
|
1426
1621
|
}
|
1427
1622
|
|
1428
1623
|
private:
|
1429
|
-
|
1624
|
+
int fd;
|
1430
1625
|
};
|
1431
1626
|
|
1432
1627
|
|
@@ -1439,13 +1634,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1439
1634
|
|
1440
1635
|
if (!fptr) return Qfalse;
|
1441
1636
|
|
1442
|
-
|
1443
|
-
|
1444
|
-
if (fp == NULL) return Qfalse;
|
1445
|
-
|
1637
|
+
// prepare for unbuffered write(2) below:
|
1638
|
+
rb_funcall(file, rb_intern("flush"), 0);
|
1446
1639
|
|
1447
1640
|
ContextInfo* context_info;
|
1448
|
-
|
1641
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1449
1642
|
Isolate* isolate;
|
1450
1643
|
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1451
1644
|
|
@@ -1459,13 +1652,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1459
1652
|
|
1460
1653
|
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1461
1654
|
|
1462
|
-
FileOutputStream stream(
|
1655
|
+
FileOutputStream stream(fptr->fd);
|
1463
1656
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1464
1657
|
|
1465
|
-
fflush(fp);
|
1466
|
-
|
1467
1658
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1468
1659
|
|
1660
|
+
/* TODO: perhaps rb_sys_fail here */
|
1661
|
+
if (stream.err) return Qfalse;
|
1662
|
+
|
1469
1663
|
return Qtrue;
|
1470
1664
|
}
|
1471
1665
|
|
@@ -1473,12 +1667,12 @@ static VALUE
|
|
1473
1667
|
rb_context_stop(VALUE self) {
|
1474
1668
|
|
1475
1669
|
ContextInfo* context_info;
|
1476
|
-
|
1670
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1477
1671
|
|
1478
1672
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1479
1673
|
|
1480
1674
|
// flag for termination
|
1481
|
-
isolate
|
1675
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1482
1676
|
|
1483
1677
|
isolate->TerminateExecution();
|
1484
1678
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
@@ -1490,7 +1684,7 @@ static VALUE
|
|
1490
1684
|
rb_context_dispose(VALUE self) {
|
1491
1685
|
|
1492
1686
|
ContextInfo* context_info;
|
1493
|
-
|
1687
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1494
1688
|
|
1495
1689
|
free_context(context_info);
|
1496
1690
|
|
@@ -1507,18 +1701,20 @@ nogvl_context_call(void *args) {
|
|
1507
1701
|
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
1702
|
Isolate* isolate = isolate_info->isolate;
|
1509
1703
|
|
1510
|
-
|
1511
|
-
isolate
|
1512
|
-
// terminate ASAP
|
1513
|
-
isolate->SetData(DO_TERMINATE, (void*)false);
|
1704
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1705
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1514
1706
|
|
1515
1707
|
if (call->max_memory > 0) {
|
1516
|
-
isolate
|
1517
|
-
isolate
|
1708
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1709
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1518
1710
|
if (!isolate_info->added_gc_cb) {
|
1519
|
-
|
1711
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1520
1712
|
isolate_info->added_gc_cb = true;
|
1713
|
+
}
|
1521
1714
|
}
|
1715
|
+
|
1716
|
+
if (call->marshal_stackdepth > 0) {
|
1717
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1522
1718
|
}
|
1523
1719
|
|
1524
1720
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1536,7 +1732,7 @@ nogvl_context_call(void *args) {
|
|
1536
1732
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1537
1733
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
1538
1734
|
|
1539
|
-
isolate
|
1735
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1540
1736
|
|
1541
1737
|
return NULL;
|
1542
1738
|
}
|
@@ -1551,7 +1747,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1551
1747
|
FunctionCall call;
|
1552
1748
|
VALUE *call_argv = NULL;
|
1553
1749
|
|
1554
|
-
|
1750
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1555
1751
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1556
1752
|
|
1557
1753
|
if (argc < 1) {
|
@@ -1559,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1559
1755
|
}
|
1560
1756
|
|
1561
1757
|
VALUE function_name = argv[0];
|
1562
|
-
|
1563
|
-
rb_raise(rb_eTypeError, "first argument should be a String");
|
1564
|
-
}
|
1758
|
+
Check_Type(function_name, T_STRING);
|
1565
1759
|
|
1566
1760
|
char *fname = RSTRING_PTR(function_name);
|
1567
1761
|
if (!fname) {
|
@@ -1584,6 +1778,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1584
1778
|
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1585
1779
|
call.max_memory = (size_t)sl_int;
|
1586
1780
|
}
|
1781
|
+
|
1782
|
+
call.marshal_stackdepth = 0;
|
1783
|
+
VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
|
1784
|
+
if (marshal_stackdepth != Qnil) {
|
1785
|
+
unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
|
1786
|
+
call.marshal_stackdepth = (size_t)sl_int;
|
1787
|
+
}
|
1587
1788
|
|
1588
1789
|
bool missingFunction = false;
|
1589
1790
|
{
|
@@ -1605,23 +1806,15 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1605
1806
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1606
1807
|
missingFunction = true;
|
1607
1808
|
} else {
|
1608
|
-
|
1609
1809
|
Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
|
1810
|
+
VALUE tmp;
|
1610
1811
|
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
|
-
}
|
1812
|
+
call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
|
1813
|
+
for(int i=0; i < call.argc; i++) {
|
1814
|
+
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1621
1815
|
}
|
1622
1816
|
rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
|
1623
|
-
|
1624
|
-
|
1817
|
+
RB_ALLOCV_END(tmp);
|
1625
1818
|
}
|
1626
1819
|
}
|
1627
1820
|
|
@@ -1634,7 +1827,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1634
1827
|
|
1635
1828
|
static VALUE rb_context_create_isolate_value(VALUE self) {
|
1636
1829
|
ContextInfo* context_info;
|
1637
|
-
|
1830
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1638
1831
|
IsolateInfo *isolate_info = context_info->isolate_info;
|
1639
1832
|
|
1640
1833
|
if (!isolate_info) {
|
@@ -1642,18 +1835,13 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1642
1835
|
}
|
1643
1836
|
|
1644
1837
|
isolate_info->hold();
|
1645
|
-
return
|
1838
|
+
return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
|
1646
1839
|
}
|
1647
1840
|
|
1648
1841
|
static void set_ruby_exiting(VALUE value) {
|
1649
1842
|
(void)value;
|
1650
1843
|
|
1651
|
-
|
1652
|
-
|
1653
|
-
ruby_exiting = true;
|
1654
|
-
if (res == 0) {
|
1655
|
-
pthread_rwlock_unlock(&exit_lock);
|
1656
|
-
}
|
1844
|
+
ruby_exiting.store(1);
|
1657
1845
|
}
|
1658
1846
|
|
1659
1847
|
extern "C" {
|
@@ -1698,7 +1886,6 @@ extern "C" {
|
|
1698
1886
|
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
1699
1887
|
|
1700
1888
|
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
1889
|
|
1703
1890
|
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
1704
1891
|
rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
|
@@ -1720,9 +1907,5 @@ extern "C" {
|
|
1720
1907
|
thread_attr_p = &attr;
|
1721
1908
|
}
|
1722
1909
|
}
|
1723
|
-
auto on_fork_for_child = []() {
|
1724
|
-
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1725
|
-
};
|
1726
|
-
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1727
1910
|
}
|
1728
1911
|
}
|