mini_racer 0.2.10 → 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/.dockerignore +12 -0
- data/.github/workflows/ci.yml +78 -0
- data/.gitignore +2 -0
- data/.travis.yml +13 -14
- data/CHANGELOG +89 -0
- data/Dockerfile +21 -0
- data/README.md +32 -4
- data/Rakefile +2 -1
- data/ext/mini_racer_extension/extconf.rb +32 -12
- data/ext/mini_racer_extension/mini_racer_extension.cc +552 -242
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer/version.rb +4 -1
- data/lib/mini_racer.rb +91 -15
- data/mini_racer.gemspec +5 -6
- metadata +40 -20
|
@@ -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
|
|
|
@@ -25,6 +35,7 @@ public:
|
|
|
25
35
|
ArrayBuffer::Allocator* allocator;
|
|
26
36
|
StartupData* startup_data;
|
|
27
37
|
bool interrupted;
|
|
38
|
+
bool added_gc_cb;
|
|
28
39
|
pid_t pid;
|
|
29
40
|
VALUE mutex;
|
|
30
41
|
|
|
@@ -42,15 +53,12 @@ public:
|
|
|
42
53
|
|
|
43
54
|
|
|
44
55
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
|
45
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
|
56
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
|
46
57
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
|
47
58
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
|
48
59
|
}
|
|
49
60
|
|
|
50
|
-
~IsolateInfo()
|
|
51
|
-
void free_isolate(IsolateInfo*);
|
|
52
|
-
free_isolate(this);
|
|
53
|
-
}
|
|
61
|
+
~IsolateInfo();
|
|
54
62
|
|
|
55
63
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
|
56
64
|
|
|
@@ -117,6 +125,7 @@ typedef struct {
|
|
|
117
125
|
useconds_t timeout;
|
|
118
126
|
EvalResult* result;
|
|
119
127
|
size_t max_memory;
|
|
128
|
+
size_t marshal_stackdepth;
|
|
120
129
|
} EvalParams;
|
|
121
130
|
|
|
122
131
|
typedef struct {
|
|
@@ -128,13 +137,152 @@ typedef struct {
|
|
|
128
137
|
Local<Value> *argv;
|
|
129
138
|
EvalResult result;
|
|
130
139
|
size_t max_memory;
|
|
140
|
+
size_t marshal_stackdepth;
|
|
131
141
|
} FunctionCall;
|
|
132
142
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
+
}
|
|
138
286
|
};
|
|
139
287
|
|
|
140
288
|
static VALUE rb_cContext;
|
|
@@ -156,6 +304,34 @@ static VALUE rb_cDateTime = Qnil;
|
|
|
156
304
|
static std::unique_ptr<Platform> current_platform = NULL;
|
|
157
305
|
static std::mutex platform_lock;
|
|
158
306
|
|
|
307
|
+
static pthread_attr_t *thread_attr_p;
|
|
308
|
+
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
|
309
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
|
310
|
+
static bool single_threaded = false;
|
|
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
|
+
|
|
159
335
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
160
336
|
bool platform_already_initialized = false;
|
|
161
337
|
|
|
@@ -167,7 +343,10 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
|
167
343
|
platform_lock.lock();
|
|
168
344
|
|
|
169
345
|
if (current_platform == NULL) {
|
|
170
|
-
|
|
346
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
|
347
|
+
single_threaded = true;
|
|
348
|
+
}
|
|
349
|
+
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
|
|
171
350
|
} else {
|
|
172
351
|
platform_already_initialized = true;
|
|
173
352
|
}
|
|
@@ -190,7 +369,11 @@ static void init_v8() {
|
|
|
190
369
|
|
|
191
370
|
if (current_platform == NULL) {
|
|
192
371
|
V8::InitializeICU();
|
|
193
|
-
|
|
372
|
+
if (single_threaded) {
|
|
373
|
+
current_platform = platform::NewSingleThreadedDefaultPlatform();
|
|
374
|
+
} else {
|
|
375
|
+
current_platform = platform::NewDefaultPlatform();
|
|
376
|
+
}
|
|
194
377
|
V8::InitializePlatform(current_platform.get());
|
|
195
378
|
V8::Initialize();
|
|
196
379
|
}
|
|
@@ -199,16 +382,18 @@ static void init_v8() {
|
|
|
199
382
|
}
|
|
200
383
|
|
|
201
384
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
|
202
|
-
if((
|
|
385
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
203
388
|
|
|
204
|
-
size_t softlimit =
|
|
389
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
|
205
390
|
|
|
206
391
|
HeapStatistics stats;
|
|
207
392
|
isolate->GetHeapStatistics(&stats);
|
|
208
393
|
size_t used = stats.used_heap_size();
|
|
209
394
|
|
|
210
395
|
if(used > softlimit) {
|
|
211
|
-
isolate
|
|
396
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
|
212
397
|
isolate->TerminateExecution();
|
|
213
398
|
}
|
|
214
399
|
}
|
|
@@ -233,26 +418,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
|
233
418
|
Local<Value> local_value = v8res.ToLocalChecked();
|
|
234
419
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
|
235
420
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
|
|
240
|
-
.As<Function>();
|
|
241
|
-
|
|
242
|
-
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
|
243
|
-
const unsigned argc = 1;
|
|
244
|
-
Local<Value> argv[argc] = { object };
|
|
245
|
-
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
|
421
|
+
MaybeLocal<v8::Value> ml = context->Global()->Get(
|
|
422
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"));
|
|
246
423
|
|
|
247
|
-
if (
|
|
424
|
+
if (ml.IsEmpty()) { // exception
|
|
248
425
|
evalRes.executed = false;
|
|
249
426
|
} else {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
}
|
|
254
446
|
}
|
|
255
|
-
|
|
256
447
|
} else {
|
|
257
448
|
Persistent<Value>* persistent = new Persistent<Value>();
|
|
258
449
|
persistent->Reset(isolate, local_value);
|
|
@@ -291,7 +482,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
|
291
482
|
} else if(trycatch.HasTerminated()) {
|
|
292
483
|
evalRes.terminated = true;
|
|
293
484
|
evalRes.message = new Persistent<Value>();
|
|
294
|
-
Local<String> tmp = String::
|
|
485
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
|
295
486
|
evalRes.message->Reset(isolate, tmp);
|
|
296
487
|
}
|
|
297
488
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
|
@@ -303,12 +494,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
|
303
494
|
}
|
|
304
495
|
}
|
|
305
496
|
|
|
306
|
-
void*
|
|
497
|
+
static void*
|
|
307
498
|
nogvl_context_eval(void* arg) {
|
|
308
499
|
|
|
309
500
|
EvalParams* eval_params = (EvalParams*)arg;
|
|
310
501
|
EvalResult* result = eval_params->result;
|
|
311
|
-
|
|
502
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
|
503
|
+
Isolate* isolate = isolate_info->isolate;
|
|
312
504
|
|
|
313
505
|
Isolate::Scope isolate_scope(isolate);
|
|
314
506
|
HandleScope handle_scope(isolate);
|
|
@@ -317,14 +509,15 @@ nogvl_context_eval(void* arg) {
|
|
|
317
509
|
Context::Scope context_scope(context);
|
|
318
510
|
v8::ScriptOrigin *origin = NULL;
|
|
319
511
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
+
}
|
|
328
521
|
|
|
329
522
|
MaybeLocal<Script> parsed_script;
|
|
330
523
|
|
|
@@ -350,9 +543,8 @@ nogvl_context_eval(void* arg) {
|
|
|
350
543
|
result->message->Reset(isolate, trycatch.Exception());
|
|
351
544
|
} else {
|
|
352
545
|
// parsing successful
|
|
353
|
-
if (eval_params->
|
|
354
|
-
isolate
|
|
355
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
|
546
|
+
if (eval_params->marshal_stackdepth > 0) {
|
|
547
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
|
356
548
|
}
|
|
357
549
|
|
|
358
550
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
|
@@ -360,11 +552,17 @@ nogvl_context_eval(void* arg) {
|
|
|
360
552
|
|
|
361
553
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
|
362
554
|
|
|
363
|
-
isolate
|
|
555
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
|
364
556
|
|
|
365
557
|
return NULL;
|
|
366
558
|
}
|
|
367
559
|
|
|
560
|
+
static VALUE new_empty_failed_conv_obj() {
|
|
561
|
+
// TODO isolate code that translates execption to ruby
|
|
562
|
+
// exception so we can properly return it
|
|
563
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
|
564
|
+
}
|
|
565
|
+
|
|
368
566
|
// assumes isolate locking is in place
|
|
369
567
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
370
568
|
Local<Value> value) {
|
|
@@ -372,6 +570,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
|
372
570
|
Isolate::Scope isolate_scope(isolate);
|
|
373
571
|
HandleScope scope(isolate);
|
|
374
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
|
+
|
|
375
585
|
if (value->IsNull() || value->IsUndefined()){
|
|
376
586
|
return Qnil;
|
|
377
587
|
}
|
|
@@ -396,8 +606,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
|
396
606
|
VALUE rb_array = rb_ary_new();
|
|
397
607
|
Local<Array> arr = Local<Array>::Cast(value);
|
|
398
608
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
|
399
|
-
|
|
400
|
-
|
|
609
|
+
MaybeLocal<Value> element = arr->Get(context, i);
|
|
610
|
+
if (element.IsEmpty()) {
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
|
401
614
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
|
402
615
|
return rb_elem;
|
|
403
616
|
}
|
|
@@ -427,18 +640,20 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
|
427
640
|
if (!maybe_props.IsEmpty()) {
|
|
428
641
|
Local<Array> props = maybe_props.ToLocalChecked();
|
|
429
642
|
for(uint32_t i=0; i < props->Length(); i++) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
643
|
+
MaybeLocal<Value> key = props->Get(context, i);
|
|
644
|
+
if (key.IsEmpty()) {
|
|
645
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
|
646
|
+
}
|
|
647
|
+
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
|
434
648
|
|
|
435
|
-
|
|
436
|
-
//
|
|
437
|
-
|
|
438
|
-
|
|
649
|
+
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
|
650
|
+
// this may have failed due to Get raising
|
|
651
|
+
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
|
652
|
+
return new_empty_failed_conv_obj();
|
|
439
653
|
}
|
|
440
654
|
|
|
441
|
-
VALUE rb_value = convert_v8_to_ruby(
|
|
655
|
+
VALUE rb_value = convert_v8_to_ruby(
|
|
656
|
+
isolate, context, prop_value.ToLocalChecked());
|
|
442
657
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
|
443
658
|
}
|
|
444
659
|
}
|
|
@@ -455,7 +670,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
|
455
670
|
rb_enc_find("utf-8")
|
|
456
671
|
);
|
|
457
672
|
|
|
458
|
-
return
|
|
673
|
+
return rb_str_intern(str_symbol);
|
|
459
674
|
}
|
|
460
675
|
|
|
461
676
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
|
@@ -509,7 +724,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
|
509
724
|
case T_FLOAT:
|
|
510
725
|
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
|
511
726
|
case T_STRING:
|
|
512
|
-
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());
|
|
513
728
|
case T_NIL:
|
|
514
729
|
return scope.Escape(Null(isolate));
|
|
515
730
|
case T_TRUE:
|
|
@@ -520,7 +735,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
|
520
735
|
length = RARRAY_LEN(value);
|
|
521
736
|
array = Array::New(isolate, (int)length);
|
|
522
737
|
for(i=0; i<length; i++) {
|
|
523
|
-
|
|
738
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
|
739
|
+
(void)(success);
|
|
524
740
|
}
|
|
525
741
|
return scope.Escape(array);
|
|
526
742
|
case T_HASH:
|
|
@@ -529,13 +745,14 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
|
529
745
|
length = RARRAY_LEN(hash_as_array);
|
|
530
746
|
for(i=0; i<length; i++) {
|
|
531
747
|
pair = rb_ary_entry(hash_as_array, i);
|
|
532
|
-
|
|
748
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
|
533
749
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
|
750
|
+
(void)(success);
|
|
534
751
|
}
|
|
535
752
|
return scope.Escape(object);
|
|
536
753
|
case T_SYMBOL:
|
|
537
754
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
|
538
|
-
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());
|
|
539
756
|
case T_DATA:
|
|
540
757
|
klass = rb_funcall(value, rb_intern("class"), 0);
|
|
541
758
|
if (klass == rb_cTime || klass == rb_cDateTime)
|
|
@@ -559,9 +776,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
|
559
776
|
case T_UNDEF:
|
|
560
777
|
case T_NODE:
|
|
561
778
|
default:
|
|
562
|
-
return scope.Escape(String::
|
|
779
|
+
return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
|
780
|
+
}
|
|
563
781
|
}
|
|
564
|
-
}
|
|
565
782
|
|
|
566
783
|
static void unblock_eval(void *ptr) {
|
|
567
784
|
EvalParams* eval = (EvalParams*)ptr;
|
|
@@ -572,54 +789,45 @@ static void unblock_eval(void *ptr) {
|
|
|
572
789
|
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
|
573
790
|
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
|
574
791
|
*/
|
|
575
|
-
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
|
792
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
|
576
793
|
const char *utf8_source, const char *name) {
|
|
577
794
|
Context::Scope context_scope(context);
|
|
578
795
|
TryCatch try_catch(isolate);
|
|
579
796
|
Local<String> source_string;
|
|
580
|
-
if (!String::NewFromUtf8(isolate, utf8_source
|
|
581
|
-
NewStringType::kNormal)
|
|
582
|
-
.ToLocal(&source_string)) {
|
|
797
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
|
583
798
|
return false;
|
|
584
799
|
}
|
|
585
|
-
Local<
|
|
586
|
-
|
|
587
|
-
.ToLocalChecked();
|
|
800
|
+
Local<String> resource_name =
|
|
801
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
|
588
802
|
ScriptOrigin origin(resource_name);
|
|
589
803
|
ScriptCompiler::Source source(source_string, origin);
|
|
590
804
|
Local<Script> script;
|
|
591
805
|
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
|
592
806
|
return false;
|
|
593
|
-
if (script->Run(context).IsEmpty())
|
|
594
|
-
return false;
|
|
595
|
-
// CHECK(!try_catch.HasCaught());
|
|
807
|
+
if (script->Run(context).IsEmpty()) return false;
|
|
596
808
|
return true;
|
|
597
809
|
}
|
|
598
810
|
|
|
599
|
-
StartupData
|
|
811
|
+
static StartupData
|
|
600
812
|
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
SnapshotCreator snapshot_creator;
|
|
606
|
-
Isolate *isolate = snapshot_creator.GetIsolate();
|
|
813
|
+
Isolate *isolate = Isolate::Allocate();
|
|
814
|
+
|
|
815
|
+
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
|
816
|
+
SnapshotCreator snapshot_creator(isolate);
|
|
607
817
|
{
|
|
608
818
|
HandleScope scope(isolate);
|
|
609
|
-
|
|
819
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
|
610
820
|
if (embedded_source != nullptr &&
|
|
611
|
-
!run_extra_code(isolate, context, embedded_source,
|
|
612
|
-
|
|
613
|
-
return result;
|
|
821
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
|
822
|
+
return {};
|
|
614
823
|
}
|
|
615
824
|
snapshot_creator.SetDefaultContext(context);
|
|
616
825
|
}
|
|
617
|
-
|
|
826
|
+
return snapshot_creator.CreateBlob(
|
|
618
827
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
|
619
828
|
}
|
|
620
|
-
return result;
|
|
621
|
-
}
|
|
622
829
|
|
|
830
|
+
static
|
|
623
831
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
624
832
|
const char *warmup_source) {
|
|
625
833
|
// Use following steps to create a warmed up snapshot blob from a cold one:
|
|
@@ -655,14 +863,14 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
|
655
863
|
|
|
656
864
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
|
657
865
|
SnapshotInfo* snapshot_info;
|
|
658
|
-
|
|
866
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
|
659
867
|
|
|
660
868
|
return INT2NUM(snapshot_info->raw_size);
|
|
661
869
|
}
|
|
662
870
|
|
|
663
871
|
static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
664
872
|
SnapshotInfo* snapshot_info;
|
|
665
|
-
|
|
873
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
|
666
874
|
|
|
667
875
|
if(TYPE(str) != T_STRING) {
|
|
668
876
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
|
@@ -685,14 +893,14 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
|
685
893
|
|
|
686
894
|
static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
|
|
687
895
|
SnapshotInfo* snapshot_info;
|
|
688
|
-
|
|
896
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
|
689
897
|
|
|
690
898
|
return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
|
|
691
899
|
}
|
|
692
900
|
|
|
693
901
|
static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|
694
902
|
SnapshotInfo* snapshot_info;
|
|
695
|
-
|
|
903
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
|
696
904
|
|
|
697
905
|
if(TYPE(str) != T_STRING) {
|
|
698
906
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
|
@@ -739,13 +947,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
|
|
|
739
947
|
|
|
740
948
|
static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
|
741
949
|
IsolateInfo* isolate_info;
|
|
742
|
-
|
|
950
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
|
743
951
|
|
|
744
952
|
init_v8();
|
|
745
953
|
|
|
746
954
|
SnapshotInfo* snapshot_info = nullptr;
|
|
747
955
|
if (!NIL_P(snapshot)) {
|
|
748
|
-
|
|
956
|
+
TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
|
|
749
957
|
}
|
|
750
958
|
|
|
751
959
|
isolate_info->init(snapshot_info);
|
|
@@ -756,7 +964,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
|
|
756
964
|
|
|
757
965
|
static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
758
966
|
IsolateInfo* isolate_info;
|
|
759
|
-
|
|
967
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
|
760
968
|
|
|
761
969
|
if (current_platform == NULL) return Qfalse;
|
|
762
970
|
|
|
@@ -765,9 +973,32 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
|
765
973
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
|
766
974
|
}
|
|
767
975
|
|
|
976
|
+
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
|
977
|
+
IsolateInfo* isolate_info;
|
|
978
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
|
979
|
+
|
|
980
|
+
if (current_platform == NULL) return Qfalse;
|
|
981
|
+
|
|
982
|
+
isolate_info->isolate->LowMemoryNotification();
|
|
983
|
+
return Qnil;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
|
987
|
+
IsolateInfo* isolate_info;
|
|
988
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
|
989
|
+
|
|
990
|
+
if (current_platform == NULL) return Qfalse;
|
|
991
|
+
|
|
992
|
+
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
|
993
|
+
return Qtrue;
|
|
994
|
+
} else {
|
|
995
|
+
return Qfalse;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
768
999
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
769
1000
|
ContextInfo* context_info;
|
|
770
|
-
|
|
1001
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
771
1002
|
|
|
772
1003
|
init_v8();
|
|
773
1004
|
|
|
@@ -778,11 +1009,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
|
778
1009
|
|
|
779
1010
|
SnapshotInfo *snapshot_info = nullptr;
|
|
780
1011
|
if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
|
|
781
|
-
|
|
1012
|
+
TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
|
|
782
1013
|
}
|
|
783
1014
|
isolate_info->init(snapshot_info);
|
|
784
1015
|
} else { // given isolate or snapshot
|
|
785
|
-
|
|
1016
|
+
TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
|
|
786
1017
|
}
|
|
787
1018
|
|
|
788
1019
|
context_info->isolate_info = isolate_info;
|
|
@@ -812,7 +1043,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
|
812
1043
|
static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
813
1044
|
EvalResult& result) {
|
|
814
1045
|
ContextInfo *context_info;
|
|
815
|
-
|
|
1046
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
816
1047
|
|
|
817
1048
|
Isolate *isolate = context_info->isolate_info->isolate;
|
|
818
1049
|
Persistent<Context> *p_ctx = context_info->context;
|
|
@@ -839,7 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
|
839
1070
|
// a v8 scope, if we do the scope is never cleaned up properly and we leak
|
|
840
1071
|
if (!result.parsed) {
|
|
841
1072
|
if(TYPE(message) == T_STRING) {
|
|
842
|
-
rb_raise(rb_eParseError, "%
|
|
1073
|
+
rb_raise(rb_eParseError, "%" PRIsVALUE, message);
|
|
843
1074
|
} else {
|
|
844
1075
|
rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
|
|
845
1076
|
}
|
|
@@ -848,9 +1079,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
|
848
1079
|
if (!result.executed) {
|
|
849
1080
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
|
850
1081
|
if (ruby_exception == Qnil) {
|
|
851
|
-
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);
|
|
852
1084
|
// If we were terminated or have the memory softlimit flag set
|
|
853
|
-
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) {
|
|
854
1090
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
|
855
1091
|
} else {
|
|
856
1092
|
ruby_exception = rb_eScriptRuntimeError;
|
|
@@ -858,15 +1094,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
|
858
1094
|
|
|
859
1095
|
// exception report about what happened
|
|
860
1096
|
if (TYPE(backtrace) == T_STRING) {
|
|
861
|
-
rb_raise(ruby_exception, "%
|
|
1097
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
|
|
862
1098
|
} else if(TYPE(message) == T_STRING) {
|
|
863
|
-
rb_raise(ruby_exception, "%
|
|
1099
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, message);
|
|
864
1100
|
} else {
|
|
865
1101
|
rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
|
|
866
1102
|
}
|
|
1103
|
+
} else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
|
|
1104
|
+
rb_exc_raise(ruby_exception);
|
|
867
1105
|
} else {
|
|
868
1106
|
VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
|
|
869
|
-
rb_raise(CLASS_OF(ruby_exception), "%
|
|
1107
|
+
rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
|
|
870
1108
|
}
|
|
871
1109
|
}
|
|
872
1110
|
|
|
@@ -885,6 +1123,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
|
885
1123
|
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
|
886
1124
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
|
887
1125
|
} else {
|
|
1126
|
+
StackCounter::Reset(isolate);
|
|
888
1127
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
|
889
1128
|
}
|
|
890
1129
|
|
|
@@ -907,7 +1146,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
|
907
1146
|
EvalResult eval_result;
|
|
908
1147
|
ContextInfo* context_info;
|
|
909
1148
|
|
|
910
|
-
|
|
1149
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
911
1150
|
Isolate* isolate = context_info->isolate_info->isolate;
|
|
912
1151
|
|
|
913
1152
|
if(TYPE(str) != T_STRING) {
|
|
@@ -925,13 +1164,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
|
925
1164
|
HandleScope handle_scope(isolate);
|
|
926
1165
|
|
|
927
1166
|
Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
|
|
928
|
-
NewStringType::kNormal, (
|
|
1167
|
+
NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
|
|
929
1168
|
|
|
930
1169
|
Local<String> local_filename;
|
|
931
1170
|
|
|
932
1171
|
if (filename != Qnil) {
|
|
933
1172
|
local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
|
|
934
|
-
NewStringType::kNormal, (
|
|
1173
|
+
NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
|
|
935
1174
|
eval_params.filename = &local_filename;
|
|
936
1175
|
} else {
|
|
937
1176
|
eval_params.filename = NULL;
|
|
@@ -942,6 +1181,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
|
942
1181
|
eval_params.result = &eval_result;
|
|
943
1182
|
eval_params.timeout = 0;
|
|
944
1183
|
eval_params.max_memory = 0;
|
|
1184
|
+
eval_params.marshal_stackdepth = 0;
|
|
945
1185
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
|
946
1186
|
if (timeout != Qnil) {
|
|
947
1187
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
|
@@ -952,6 +1192,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
|
952
1192
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
|
953
1193
|
}
|
|
954
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
|
+
|
|
955
1200
|
eval_result.message = NULL;
|
|
956
1201
|
eval_result.backtrace = NULL;
|
|
957
1202
|
|
|
@@ -989,7 +1234,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
|
|
|
989
1234
|
return exception;
|
|
990
1235
|
}
|
|
991
1236
|
|
|
992
|
-
void*
|
|
1237
|
+
static void*
|
|
993
1238
|
gvl_ruby_callback(void* data) {
|
|
994
1239
|
|
|
995
1240
|
FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
|
|
@@ -1005,7 +1250,7 @@ gvl_ruby_callback(void* data) {
|
|
|
1005
1250
|
HandleScope scope(args->GetIsolate());
|
|
1006
1251
|
Local<External> external = Local<External>::Cast(args->Data());
|
|
1007
1252
|
|
|
1008
|
-
self =
|
|
1253
|
+
self = (VALUE)(external->Value());
|
|
1009
1254
|
callback = rb_iv_get(self, "@callback");
|
|
1010
1255
|
|
|
1011
1256
|
parent = rb_iv_get(self, "@parent");
|
|
@@ -1013,7 +1258,7 @@ gvl_ruby_callback(void* data) {
|
|
|
1013
1258
|
return NULL;
|
|
1014
1259
|
}
|
|
1015
1260
|
|
|
1016
|
-
|
|
1261
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
|
1017
1262
|
|
|
1018
1263
|
if (length > 0) {
|
|
1019
1264
|
ruby_args = rb_ary_tmp_new(length);
|
|
@@ -1021,6 +1266,7 @@ gvl_ruby_callback(void* data) {
|
|
|
1021
1266
|
|
|
1022
1267
|
for (int i = 0; i < length; i++) {
|
|
1023
1268
|
Local<Value> value = ((*args)[i]).As<Value>();
|
|
1269
|
+
StackCounter::Reset(args->GetIsolate());
|
|
1024
1270
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
|
1025
1271
|
*context_info->context, value);
|
|
1026
1272
|
rb_ary_push(ruby_args, tmp);
|
|
@@ -1034,22 +1280,26 @@ gvl_ruby_callback(void* data) {
|
|
|
1034
1280
|
callback_data.ruby_args = ruby_args;
|
|
1035
1281
|
callback_data.failed = false;
|
|
1036
1282
|
|
|
1037
|
-
if ((
|
|
1038
|
-
args->GetIsolate()->ThrowException(
|
|
1283
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
|
1284
|
+
args->GetIsolate()->ThrowException(
|
|
1285
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
|
1286
|
+
"Terminated execution during transition from Ruby to JS"));
|
|
1039
1287
|
args->GetIsolate()->TerminateExecution();
|
|
1040
1288
|
if (length > 0) {
|
|
1041
1289
|
rb_ary_clear(ruby_args);
|
|
1042
|
-
rb_gc_force_recycle(ruby_args);
|
|
1043
1290
|
}
|
|
1044
1291
|
return NULL;
|
|
1045
1292
|
}
|
|
1046
1293
|
|
|
1047
|
-
|
|
1048
|
-
|
|
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);
|
|
1049
1299
|
|
|
1050
1300
|
if(callback_data.failed) {
|
|
1051
1301
|
rb_iv_set(parent, "@current_exception", result);
|
|
1052
|
-
args->GetIsolate()->ThrowException(String::
|
|
1302
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
|
1053
1303
|
}
|
|
1054
1304
|
else {
|
|
1055
1305
|
HandleScope scope(args->GetIsolate());
|
|
@@ -1059,26 +1309,24 @@ gvl_ruby_callback(void* data) {
|
|
|
1059
1309
|
|
|
1060
1310
|
if (length > 0) {
|
|
1061
1311
|
rb_ary_clear(ruby_args);
|
|
1062
|
-
rb_gc_force_recycle(ruby_args);
|
|
1063
1312
|
}
|
|
1064
1313
|
|
|
1065
|
-
if ((
|
|
1066
|
-
|
|
1067
|
-
isolate->TerminateExecution();
|
|
1314
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
|
1315
|
+
args->GetIsolate()->TerminateExecution();
|
|
1068
1316
|
}
|
|
1069
1317
|
|
|
1070
1318
|
return NULL;
|
|
1071
1319
|
}
|
|
1072
1320
|
|
|
1073
1321
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
|
1074
|
-
bool has_gvl = (
|
|
1322
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
|
1075
1323
|
|
|
1076
1324
|
if(has_gvl) {
|
|
1077
1325
|
gvl_ruby_callback((void*)&args);
|
|
1078
1326
|
} else {
|
|
1079
|
-
args.GetIsolate()
|
|
1327
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
|
1080
1328
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
|
1081
|
-
args.GetIsolate()
|
|
1329
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
|
1082
1330
|
}
|
|
1083
1331
|
}
|
|
1084
1332
|
|
|
@@ -1095,7 +1343,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
|
1095
1343
|
bool parse_error = false;
|
|
1096
1344
|
bool attach_error = false;
|
|
1097
1345
|
|
|
1098
|
-
|
|
1346
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
|
1099
1347
|
Isolate* isolate = context_info->isolate_info->isolate;
|
|
1100
1348
|
|
|
1101
1349
|
{
|
|
@@ -1108,32 +1356,32 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
|
1108
1356
|
|
|
1109
1357
|
Local<String> v8_str =
|
|
1110
1358
|
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
|
1111
|
-
NewStringType::kNormal, (
|
|
1359
|
+
NewStringType::kNormal, RSTRING_LENINT(name))
|
|
1112
1360
|
.ToLocalChecked();
|
|
1113
1361
|
|
|
1114
|
-
//
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
*self_copy = self;
|
|
1118
|
-
|
|
1119
|
-
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);
|
|
1120
1365
|
|
|
1121
1366
|
if (parent_object == Qnil) {
|
|
1122
|
-
context->Global()->Set(
|
|
1123
|
-
|
|
1367
|
+
Maybe<bool> success = context->Global()->Set(
|
|
1368
|
+
context,
|
|
1369
|
+
v8_str,
|
|
1370
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
|
1124
1371
|
->GetFunction(context)
|
|
1125
1372
|
.ToLocalChecked());
|
|
1373
|
+
(void)success;
|
|
1126
1374
|
|
|
1127
1375
|
} else {
|
|
1128
1376
|
Local<String> eval =
|
|
1129
1377
|
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
|
1130
1378
|
NewStringType::kNormal,
|
|
1131
|
-
(
|
|
1379
|
+
RSTRING_LENINT(parent_object_eval))
|
|
1132
1380
|
.ToLocalChecked();
|
|
1133
1381
|
|
|
1134
1382
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
|
1135
1383
|
if (parsed_script.IsEmpty()) {
|
|
1136
|
-
|
|
1384
|
+
parse_error = true;
|
|
1137
1385
|
} else {
|
|
1138
1386
|
MaybeLocal<Value> maybe_value =
|
|
1139
1387
|
parsed_script.ToLocalChecked()->Run(context);
|
|
@@ -1142,12 +1390,14 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
|
1142
1390
|
if (!maybe_value.IsEmpty()) {
|
|
1143
1391
|
Local<Value> value = maybe_value.ToLocalChecked();
|
|
1144
1392
|
if (value->IsObject()) {
|
|
1145
|
-
value.As<Object>()->Set(
|
|
1146
|
-
|
|
1147
|
-
|
|
1393
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
|
1394
|
+
context,
|
|
1395
|
+
v8_str,
|
|
1396
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
|
1148
1397
|
->GetFunction(context)
|
|
1149
1398
|
.ToLocalChecked());
|
|
1150
|
-
|
|
1399
|
+
(void)success;
|
|
1400
|
+
attach_error = false;
|
|
1151
1401
|
}
|
|
1152
1402
|
}
|
|
1153
1403
|
}
|
|
@@ -1168,7 +1418,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
|
1168
1418
|
|
|
1169
1419
|
static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1170
1420
|
ContextInfo* context_info;
|
|
1171
|
-
|
|
1421
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
1172
1422
|
|
|
1173
1423
|
if (!context_info->isolate_info) {
|
|
1174
1424
|
rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
|
|
@@ -1177,35 +1427,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
|
1177
1427
|
return context_info->isolate_info->mutex;
|
|
1178
1428
|
}
|
|
1179
1429
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
if (isolate_info->isolate) {
|
|
1187
|
-
if (isolate_info->interrupted) {
|
|
1188
|
-
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
|
|
1430
|
+
IsolateInfo::~IsolateInfo() {
|
|
1431
|
+
if (isolate) {
|
|
1432
|
+
if (this->interrupted) {
|
|
1433
|
+
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
|
1434
|
+
"it can not be disposed and memory will not be "
|
|
1435
|
+
"reclaimed till the Ruby process exits.\n");
|
|
1189
1436
|
} else {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1437
|
+
if (this->pid != getpid() && !single_threaded) {
|
|
1438
|
+
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
|
1439
|
+
"it can not be disposed and "
|
|
1440
|
+
"memory will not be reclaimed "
|
|
1441
|
+
"till the Ruby process exits.\n"
|
|
1442
|
+
"It is VERY likely your process will hang.\n"
|
|
1443
|
+
"If you wish to use v8 in forked environment "
|
|
1444
|
+
"please ensure the platform is initialized with:\n"
|
|
1445
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
|
1446
|
+
);
|
|
1193
1447
|
} else {
|
|
1194
|
-
|
|
1448
|
+
isolate->Dispose();
|
|
1195
1449
|
}
|
|
1196
1450
|
}
|
|
1197
|
-
|
|
1451
|
+
isolate = nullptr;
|
|
1198
1452
|
}
|
|
1199
1453
|
|
|
1200
|
-
if (
|
|
1201
|
-
delete[]
|
|
1202
|
-
delete
|
|
1454
|
+
if (startup_data) {
|
|
1455
|
+
delete[] startup_data->data;
|
|
1456
|
+
delete startup_data;
|
|
1203
1457
|
}
|
|
1204
1458
|
|
|
1205
|
-
delete
|
|
1459
|
+
delete allocator;
|
|
1206
1460
|
}
|
|
1207
1461
|
|
|
1208
|
-
static void
|
|
1462
|
+
static void free_context_raw(void *arg) {
|
|
1209
1463
|
ContextInfo* context_info = (ContextInfo*)arg;
|
|
1210
1464
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
|
1211
1465
|
Persistent<Context>* context = context_info->context;
|
|
@@ -1222,6 +1476,20 @@ static void *free_context_raw(void* arg) {
|
|
|
1222
1476
|
}
|
|
1223
1477
|
|
|
1224
1478
|
xfree(context_info);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
static void *free_context_thr(void* arg) {
|
|
1482
|
+
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
|
1483
|
+
return NULL;
|
|
1484
|
+
}
|
|
1485
|
+
if (ruby_exiting) {
|
|
1486
|
+
return NULL;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
free_context_raw(arg);
|
|
1490
|
+
|
|
1491
|
+
pthread_rwlock_unlock(&exit_lock);
|
|
1492
|
+
|
|
1225
1493
|
return NULL;
|
|
1226
1494
|
}
|
|
1227
1495
|
|
|
@@ -1235,22 +1503,17 @@ static void free_context(ContextInfo* context_info) {
|
|
|
1235
1503
|
context_info_copy->context = context_info->context;
|
|
1236
1504
|
|
|
1237
1505
|
if (isolate_info && isolate_info->refs() > 1) {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1506
|
+
pthread_t free_context_thread;
|
|
1507
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
|
1508
|
+
free_context_thr, (void*)context_info_copy)) {
|
|
1509
|
+
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
|
1510
|
+
}
|
|
1243
1511
|
} else {
|
|
1244
1512
|
free_context_raw(context_info_copy);
|
|
1245
1513
|
}
|
|
1246
1514
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
if (isolate_info) {
|
|
1252
|
-
context_info->isolate_info = NULL;
|
|
1253
|
-
}
|
|
1515
|
+
context_info->context = NULL;
|
|
1516
|
+
context_info->isolate_info = NULL;
|
|
1254
1517
|
}
|
|
1255
1518
|
|
|
1256
1519
|
static void deallocate_isolate(void* data) {
|
|
@@ -1265,7 +1528,12 @@ static void mark_isolate(void* data) {
|
|
|
1265
1528
|
isolate_info->mark();
|
|
1266
1529
|
}
|
|
1267
1530
|
|
|
1268
|
-
|
|
1531
|
+
static size_t isolate_memsize(const void *ptr) {
|
|
1532
|
+
const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
|
|
1533
|
+
return sizeof(*isolate_info);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
static void deallocate(void* data) {
|
|
1269
1537
|
ContextInfo* context_info = (ContextInfo*)data;
|
|
1270
1538
|
|
|
1271
1539
|
free_context(context_info);
|
|
@@ -1273,6 +1541,11 @@ void deallocate(void* data) {
|
|
|
1273
1541
|
xfree(data);
|
|
1274
1542
|
}
|
|
1275
1543
|
|
|
1544
|
+
static size_t context_memsize(const void *ptr)
|
|
1545
|
+
{
|
|
1546
|
+
return sizeof(ContextInfo);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1276
1549
|
static void mark_context(void* data) {
|
|
1277
1550
|
ContextInfo* context_info = (ContextInfo*)data;
|
|
1278
1551
|
if (context_info->isolate_info) {
|
|
@@ -1280,48 +1553,39 @@ static void mark_context(void* data) {
|
|
|
1280
1553
|
}
|
|
1281
1554
|
}
|
|
1282
1555
|
|
|
1283
|
-
void
|
|
1284
|
-
xfree(data);
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
void deallocate_snapshot(void * data) {
|
|
1556
|
+
static void deallocate_snapshot(void * data) {
|
|
1288
1557
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
|
1289
1558
|
delete[] snapshot_info->data;
|
|
1290
1559
|
xfree(snapshot_info);
|
|
1291
1560
|
}
|
|
1292
1561
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
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;
|
|
1296
1565
|
}
|
|
1297
1566
|
|
|
1298
|
-
VALUE allocate(VALUE klass) {
|
|
1299
|
-
ContextInfo* context_info
|
|
1300
|
-
|
|
1301
|
-
context_info->context = NULL;
|
|
1302
|
-
|
|
1303
|
-
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
|
1567
|
+
static VALUE allocate(VALUE klass) {
|
|
1568
|
+
ContextInfo* context_info;
|
|
1569
|
+
return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
|
|
1304
1570
|
}
|
|
1305
1571
|
|
|
1306
|
-
VALUE allocate_snapshot(VALUE klass) {
|
|
1307
|
-
SnapshotInfo* snapshot_info
|
|
1308
|
-
snapshot_info->data = NULL;
|
|
1309
|
-
snapshot_info->raw_size = 0;
|
|
1572
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
|
1573
|
+
SnapshotInfo* snapshot_info;
|
|
1310
1574
|
|
|
1311
|
-
return
|
|
1575
|
+
return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
|
|
1312
1576
|
}
|
|
1313
1577
|
|
|
1314
|
-
VALUE allocate_isolate(VALUE klass) {
|
|
1578
|
+
static VALUE allocate_isolate(VALUE klass) {
|
|
1315
1579
|
IsolateInfo* isolate_info = new IsolateInfo();
|
|
1316
1580
|
|
|
1317
|
-
return
|
|
1581
|
+
return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
|
|
1318
1582
|
}
|
|
1319
1583
|
|
|
1320
1584
|
static VALUE
|
|
1321
1585
|
rb_heap_stats(VALUE self) {
|
|
1322
1586
|
|
|
1323
1587
|
ContextInfo* context_info;
|
|
1324
|
-
|
|
1588
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
1325
1589
|
Isolate* isolate;
|
|
1326
1590
|
v8::HeapStatistics stats;
|
|
1327
1591
|
|
|
@@ -1353,7 +1617,9 @@ rb_heap_stats(VALUE self) {
|
|
|
1353
1617
|
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
|
1354
1618
|
class FileOutputStream : public OutputStream {
|
|
1355
1619
|
public:
|
|
1356
|
-
|
|
1620
|
+
int err;
|
|
1621
|
+
|
|
1622
|
+
FileOutputStream(int fd) : fd(fd) { err = 0; }
|
|
1357
1623
|
|
|
1358
1624
|
virtual int GetChunkSize() {
|
|
1359
1625
|
return 65536;
|
|
@@ -1362,17 +1628,27 @@ class FileOutputStream : public OutputStream {
|
|
|
1362
1628
|
virtual void EndOfStream() {}
|
|
1363
1629
|
|
|
1364
1630
|
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
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;
|
|
1372
1648
|
}
|
|
1373
1649
|
|
|
1374
1650
|
private:
|
|
1375
|
-
|
|
1651
|
+
int fd;
|
|
1376
1652
|
};
|
|
1377
1653
|
|
|
1378
1654
|
|
|
@@ -1385,13 +1661,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
|
1385
1661
|
|
|
1386
1662
|
if (!fptr) return Qfalse;
|
|
1387
1663
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
if (fp == NULL) return Qfalse;
|
|
1391
|
-
|
|
1664
|
+
// prepare for unbuffered write(2) below:
|
|
1665
|
+
rb_funcall(file, rb_intern("flush"), 0);
|
|
1392
1666
|
|
|
1393
1667
|
ContextInfo* context_info;
|
|
1394
|
-
|
|
1668
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
1395
1669
|
Isolate* isolate;
|
|
1396
1670
|
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
|
1397
1671
|
|
|
@@ -1405,11 +1679,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
|
1405
1679
|
|
|
1406
1680
|
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
|
1407
1681
|
|
|
1408
|
-
FileOutputStream stream(
|
|
1682
|
+
FileOutputStream stream(fptr->fd);
|
|
1409
1683
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
|
1410
1684
|
|
|
1411
1685
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
|
1412
1686
|
|
|
1687
|
+
/* TODO: perhaps rb_sys_fail here */
|
|
1688
|
+
if (stream.err) return Qfalse;
|
|
1689
|
+
|
|
1413
1690
|
return Qtrue;
|
|
1414
1691
|
}
|
|
1415
1692
|
|
|
@@ -1417,12 +1694,12 @@ static VALUE
|
|
|
1417
1694
|
rb_context_stop(VALUE self) {
|
|
1418
1695
|
|
|
1419
1696
|
ContextInfo* context_info;
|
|
1420
|
-
|
|
1697
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
1421
1698
|
|
|
1422
1699
|
Isolate* isolate = context_info->isolate_info->isolate;
|
|
1423
1700
|
|
|
1424
1701
|
// flag for termination
|
|
1425
|
-
isolate
|
|
1702
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
|
1426
1703
|
|
|
1427
1704
|
isolate->TerminateExecution();
|
|
1428
1705
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
|
@@ -1434,7 +1711,7 @@ static VALUE
|
|
|
1434
1711
|
rb_context_dispose(VALUE self) {
|
|
1435
1712
|
|
|
1436
1713
|
ContextInfo* context_info;
|
|
1437
|
-
|
|
1714
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
1438
1715
|
|
|
1439
1716
|
free_context(context_info);
|
|
1440
1717
|
|
|
@@ -1448,17 +1725,23 @@ nogvl_context_call(void *args) {
|
|
|
1448
1725
|
if (!call) {
|
|
1449
1726
|
return NULL;
|
|
1450
1727
|
}
|
|
1451
|
-
|
|
1728
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
|
1729
|
+
Isolate* isolate = isolate_info->isolate;
|
|
1452
1730
|
|
|
1453
|
-
|
|
1454
|
-
isolate
|
|
1455
|
-
// terminate ASAP
|
|
1456
|
-
isolate->SetData(DO_TERMINATE, (void*)false);
|
|
1731
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
|
1732
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
|
1457
1733
|
|
|
1458
1734
|
if (call->max_memory > 0) {
|
|
1459
|
-
isolate
|
|
1460
|
-
isolate
|
|
1461
|
-
|
|
1735
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
|
1736
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
|
1737
|
+
if (!isolate_info->added_gc_cb) {
|
|
1738
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
|
1739
|
+
isolate_info->added_gc_cb = true;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
if (call->marshal_stackdepth > 0) {
|
|
1744
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
|
1462
1745
|
}
|
|
1463
1746
|
|
|
1464
1747
|
Isolate::Scope isolate_scope(isolate);
|
|
@@ -1476,7 +1759,7 @@ nogvl_context_call(void *args) {
|
|
|
1476
1759
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
|
1477
1760
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
|
1478
1761
|
|
|
1479
|
-
isolate
|
|
1762
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
|
1480
1763
|
|
|
1481
1764
|
return NULL;
|
|
1482
1765
|
}
|
|
@@ -1491,7 +1774,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
|
1491
1774
|
FunctionCall call;
|
|
1492
1775
|
VALUE *call_argv = NULL;
|
|
1493
1776
|
|
|
1494
|
-
|
|
1777
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
1495
1778
|
Isolate* isolate = context_info->isolate_info->isolate;
|
|
1496
1779
|
|
|
1497
1780
|
if (argc < 1) {
|
|
@@ -1524,6 +1807,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
|
1524
1807
|
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
|
1525
1808
|
call.max_memory = (size_t)sl_int;
|
|
1526
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
|
+
}
|
|
1527
1817
|
|
|
1528
1818
|
bool missingFunction = false;
|
|
1529
1819
|
{
|
|
@@ -1536,29 +1826,24 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
|
1536
1826
|
|
|
1537
1827
|
// examples of such usage can be found in
|
|
1538
1828
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
|
1539
|
-
|
|
1540
|
-
MaybeLocal<v8::Value> val
|
|
1829
|
+
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
|
1830
|
+
MaybeLocal<v8::Value> val;
|
|
1831
|
+
if (!fname.IsEmpty()) {
|
|
1832
|
+
val = context->Global()->Get(context, fname.ToLocalChecked());
|
|
1833
|
+
}
|
|
1541
1834
|
|
|
1542
1835
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
|
1543
1836
|
missingFunction = true;
|
|
1544
1837
|
} else {
|
|
1545
|
-
|
|
1546
1838
|
Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
|
|
1839
|
+
VALUE tmp;
|
|
1547
1840
|
call.fun = fun;
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
|
|
1552
|
-
if (!call.argv) {
|
|
1553
|
-
return Qnil;
|
|
1554
|
-
}
|
|
1555
|
-
for(int i=0; i < fun_argc; i++) {
|
|
1556
|
-
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
|
1557
|
-
}
|
|
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]);
|
|
1558
1844
|
}
|
|
1559
1845
|
rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
|
|
1560
|
-
|
|
1561
|
-
|
|
1846
|
+
RB_ALLOCV_END(tmp);
|
|
1562
1847
|
}
|
|
1563
1848
|
}
|
|
1564
1849
|
|
|
@@ -1571,7 +1856,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
|
1571
1856
|
|
|
1572
1857
|
static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1573
1858
|
ContextInfo* context_info;
|
|
1574
|
-
|
|
1859
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
|
1575
1860
|
IsolateInfo *isolate_info = context_info->isolate_info;
|
|
1576
1861
|
|
|
1577
1862
|
if (!isolate_info) {
|
|
@@ -1579,12 +1864,23 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
|
1579
1864
|
}
|
|
1580
1865
|
|
|
1581
1866
|
isolate_info->hold();
|
|
1582
|
-
return
|
|
1867
|
+
return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
static void set_ruby_exiting(VALUE value) {
|
|
1871
|
+
(void)value;
|
|
1872
|
+
|
|
1873
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
|
1874
|
+
|
|
1875
|
+
ruby_exiting = true;
|
|
1876
|
+
if (res == 0) {
|
|
1877
|
+
pthread_rwlock_unlock(&exit_lock);
|
|
1878
|
+
}
|
|
1583
1879
|
}
|
|
1584
1880
|
|
|
1585
1881
|
extern "C" {
|
|
1586
1882
|
|
|
1587
|
-
void Init_mini_racer_extension ( void )
|
|
1883
|
+
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
|
1588
1884
|
{
|
|
1589
1885
|
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
|
1590
1886
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
|
@@ -1624,7 +1920,6 @@ extern "C" {
|
|
|
1624
1920
|
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
|
1625
1921
|
|
|
1626
1922
|
rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
|
|
1627
|
-
rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
|
|
1628
1923
|
|
|
1629
1924
|
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
|
1630
1925
|
rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
|
|
@@ -1632,8 +1927,23 @@ extern "C" {
|
|
|
1632
1927
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
|
1633
1928
|
|
|
1634
1929
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
|
1930
|
+
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
|
1931
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
|
1635
1932
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
|
1636
1933
|
|
|
1637
1934
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
|
1935
|
+
|
|
1936
|
+
rb_set_end_proc(set_ruby_exiting, Qnil);
|
|
1937
|
+
|
|
1938
|
+
static pthread_attr_t attr;
|
|
1939
|
+
if (pthread_attr_init(&attr) == 0) {
|
|
1940
|
+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
|
1941
|
+
thread_attr_p = &attr;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
auto on_fork_for_child = []() {
|
|
1945
|
+
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
|
1946
|
+
};
|
|
1947
|
+
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
|
1638
1948
|
}
|
|
1639
1949
|
}
|