mini_racer 0.17.0.pre5 → 0.17.0.pre6
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/CHANGELOG +4 -0
- data/ext/mini_racer_extension/extconf.rb +2 -0
- data/ext/mini_racer_extension/mini_racer_extension.c +1564 -0
- data/ext/mini_racer_extension/mini_racer_v8.cc +840 -0
- data/ext/mini_racer_extension/mini_racer_v8.h +56 -0
- data/ext/mini_racer_extension/serde.c +747 -0
- data/lib/mini_racer/truffleruby.rb +31 -4
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +14 -387
- metadata +10 -7
- data/ext/mini_racer_extension/mini_racer_extension.cc +0 -1942
@@ -1,1942 +0,0 @@
|
|
1
|
-
#include <stdio.h>
|
2
|
-
#include <ruby.h>
|
3
|
-
#include <ruby/thread.h>
|
4
|
-
#include <ruby/io.h>
|
5
|
-
#include <ruby/version.h>
|
6
|
-
#include <v8.h>
|
7
|
-
#include <v8-profiler.h>
|
8
|
-
#include <libplatform/libplatform.h>
|
9
|
-
#include <ruby/encoding.h>
|
10
|
-
#include <pthread.h>
|
11
|
-
#include <unistd.h>
|
12
|
-
#include <mutex>
|
13
|
-
#include <atomic>
|
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
|
24
|
-
|
25
|
-
using namespace v8;
|
26
|
-
|
27
|
-
typedef struct {
|
28
|
-
const char* data;
|
29
|
-
int raw_size;
|
30
|
-
} SnapshotInfo;
|
31
|
-
|
32
|
-
class IsolateInfo {
|
33
|
-
public:
|
34
|
-
Isolate* isolate;
|
35
|
-
ArrayBuffer::Allocator* allocator;
|
36
|
-
StartupData* startup_data;
|
37
|
-
bool interrupted;
|
38
|
-
bool added_gc_cb;
|
39
|
-
pid_t pid;
|
40
|
-
VALUE mutex;
|
41
|
-
|
42
|
-
class Lock {
|
43
|
-
VALUE &mutex;
|
44
|
-
|
45
|
-
public:
|
46
|
-
Lock(VALUE &mutex) : mutex(mutex) {
|
47
|
-
rb_mutex_lock(mutex);
|
48
|
-
}
|
49
|
-
~Lock() {
|
50
|
-
rb_mutex_unlock(mutex);
|
51
|
-
}
|
52
|
-
};
|
53
|
-
|
54
|
-
|
55
|
-
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
56
|
-
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
57
|
-
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
58
|
-
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
59
|
-
}
|
60
|
-
|
61
|
-
~IsolateInfo();
|
62
|
-
|
63
|
-
void init(SnapshotInfo* snapshot_info = nullptr);
|
64
|
-
|
65
|
-
void mark() {
|
66
|
-
rb_gc_mark(mutex);
|
67
|
-
}
|
68
|
-
|
69
|
-
Lock createLock() {
|
70
|
-
Lock lock(mutex);
|
71
|
-
return lock;
|
72
|
-
}
|
73
|
-
|
74
|
-
void hold() {
|
75
|
-
refs_count++;
|
76
|
-
}
|
77
|
-
void release() {
|
78
|
-
if (--refs_count <= 0) {
|
79
|
-
delete this;
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
int refs() {
|
84
|
-
return refs_count;
|
85
|
-
}
|
86
|
-
|
87
|
-
static void* operator new(size_t size) {
|
88
|
-
return ruby_xmalloc(size);
|
89
|
-
}
|
90
|
-
|
91
|
-
static void operator delete(void *block) {
|
92
|
-
xfree(block);
|
93
|
-
}
|
94
|
-
private:
|
95
|
-
// how many references to this isolate exist
|
96
|
-
// we can't rely on Ruby's GC for this, because Ruby could destroy the
|
97
|
-
// isolate before destroying the contexts that depend on them. We'd need to
|
98
|
-
// keep a list of linked contexts in the isolate to destroy those first when
|
99
|
-
// isolate destruction was requested. Keeping such a list would require
|
100
|
-
// notification from the context VALUEs when they are constructed and
|
101
|
-
// destroyed. With a ref count, those notifications are still needed, but
|
102
|
-
// we keep a simple int rather than a list of pointers.
|
103
|
-
std::atomic_int refs_count;
|
104
|
-
};
|
105
|
-
|
106
|
-
typedef struct {
|
107
|
-
IsolateInfo* isolate_info;
|
108
|
-
Persistent<Context>* context;
|
109
|
-
} ContextInfo;
|
110
|
-
|
111
|
-
typedef struct {
|
112
|
-
bool parsed;
|
113
|
-
bool executed;
|
114
|
-
bool terminated;
|
115
|
-
bool json;
|
116
|
-
Persistent<Value>* value;
|
117
|
-
Persistent<Value>* message;
|
118
|
-
Persistent<Value>* backtrace;
|
119
|
-
} EvalResult;
|
120
|
-
|
121
|
-
typedef struct {
|
122
|
-
ContextInfo* context_info;
|
123
|
-
Local<String>* eval;
|
124
|
-
Local<String>* filename;
|
125
|
-
useconds_t timeout;
|
126
|
-
EvalResult* result;
|
127
|
-
size_t max_memory;
|
128
|
-
size_t marshal_stackdepth;
|
129
|
-
} EvalParams;
|
130
|
-
|
131
|
-
typedef struct {
|
132
|
-
ContextInfo *context_info;
|
133
|
-
char *function_name;
|
134
|
-
int argc;
|
135
|
-
bool error;
|
136
|
-
Local<Function> fun;
|
137
|
-
Local<Value> *argv;
|
138
|
-
EvalResult result;
|
139
|
-
size_t max_memory;
|
140
|
-
size_t marshal_stackdepth;
|
141
|
-
} FunctionCall;
|
142
|
-
|
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
|
-
}
|
286
|
-
};
|
287
|
-
|
288
|
-
static VALUE rb_cContext;
|
289
|
-
static VALUE rb_cSnapshot;
|
290
|
-
static VALUE rb_cIsolate;
|
291
|
-
|
292
|
-
static VALUE rb_eScriptTerminatedError;
|
293
|
-
static VALUE rb_eV8OutOfMemoryError;
|
294
|
-
static VALUE rb_eParseError;
|
295
|
-
static VALUE rb_eScriptRuntimeError;
|
296
|
-
static VALUE rb_cJavaScriptFunction;
|
297
|
-
static VALUE rb_eSnapshotError;
|
298
|
-
static VALUE rb_ePlatformAlreadyInitializedError;
|
299
|
-
static VALUE rb_mJSON;
|
300
|
-
|
301
|
-
static VALUE rb_cFailedV8Conversion;
|
302
|
-
static VALUE rb_cDateTime = Qnil;
|
303
|
-
|
304
|
-
static std::unique_ptr<Platform> current_platform = NULL;
|
305
|
-
static std::mutex platform_lock;
|
306
|
-
|
307
|
-
static pthread_attr_t *thread_attr_p;
|
308
|
-
static std::atomic_int ruby_exiting(0);
|
309
|
-
static bool single_threaded = false;
|
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
|
-
|
334
|
-
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
335
|
-
bool platform_already_initialized = false;
|
336
|
-
|
337
|
-
Check_Type(flag_as_str, T_STRING);
|
338
|
-
|
339
|
-
platform_lock.lock();
|
340
|
-
|
341
|
-
if (current_platform == NULL) {
|
342
|
-
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
343
|
-
single_threaded = true;
|
344
|
-
}
|
345
|
-
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
|
346
|
-
} else {
|
347
|
-
platform_already_initialized = true;
|
348
|
-
}
|
349
|
-
|
350
|
-
platform_lock.unlock();
|
351
|
-
|
352
|
-
// important to raise outside of the lock
|
353
|
-
if (platform_already_initialized) {
|
354
|
-
rb_raise(rb_ePlatformAlreadyInitializedError, "The V8 platform is already initialized");
|
355
|
-
}
|
356
|
-
|
357
|
-
return Qnil;
|
358
|
-
}
|
359
|
-
|
360
|
-
static void init_v8() {
|
361
|
-
// no need to wait for the lock if already initialized
|
362
|
-
if (current_platform != NULL) return;
|
363
|
-
|
364
|
-
platform_lock.lock();
|
365
|
-
|
366
|
-
if (current_platform == NULL) {
|
367
|
-
V8::InitializeICU();
|
368
|
-
if (single_threaded) {
|
369
|
-
current_platform = platform::NewSingleThreadedDefaultPlatform();
|
370
|
-
} else {
|
371
|
-
current_platform = platform::NewDefaultPlatform();
|
372
|
-
}
|
373
|
-
V8::InitializePlatform(current_platform.get());
|
374
|
-
V8::Initialize();
|
375
|
-
}
|
376
|
-
|
377
|
-
platform_lock.unlock();
|
378
|
-
}
|
379
|
-
|
380
|
-
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
381
|
-
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
382
|
-
return;
|
383
|
-
}
|
384
|
-
|
385
|
-
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
386
|
-
|
387
|
-
HeapStatistics stats;
|
388
|
-
isolate->GetHeapStatistics(&stats);
|
389
|
-
size_t used = stats.used_heap_size();
|
390
|
-
|
391
|
-
if(used > softlimit) {
|
392
|
-
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
393
|
-
isolate->TerminateExecution();
|
394
|
-
}
|
395
|
-
}
|
396
|
-
|
397
|
-
// to be called with active lock and scope
|
398
|
-
static void prepare_result(MaybeLocal<Value> v8res,
|
399
|
-
TryCatch& trycatch,
|
400
|
-
Isolate* isolate,
|
401
|
-
Local<Context> context,
|
402
|
-
EvalResult& evalRes /* out */) {
|
403
|
-
|
404
|
-
// just don't touch .parsed
|
405
|
-
evalRes.terminated = false;
|
406
|
-
evalRes.json = false;
|
407
|
-
evalRes.value = nullptr;
|
408
|
-
evalRes.message = nullptr;
|
409
|
-
evalRes.backtrace = nullptr;
|
410
|
-
evalRes.executed = !v8res.IsEmpty();
|
411
|
-
|
412
|
-
if (evalRes.executed) {
|
413
|
-
// arrays and objects get converted to json
|
414
|
-
Local<Value> local_value = v8res.ToLocalChecked();
|
415
|
-
if ((local_value->IsObject() || local_value->IsArray()) &&
|
416
|
-
!local_value->IsDate() && !local_value->IsFunction()) {
|
417
|
-
MaybeLocal<v8::Value> ml = context->Global()->Get(
|
418
|
-
context, String::NewFromUtf8Literal(isolate, "JSON"));
|
419
|
-
|
420
|
-
if (ml.IsEmpty()) { // exception
|
421
|
-
evalRes.executed = false;
|
422
|
-
} else {
|
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> maybe_json = stringify->Call(context, JSON, argc, argv);
|
433
|
-
Local<Value> json;
|
434
|
-
|
435
|
-
if (!maybe_json.ToLocal(&json)) {
|
436
|
-
evalRes.executed = false;
|
437
|
-
} else {
|
438
|
-
// JSON.stringify() returns undefined for inputs that
|
439
|
-
// are exotic objects, like WASM function or string refs
|
440
|
-
evalRes.json = !json->IsUndefined();
|
441
|
-
Persistent<Value>* persistent = new Persistent<Value>();
|
442
|
-
persistent->Reset(isolate, json);
|
443
|
-
evalRes.value = persistent;
|
444
|
-
}
|
445
|
-
}
|
446
|
-
} else {
|
447
|
-
Persistent<Value>* persistent = new Persistent<Value>();
|
448
|
-
persistent->Reset(isolate, local_value);
|
449
|
-
evalRes.value = persistent;
|
450
|
-
}
|
451
|
-
}
|
452
|
-
|
453
|
-
if (!evalRes.executed || !evalRes.parsed) {
|
454
|
-
if (trycatch.HasCaught()) {
|
455
|
-
if (!trycatch.Exception()->IsNull()) {
|
456
|
-
evalRes.message = new Persistent<Value>();
|
457
|
-
Local<Message> message = trycatch.Message();
|
458
|
-
char buf[1000];
|
459
|
-
int len, line, column;
|
460
|
-
|
461
|
-
if (!message->GetLineNumber(context).To(&line)) {
|
462
|
-
line = 0;
|
463
|
-
}
|
464
|
-
|
465
|
-
if (!message->GetStartColumn(context).To(&column)) {
|
466
|
-
column = 0;
|
467
|
-
}
|
468
|
-
|
469
|
-
len = snprintf(buf, sizeof(buf), "%s at %s:%i:%i", *String::Utf8Value(isolate, message->Get()),
|
470
|
-
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString(context).ToLocalChecked()),
|
471
|
-
line,
|
472
|
-
column);
|
473
|
-
|
474
|
-
if ((size_t) len >= sizeof(buf)) {
|
475
|
-
len = sizeof(buf) - 1;
|
476
|
-
buf[len] = '\0';
|
477
|
-
}
|
478
|
-
|
479
|
-
Local<String> v8_message = String::NewFromUtf8(isolate, buf, NewStringType::kNormal, len).ToLocalChecked();
|
480
|
-
evalRes.message->Reset(isolate, v8_message);
|
481
|
-
} else if(trycatch.HasTerminated()) {
|
482
|
-
evalRes.terminated = true;
|
483
|
-
evalRes.message = new Persistent<Value>();
|
484
|
-
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
485
|
-
evalRes.message->Reset(isolate, tmp);
|
486
|
-
}
|
487
|
-
if (!trycatch.StackTrace(context).IsEmpty()) {
|
488
|
-
evalRes.backtrace = new Persistent<Value>();
|
489
|
-
evalRes.backtrace->Reset(isolate,
|
490
|
-
trycatch.StackTrace(context).ToLocalChecked()->ToString(context).ToLocalChecked());
|
491
|
-
}
|
492
|
-
}
|
493
|
-
}
|
494
|
-
}
|
495
|
-
|
496
|
-
static void*
|
497
|
-
nogvl_context_eval(void* arg) {
|
498
|
-
|
499
|
-
EvalParams* eval_params = (EvalParams*)arg;
|
500
|
-
EvalResult* result = eval_params->result;
|
501
|
-
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
502
|
-
Isolate* isolate = isolate_info->isolate;
|
503
|
-
|
504
|
-
Isolate::Scope isolate_scope(isolate);
|
505
|
-
HandleScope handle_scope(isolate);
|
506
|
-
TryCatch trycatch(isolate);
|
507
|
-
Local<Context> context = eval_params->context_info->context->Get(isolate);
|
508
|
-
Context::Scope context_scope(context);
|
509
|
-
v8::ScriptOrigin *origin = NULL;
|
510
|
-
|
511
|
-
IsolateData::Init(isolate);
|
512
|
-
|
513
|
-
if (eval_params->max_memory > 0) {
|
514
|
-
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
|
515
|
-
if (!isolate_info->added_gc_cb) {
|
516
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
517
|
-
isolate_info->added_gc_cb = true;
|
518
|
-
}
|
519
|
-
}
|
520
|
-
|
521
|
-
MaybeLocal<Script> parsed_script;
|
522
|
-
|
523
|
-
if (eval_params->filename) {
|
524
|
-
origin = new v8::ScriptOrigin(isolate, *eval_params->filename);
|
525
|
-
}
|
526
|
-
|
527
|
-
parsed_script = Script::Compile(context, *eval_params->eval, origin);
|
528
|
-
|
529
|
-
if (origin) {
|
530
|
-
delete origin;
|
531
|
-
}
|
532
|
-
|
533
|
-
result->parsed = !parsed_script.IsEmpty();
|
534
|
-
result->executed = false;
|
535
|
-
result->terminated = false;
|
536
|
-
result->json = false;
|
537
|
-
result->value = NULL;
|
538
|
-
|
539
|
-
MaybeLocal<Value> maybe_value;
|
540
|
-
if (!result->parsed) {
|
541
|
-
result->message = new Persistent<Value>();
|
542
|
-
result->message->Reset(isolate, trycatch.Exception());
|
543
|
-
} else {
|
544
|
-
// parsing successful
|
545
|
-
if (eval_params->marshal_stackdepth > 0) {
|
546
|
-
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
547
|
-
}
|
548
|
-
|
549
|
-
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
550
|
-
}
|
551
|
-
|
552
|
-
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
553
|
-
|
554
|
-
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
555
|
-
|
556
|
-
return NULL;
|
557
|
-
}
|
558
|
-
|
559
|
-
static VALUE new_empty_failed_conv_obj() {
|
560
|
-
// TODO isolate code that translates execption to ruby
|
561
|
-
// exception so we can properly return it
|
562
|
-
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
563
|
-
}
|
564
|
-
|
565
|
-
// assumes isolate locking is in place
|
566
|
-
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
567
|
-
Local<Value> value) {
|
568
|
-
|
569
|
-
Isolate::Scope isolate_scope(isolate);
|
570
|
-
HandleScope scope(isolate);
|
571
|
-
Context::Scope context_scope(context);
|
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
|
-
|
585
|
-
if (value->IsNull() || value->IsUndefined()){
|
586
|
-
return Qnil;
|
587
|
-
}
|
588
|
-
|
589
|
-
if (value->IsInt32()) {
|
590
|
-
return INT2FIX(value->Int32Value(context).ToChecked());
|
591
|
-
}
|
592
|
-
|
593
|
-
if (value->IsTrue()) {
|
594
|
-
return Qtrue;
|
595
|
-
}
|
596
|
-
|
597
|
-
if (value->IsFalse()) {
|
598
|
-
return Qfalse;
|
599
|
-
}
|
600
|
-
|
601
|
-
struct State {
|
602
|
-
Isolate* isolate;
|
603
|
-
Local<Value> value;
|
604
|
-
Local<Context> context;
|
605
|
-
} state = {isolate, value, context};
|
606
|
-
|
607
|
-
// calls to rb_*() functions can raise exceptions and longjmp,
|
608
|
-
// and therefore must be inside this protected block
|
609
|
-
auto can_raise = [](VALUE arg) -> VALUE {
|
610
|
-
State* state =
|
611
|
-
reinterpret_cast<State*>(static_cast<uintptr_t>(RB_NUM2ULL(arg)));
|
612
|
-
Isolate* isolate = state->isolate;
|
613
|
-
Local<Value> value = state->value;
|
614
|
-
Local<Context> context = state->context;
|
615
|
-
|
616
|
-
if (value->IsNumber()) {
|
617
|
-
return rb_float_new(value->NumberValue(context).ToChecked());
|
618
|
-
}
|
619
|
-
|
620
|
-
if (value->IsArray()) {
|
621
|
-
VALUE rb_array = rb_ary_new();
|
622
|
-
Local<Array> arr = Local<Array>::Cast(value);
|
623
|
-
for(uint32_t i = 0; i < arr->Length(); i++) {
|
624
|
-
MaybeLocal<Value> element = arr->Get(context, i);
|
625
|
-
if (element.IsEmpty()) {
|
626
|
-
continue;
|
627
|
-
}
|
628
|
-
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
629
|
-
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
630
|
-
return rb_elem;
|
631
|
-
}
|
632
|
-
rb_ary_push(rb_array, rb_elem);
|
633
|
-
}
|
634
|
-
return rb_array;
|
635
|
-
}
|
636
|
-
|
637
|
-
if (value->IsFunction()){
|
638
|
-
return rb_funcall(rb_cJavaScriptFunction, rb_intern("new"), 0);
|
639
|
-
}
|
640
|
-
|
641
|
-
if (value->IsDate()){
|
642
|
-
double ts = Local<Date>::Cast(value)->ValueOf();
|
643
|
-
double secs = ts/1000;
|
644
|
-
long nanos = round((secs - floor(secs)) * 1000000);
|
645
|
-
|
646
|
-
return rb_time_new(secs, nanos);
|
647
|
-
}
|
648
|
-
|
649
|
-
if (value->IsObject()) {
|
650
|
-
VALUE rb_hash = rb_hash_new();
|
651
|
-
TryCatch trycatch(isolate);
|
652
|
-
|
653
|
-
Local<Object> object = value->ToObject(context).ToLocalChecked();
|
654
|
-
auto maybe_props = object->GetOwnPropertyNames(context);
|
655
|
-
if (!maybe_props.IsEmpty()) {
|
656
|
-
Local<Array> props = maybe_props.ToLocalChecked();
|
657
|
-
for (uint32_t i = 0; i < props->Length(); i++) {
|
658
|
-
MaybeLocal<Value> key = props->Get(context, i);
|
659
|
-
if (key.IsEmpty()) {
|
660
|
-
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
661
|
-
}
|
662
|
-
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
663
|
-
|
664
|
-
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
665
|
-
// this may have failed due to Get raising
|
666
|
-
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
667
|
-
return new_empty_failed_conv_obj();
|
668
|
-
}
|
669
|
-
|
670
|
-
VALUE rb_value = convert_v8_to_ruby(
|
671
|
-
isolate, context, prop_value.ToLocalChecked());
|
672
|
-
rb_hash_aset(rb_hash, rb_key, rb_value);
|
673
|
-
}
|
674
|
-
}
|
675
|
-
return rb_hash;
|
676
|
-
}
|
677
|
-
|
678
|
-
if (value->IsSymbol()) {
|
679
|
-
Local<Symbol> symbol = Local<Symbol>::Cast(value);
|
680
|
-
Local<Value> description = symbol->Description(isolate);
|
681
|
-
v8::String::Utf8Value symbol_name(isolate, description);
|
682
|
-
|
683
|
-
VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
|
684
|
-
|
685
|
-
return rb_str_intern(str_symbol);
|
686
|
-
}
|
687
|
-
|
688
|
-
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
689
|
-
|
690
|
-
if (rstr_maybe.IsEmpty()) {
|
691
|
-
return Qnil;
|
692
|
-
} else {
|
693
|
-
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
694
|
-
return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
695
|
-
}
|
696
|
-
};
|
697
|
-
|
698
|
-
// this is kind of slow because RB_ULL2NUM allocs when the pointer
|
699
|
-
// doesn't fit in a fixnum but yolo'ing and reinterpret_casting the
|
700
|
-
// pointer to a VALUE is probably not very sound
|
701
|
-
VALUE arg = RB_ULL2NUM(reinterpret_cast<uintptr_t>(&state));
|
702
|
-
return rb_protect(can_raise, arg, nullptr);
|
703
|
-
}
|
704
|
-
|
705
|
-
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
706
|
-
const Persistent<Context>& context,
|
707
|
-
Local<Value> value) {
|
708
|
-
HandleScope scope(isolate);
|
709
|
-
return convert_v8_to_ruby(isolate,
|
710
|
-
Local<Context>::New(isolate, context),
|
711
|
-
value);
|
712
|
-
}
|
713
|
-
|
714
|
-
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
715
|
-
const Persistent<Context>& context,
|
716
|
-
const Persistent<Value>& value) {
|
717
|
-
HandleScope scope(isolate);
|
718
|
-
return convert_v8_to_ruby(isolate,
|
719
|
-
Local<Context>::New(isolate, context),
|
720
|
-
Local<Value>::New(isolate, value));
|
721
|
-
}
|
722
|
-
|
723
|
-
static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context, VALUE value) {
|
724
|
-
EscapableHandleScope scope(isolate);
|
725
|
-
|
726
|
-
Local<Array> array;
|
727
|
-
Local<Object> object;
|
728
|
-
VALUE hash_as_array;
|
729
|
-
VALUE pair;
|
730
|
-
int i;
|
731
|
-
long length;
|
732
|
-
long fixnum;
|
733
|
-
VALUE klass;
|
734
|
-
|
735
|
-
switch (TYPE(value)) {
|
736
|
-
case T_FIXNUM:
|
737
|
-
fixnum = NUM2LONG(value);
|
738
|
-
if (fixnum > INT_MAX)
|
739
|
-
{
|
740
|
-
return scope.Escape(Number::New(isolate, (double)fixnum));
|
741
|
-
}
|
742
|
-
return scope.Escape(Integer::New(isolate, (int)fixnum));
|
743
|
-
case T_FLOAT:
|
744
|
-
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
745
|
-
case T_STRING:
|
746
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
747
|
-
case T_NIL:
|
748
|
-
return scope.Escape(Null(isolate));
|
749
|
-
case T_TRUE:
|
750
|
-
return scope.Escape(True(isolate));
|
751
|
-
case T_FALSE:
|
752
|
-
return scope.Escape(False(isolate));
|
753
|
-
case T_ARRAY:
|
754
|
-
length = RARRAY_LEN(value);
|
755
|
-
array = Array::New(isolate, (int)length);
|
756
|
-
for(i=0; i<length; i++) {
|
757
|
-
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
758
|
-
(void)(success);
|
759
|
-
}
|
760
|
-
return scope.Escape(array);
|
761
|
-
case T_HASH:
|
762
|
-
object = Object::New(isolate);
|
763
|
-
hash_as_array = rb_funcall(value, rb_intern("to_a"), 0);
|
764
|
-
length = RARRAY_LEN(hash_as_array);
|
765
|
-
for(i=0; i<length; i++) {
|
766
|
-
pair = rb_ary_entry(hash_as_array, i);
|
767
|
-
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
768
|
-
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
769
|
-
(void)(success);
|
770
|
-
}
|
771
|
-
return scope.Escape(object);
|
772
|
-
case T_SYMBOL:
|
773
|
-
value = rb_funcall(value, rb_intern("to_s"), 0);
|
774
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
775
|
-
case T_DATA:
|
776
|
-
klass = rb_funcall(value, rb_intern("class"), 0);
|
777
|
-
if (klass == rb_cTime || klass == rb_cDateTime)
|
778
|
-
{
|
779
|
-
if (klass == rb_cDateTime)
|
780
|
-
{
|
781
|
-
value = rb_funcall(value, rb_intern("to_time"), 0);
|
782
|
-
}
|
783
|
-
value = rb_funcall(value, rb_intern("to_f"), 0);
|
784
|
-
return scope.Escape(Date::New(context, NUM2DBL(value) * 1000).ToLocalChecked());
|
785
|
-
}
|
786
|
-
case T_OBJECT:
|
787
|
-
case T_CLASS:
|
788
|
-
case T_ICLASS:
|
789
|
-
case T_MODULE:
|
790
|
-
case T_REGEXP:
|
791
|
-
case T_MATCH:
|
792
|
-
case T_STRUCT:
|
793
|
-
case T_BIGNUM:
|
794
|
-
case T_FILE:
|
795
|
-
case T_UNDEF:
|
796
|
-
case T_NODE:
|
797
|
-
default:
|
798
|
-
return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
799
|
-
}
|
800
|
-
}
|
801
|
-
|
802
|
-
static void unblock_eval(void *ptr) {
|
803
|
-
EvalParams* eval = (EvalParams*)ptr;
|
804
|
-
eval->context_info->isolate_info->interrupted = true;
|
805
|
-
}
|
806
|
-
|
807
|
-
/*
|
808
|
-
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
809
|
-
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
810
|
-
*/
|
811
|
-
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
812
|
-
const char *utf8_source, const char *name) {
|
813
|
-
Context::Scope context_scope(context);
|
814
|
-
TryCatch try_catch(isolate);
|
815
|
-
Local<String> source_string;
|
816
|
-
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
817
|
-
return false;
|
818
|
-
}
|
819
|
-
Local<String> resource_name =
|
820
|
-
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
821
|
-
ScriptOrigin origin(isolate, resource_name);
|
822
|
-
ScriptCompiler::Source source(source_string, origin);
|
823
|
-
Local<Script> script;
|
824
|
-
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
825
|
-
return false;
|
826
|
-
if (script->Run(context).IsEmpty()) return false;
|
827
|
-
return true;
|
828
|
-
}
|
829
|
-
|
830
|
-
static StartupData
|
831
|
-
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
832
|
-
Isolate *isolate = Isolate::Allocate();
|
833
|
-
|
834
|
-
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
835
|
-
SnapshotCreator snapshot_creator(isolate);
|
836
|
-
{
|
837
|
-
HandleScope scope(isolate);
|
838
|
-
Local<v8::Context> context = v8::Context::New(isolate);
|
839
|
-
if (embedded_source != nullptr &&
|
840
|
-
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
841
|
-
return {};
|
842
|
-
}
|
843
|
-
snapshot_creator.SetDefaultContext(context);
|
844
|
-
}
|
845
|
-
return snapshot_creator.CreateBlob(
|
846
|
-
SnapshotCreator::FunctionCodeHandling::kClear);
|
847
|
-
}
|
848
|
-
|
849
|
-
static
|
850
|
-
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
851
|
-
const char *warmup_source) {
|
852
|
-
// Use following steps to create a warmed up snapshot blob from a cold one:
|
853
|
-
// - Create a new isolate from the cold snapshot.
|
854
|
-
// - Create a new context to run the warmup script. This will trigger
|
855
|
-
// compilation of executed functions.
|
856
|
-
// - Create a new context. This context will be unpolluted.
|
857
|
-
// - Serialize the isolate and the second context into a new snapshot blob.
|
858
|
-
StartupData result = {nullptr, 0};
|
859
|
-
|
860
|
-
if (cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != nullptr &&
|
861
|
-
warmup_source != NULL) {
|
862
|
-
SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
|
863
|
-
Isolate *isolate = snapshot_creator.GetIsolate();
|
864
|
-
{
|
865
|
-
HandleScope scope(isolate);
|
866
|
-
Local<Context> context = Context::New(isolate);
|
867
|
-
if (!run_extra_code(isolate, context, warmup_source, "<warm-up>")) {
|
868
|
-
return result;
|
869
|
-
}
|
870
|
-
}
|
871
|
-
{
|
872
|
-
HandleScope handle_scope(isolate);
|
873
|
-
isolate->ContextDisposedNotification(false);
|
874
|
-
Local<Context> context = Context::New(isolate);
|
875
|
-
snapshot_creator.SetDefaultContext(context);
|
876
|
-
}
|
877
|
-
result = snapshot_creator.CreateBlob(
|
878
|
-
SnapshotCreator::FunctionCodeHandling::kKeep);
|
879
|
-
}
|
880
|
-
return result;
|
881
|
-
}
|
882
|
-
|
883
|
-
static VALUE rb_snapshot_size(VALUE self) {
|
884
|
-
SnapshotInfo* snapshot_info;
|
885
|
-
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
886
|
-
|
887
|
-
return INT2NUM(snapshot_info->raw_size);
|
888
|
-
}
|
889
|
-
|
890
|
-
static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
891
|
-
SnapshotInfo* snapshot_info;
|
892
|
-
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
893
|
-
|
894
|
-
Check_Type(str, T_STRING);
|
895
|
-
|
896
|
-
init_v8();
|
897
|
-
|
898
|
-
StartupData startup_data = create_snapshot_data_blob(RSTRING_PTR(str));
|
899
|
-
|
900
|
-
if (startup_data.data == NULL && startup_data.raw_size == 0) {
|
901
|
-
rb_raise(rb_eSnapshotError, "Could not create snapshot, most likely the source is incorrect");
|
902
|
-
}
|
903
|
-
|
904
|
-
snapshot_info->data = startup_data.data;
|
905
|
-
snapshot_info->raw_size = startup_data.raw_size;
|
906
|
-
|
907
|
-
return Qnil;
|
908
|
-
}
|
909
|
-
|
910
|
-
static VALUE rb_snapshot_dump(VALUE self) {
|
911
|
-
SnapshotInfo* snapshot_info;
|
912
|
-
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
913
|
-
|
914
|
-
return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
|
915
|
-
}
|
916
|
-
|
917
|
-
static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
918
|
-
SnapshotInfo* snapshot_info;
|
919
|
-
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
920
|
-
|
921
|
-
Check_Type(str, T_STRING);
|
922
|
-
|
923
|
-
init_v8();
|
924
|
-
|
925
|
-
StartupData cold_startup_data = {snapshot_info->data, snapshot_info->raw_size};
|
926
|
-
StartupData warm_startup_data = warm_up_snapshot_data_blob(cold_startup_data, RSTRING_PTR(str));
|
927
|
-
|
928
|
-
if (warm_startup_data.data == NULL && warm_startup_data.raw_size == 0) {
|
929
|
-
rb_raise(rb_eSnapshotError, "Could not warm up snapshot, most likely the source is incorrect");
|
930
|
-
} else {
|
931
|
-
delete[] snapshot_info->data;
|
932
|
-
|
933
|
-
snapshot_info->data = warm_startup_data.data;
|
934
|
-
snapshot_info->raw_size = warm_startup_data.raw_size;
|
935
|
-
}
|
936
|
-
|
937
|
-
return self;
|
938
|
-
}
|
939
|
-
|
940
|
-
void IsolateInfo::init(SnapshotInfo* snapshot_info) {
|
941
|
-
allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
|
942
|
-
|
943
|
-
Isolate::CreateParams create_params;
|
944
|
-
create_params.array_buffer_allocator = allocator;
|
945
|
-
|
946
|
-
if (snapshot_info) {
|
947
|
-
int raw_size = snapshot_info->raw_size;
|
948
|
-
char* data = new char[raw_size];
|
949
|
-
memcpy(data, snapshot_info->data, raw_size);
|
950
|
-
|
951
|
-
startup_data = new StartupData;
|
952
|
-
startup_data->data = data;
|
953
|
-
startup_data->raw_size = raw_size;
|
954
|
-
|
955
|
-
create_params.snapshot_blob = startup_data;
|
956
|
-
}
|
957
|
-
|
958
|
-
isolate = Isolate::New(create_params);
|
959
|
-
}
|
960
|
-
|
961
|
-
static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
962
|
-
IsolateInfo* isolate_info;
|
963
|
-
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
964
|
-
|
965
|
-
init_v8();
|
966
|
-
|
967
|
-
SnapshotInfo* snapshot_info = nullptr;
|
968
|
-
if (!NIL_P(snapshot)) {
|
969
|
-
TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
|
970
|
-
}
|
971
|
-
|
972
|
-
isolate_info->init(snapshot_info);
|
973
|
-
isolate_info->hold();
|
974
|
-
|
975
|
-
return Qnil;
|
976
|
-
}
|
977
|
-
|
978
|
-
static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
979
|
-
IsolateInfo* isolate_info;
|
980
|
-
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
981
|
-
|
982
|
-
if (current_platform == NULL) return Qfalse;
|
983
|
-
|
984
|
-
double duration = NUM2DBL(idle_time_in_ms) / 1000.0;
|
985
|
-
double now = current_platform->MonotonicallyIncreasingTime();
|
986
|
-
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
987
|
-
}
|
988
|
-
|
989
|
-
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
990
|
-
IsolateInfo* isolate_info;
|
991
|
-
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
992
|
-
|
993
|
-
if (current_platform == NULL) return Qfalse;
|
994
|
-
|
995
|
-
Locker guard { isolate_info->isolate };
|
996
|
-
|
997
|
-
isolate_info->isolate->LowMemoryNotification();
|
998
|
-
return Qnil;
|
999
|
-
}
|
1000
|
-
|
1001
|
-
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
1002
|
-
IsolateInfo* isolate_info;
|
1003
|
-
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
1004
|
-
|
1005
|
-
if (current_platform == NULL) return Qfalse;
|
1006
|
-
|
1007
|
-
Locker guard { isolate_info->isolate };
|
1008
|
-
|
1009
|
-
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
1010
|
-
return Qtrue;
|
1011
|
-
} else {
|
1012
|
-
return Qfalse;
|
1013
|
-
}
|
1014
|
-
}
|
1015
|
-
|
1016
|
-
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
1017
|
-
ContextInfo* context_info;
|
1018
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1019
|
-
|
1020
|
-
init_v8();
|
1021
|
-
|
1022
|
-
IsolateInfo* isolate_info;
|
1023
|
-
|
1024
|
-
if (NIL_P(isolate) || !rb_obj_is_kind_of(isolate, rb_cIsolate)) {
|
1025
|
-
isolate_info = new IsolateInfo();
|
1026
|
-
|
1027
|
-
SnapshotInfo *snapshot_info = nullptr;
|
1028
|
-
if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
|
1029
|
-
TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
|
1030
|
-
}
|
1031
|
-
isolate_info->init(snapshot_info);
|
1032
|
-
} else { // given isolate or snapshot
|
1033
|
-
TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
|
1034
|
-
}
|
1035
|
-
|
1036
|
-
context_info->isolate_info = isolate_info;
|
1037
|
-
isolate_info->hold();
|
1038
|
-
|
1039
|
-
{
|
1040
|
-
// the ruby lock is needed if this isn't a new isolate
|
1041
|
-
IsolateInfo::Lock ruby_lock(isolate_info->mutex);
|
1042
|
-
Locker lock(isolate_info->isolate);
|
1043
|
-
Isolate::Scope isolate_scope(isolate_info->isolate);
|
1044
|
-
HandleScope handle_scope(isolate_info->isolate);
|
1045
|
-
|
1046
|
-
Local<Context> context = Context::New(isolate_info->isolate);
|
1047
|
-
|
1048
|
-
context_info->context = new Persistent<Context>();
|
1049
|
-
context_info->context->Reset(isolate_info->isolate, context);
|
1050
|
-
}
|
1051
|
-
|
1052
|
-
if (Qnil == rb_cDateTime && rb_funcall(rb_cObject, rb_intern("const_defined?"), 1, rb_str_new2("DateTime")) == Qtrue)
|
1053
|
-
{
|
1054
|
-
rb_cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
1055
|
-
}
|
1056
|
-
|
1057
|
-
return Qnil;
|
1058
|
-
}
|
1059
|
-
|
1060
|
-
static VALUE convert_result_to_ruby(VALUE self /* context */,
|
1061
|
-
EvalResult& result) {
|
1062
|
-
ContextInfo *context_info;
|
1063
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1064
|
-
|
1065
|
-
Isolate *isolate = context_info->isolate_info->isolate;
|
1066
|
-
Persistent<Context> *p_ctx = context_info->context;
|
1067
|
-
|
1068
|
-
VALUE message = Qnil;
|
1069
|
-
VALUE backtrace = Qnil;
|
1070
|
-
{
|
1071
|
-
Locker lock(isolate);
|
1072
|
-
if (result.message) {
|
1073
|
-
message = convert_v8_to_ruby(isolate, *p_ctx, *result.message);
|
1074
|
-
result.message->Reset();
|
1075
|
-
delete result.message;
|
1076
|
-
result.message = nullptr;
|
1077
|
-
}
|
1078
|
-
|
1079
|
-
if (result.backtrace) {
|
1080
|
-
backtrace = convert_v8_to_ruby(isolate, *p_ctx, *result.backtrace);
|
1081
|
-
result.backtrace->Reset();
|
1082
|
-
delete result.backtrace;
|
1083
|
-
}
|
1084
|
-
}
|
1085
|
-
|
1086
|
-
// NOTE: this is very important, we can not do an rb_raise from within
|
1087
|
-
// a v8 scope, if we do the scope is never cleaned up properly and we leak
|
1088
|
-
if (!result.parsed) {
|
1089
|
-
if(TYPE(message) == T_STRING) {
|
1090
|
-
rb_raise(rb_eParseError, "%" PRIsVALUE, message);
|
1091
|
-
} else {
|
1092
|
-
rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
|
1093
|
-
}
|
1094
|
-
}
|
1095
|
-
|
1096
|
-
if (!result.executed) {
|
1097
|
-
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
1098
|
-
if (ruby_exception == Qnil) {
|
1099
|
-
bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
|
1100
|
-
bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
|
1101
|
-
// If we were terminated or have the memory softlimit flag set
|
1102
|
-
if (marshal_stack_maxdepth_reached) {
|
1103
|
-
ruby_exception = rb_eScriptRuntimeError;
|
1104
|
-
message = rb_utf8_str_new_literal("Marshal object depth too deep. Script terminated.");
|
1105
|
-
} else if (result.terminated || mem_softlimit_reached) {
|
1106
|
-
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
1107
|
-
} else {
|
1108
|
-
ruby_exception = rb_eScriptRuntimeError;
|
1109
|
-
}
|
1110
|
-
|
1111
|
-
// exception report about what happened
|
1112
|
-
if (TYPE(backtrace) == T_STRING) {
|
1113
|
-
rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
|
1114
|
-
} else if(TYPE(message) == T_STRING) {
|
1115
|
-
rb_raise(ruby_exception, "%" PRIsVALUE, message);
|
1116
|
-
} else {
|
1117
|
-
rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
|
1118
|
-
}
|
1119
|
-
} else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
|
1120
|
-
rb_exc_raise(ruby_exception);
|
1121
|
-
} else {
|
1122
|
-
VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
|
1123
|
-
rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
|
1124
|
-
}
|
1125
|
-
}
|
1126
|
-
|
1127
|
-
VALUE ret = Qnil;
|
1128
|
-
|
1129
|
-
// New scope for return value
|
1130
|
-
{
|
1131
|
-
Locker lock(isolate);
|
1132
|
-
Isolate::Scope isolate_scope(isolate);
|
1133
|
-
HandleScope handle_scope(isolate);
|
1134
|
-
|
1135
|
-
Local<Value> tmp = Local<Value>::New(isolate, *result.value);
|
1136
|
-
|
1137
|
-
if (result.json) {
|
1138
|
-
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
1139
|
-
VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
1140
|
-
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
1141
|
-
} else {
|
1142
|
-
StackCounter::Reset(isolate);
|
1143
|
-
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
1144
|
-
}
|
1145
|
-
|
1146
|
-
result.value->Reset();
|
1147
|
-
delete result.value;
|
1148
|
-
}
|
1149
|
-
|
1150
|
-
if (rb_funcall(ret, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
1151
|
-
// TODO try to recover stack trace from the conversion error
|
1152
|
-
rb_raise(rb_eScriptRuntimeError, "Error converting JS object to Ruby object");
|
1153
|
-
}
|
1154
|
-
|
1155
|
-
|
1156
|
-
return ret;
|
1157
|
-
}
|
1158
|
-
|
1159
|
-
static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
1160
|
-
|
1161
|
-
EvalParams eval_params;
|
1162
|
-
EvalResult eval_result;
|
1163
|
-
ContextInfo* context_info;
|
1164
|
-
|
1165
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1166
|
-
Isolate* isolate = context_info->isolate_info->isolate;
|
1167
|
-
|
1168
|
-
Check_Type(str, T_STRING);
|
1169
|
-
|
1170
|
-
if (!NIL_P(filename)) {
|
1171
|
-
Check_Type(filename, T_STRING);
|
1172
|
-
}
|
1173
|
-
|
1174
|
-
{
|
1175
|
-
Locker lock(isolate);
|
1176
|
-
Isolate::Scope isolate_scope(isolate);
|
1177
|
-
HandleScope handle_scope(isolate);
|
1178
|
-
|
1179
|
-
Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
|
1180
|
-
NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
|
1181
|
-
|
1182
|
-
Local<String> local_filename;
|
1183
|
-
|
1184
|
-
if (filename != Qnil) {
|
1185
|
-
local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
|
1186
|
-
NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
|
1187
|
-
eval_params.filename = &local_filename;
|
1188
|
-
} else {
|
1189
|
-
eval_params.filename = NULL;
|
1190
|
-
}
|
1191
|
-
|
1192
|
-
eval_params.context_info = context_info;
|
1193
|
-
eval_params.eval = &eval;
|
1194
|
-
eval_params.result = &eval_result;
|
1195
|
-
eval_params.timeout = 0;
|
1196
|
-
eval_params.max_memory = 0;
|
1197
|
-
eval_params.marshal_stackdepth = 0;
|
1198
|
-
VALUE timeout = rb_iv_get(self, "@timeout");
|
1199
|
-
if (timeout != Qnil) {
|
1200
|
-
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
1201
|
-
}
|
1202
|
-
|
1203
|
-
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1204
|
-
if (mem_softlimit != Qnil) {
|
1205
|
-
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
1206
|
-
}
|
1207
|
-
|
1208
|
-
VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
|
1209
|
-
if (stack_depth != Qnil) {
|
1210
|
-
eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
|
1211
|
-
}
|
1212
|
-
|
1213
|
-
eval_result.message = NULL;
|
1214
|
-
eval_result.backtrace = NULL;
|
1215
|
-
|
1216
|
-
rb_thread_call_without_gvl(nogvl_context_eval, &eval_params, unblock_eval, &eval_params);
|
1217
|
-
}
|
1218
|
-
|
1219
|
-
return convert_result_to_ruby(self, eval_result);
|
1220
|
-
}
|
1221
|
-
|
1222
|
-
typedef struct {
|
1223
|
-
VALUE callback;
|
1224
|
-
int length;
|
1225
|
-
VALUE ruby_args;
|
1226
|
-
bool failed;
|
1227
|
-
} protected_callback_data;
|
1228
|
-
|
1229
|
-
static VALUE protected_callback(VALUE rdata) {
|
1230
|
-
protected_callback_data* data = (protected_callback_data*)rdata;
|
1231
|
-
VALUE result;
|
1232
|
-
|
1233
|
-
if (data->length > 0) {
|
1234
|
-
result = rb_funcall2(data->callback, rb_intern("call"), data->length,
|
1235
|
-
RARRAY_PTR(data->ruby_args));
|
1236
|
-
RB_GC_GUARD(data->ruby_args);
|
1237
|
-
} else {
|
1238
|
-
result = rb_funcall(data->callback, rb_intern("call"), 0);
|
1239
|
-
}
|
1240
|
-
return result;
|
1241
|
-
}
|
1242
|
-
|
1243
|
-
static
|
1244
|
-
VALUE rescue_callback(VALUE rdata, VALUE exception) {
|
1245
|
-
protected_callback_data* data = (protected_callback_data*)rdata;
|
1246
|
-
data->failed = true;
|
1247
|
-
return exception;
|
1248
|
-
}
|
1249
|
-
|
1250
|
-
static void*
|
1251
|
-
gvl_ruby_callback(void* data) {
|
1252
|
-
|
1253
|
-
FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
|
1254
|
-
VALUE ruby_args = Qnil;
|
1255
|
-
int length = args->Length();
|
1256
|
-
VALUE callback;
|
1257
|
-
VALUE result;
|
1258
|
-
VALUE self;
|
1259
|
-
VALUE parent;
|
1260
|
-
ContextInfo* context_info;
|
1261
|
-
|
1262
|
-
{
|
1263
|
-
HandleScope scope(args->GetIsolate());
|
1264
|
-
Local<External> external = Local<External>::Cast(args->Data());
|
1265
|
-
|
1266
|
-
self = (VALUE)(external->Value());
|
1267
|
-
callback = rb_iv_get(self, "@callback");
|
1268
|
-
|
1269
|
-
parent = rb_iv_get(self, "@parent");
|
1270
|
-
if (NIL_P(parent) || !rb_obj_is_kind_of(parent, rb_cContext)) {
|
1271
|
-
return NULL;
|
1272
|
-
}
|
1273
|
-
|
1274
|
-
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1275
|
-
|
1276
|
-
if (length > 0) {
|
1277
|
-
ruby_args = rb_ary_tmp_new(length);
|
1278
|
-
}
|
1279
|
-
|
1280
|
-
for (int i = 0; i < length; i++) {
|
1281
|
-
Local<Value> value = ((*args)[i]).As<Value>();
|
1282
|
-
StackCounter::Reset(args->GetIsolate());
|
1283
|
-
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1284
|
-
*context_info->context, value);
|
1285
|
-
rb_ary_push(ruby_args, tmp);
|
1286
|
-
}
|
1287
|
-
}
|
1288
|
-
|
1289
|
-
// may raise exception stay clear of handle scope
|
1290
|
-
protected_callback_data callback_data;
|
1291
|
-
callback_data.length = length;
|
1292
|
-
callback_data.callback = callback;
|
1293
|
-
callback_data.ruby_args = ruby_args;
|
1294
|
-
callback_data.failed = false;
|
1295
|
-
|
1296
|
-
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1297
|
-
args->GetIsolate()->ThrowException(
|
1298
|
-
String::NewFromUtf8Literal(args->GetIsolate(),
|
1299
|
-
"Terminated execution during transition from Ruby to JS"));
|
1300
|
-
args->GetIsolate()->TerminateExecution();
|
1301
|
-
if (length > 0) {
|
1302
|
-
rb_ary_clear(ruby_args);
|
1303
|
-
}
|
1304
|
-
return NULL;
|
1305
|
-
}
|
1306
|
-
|
1307
|
-
VALUE callback_data_value = (VALUE)&callback_data;
|
1308
|
-
|
1309
|
-
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1310
|
-
result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
|
1311
|
-
MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
|
1312
|
-
|
1313
|
-
if(callback_data.failed) {
|
1314
|
-
rb_iv_set(parent, "@current_exception", result);
|
1315
|
-
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1316
|
-
}
|
1317
|
-
else {
|
1318
|
-
HandleScope scope(args->GetIsolate());
|
1319
|
-
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), context_info->context->Get(args->GetIsolate()), result);
|
1320
|
-
args->GetReturnValue().Set(v8_result);
|
1321
|
-
}
|
1322
|
-
|
1323
|
-
if (length > 0) {
|
1324
|
-
rb_ary_clear(ruby_args);
|
1325
|
-
}
|
1326
|
-
|
1327
|
-
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1328
|
-
args->GetIsolate()->TerminateExecution();
|
1329
|
-
}
|
1330
|
-
|
1331
|
-
return NULL;
|
1332
|
-
}
|
1333
|
-
|
1334
|
-
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1335
|
-
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1336
|
-
|
1337
|
-
if(has_gvl) {
|
1338
|
-
gvl_ruby_callback((void*)&args);
|
1339
|
-
} else {
|
1340
|
-
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1341
|
-
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1342
|
-
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1343
|
-
}
|
1344
|
-
}
|
1345
|
-
|
1346
|
-
|
1347
|
-
static VALUE rb_external_function_notify_v8(VALUE self) {
|
1348
|
-
|
1349
|
-
ContextInfo* context_info;
|
1350
|
-
|
1351
|
-
VALUE parent = rb_iv_get(self, "@parent");
|
1352
|
-
VALUE name = rb_iv_get(self, "@name");
|
1353
|
-
VALUE parent_object = rb_iv_get(self, "@parent_object");
|
1354
|
-
VALUE parent_object_eval = rb_iv_get(self, "@parent_object_eval");
|
1355
|
-
|
1356
|
-
bool parse_error = false;
|
1357
|
-
bool attach_error = false;
|
1358
|
-
|
1359
|
-
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1360
|
-
Isolate* isolate = context_info->isolate_info->isolate;
|
1361
|
-
|
1362
|
-
{
|
1363
|
-
Locker lock(isolate);
|
1364
|
-
Isolate::Scope isolate_scope(isolate);
|
1365
|
-
HandleScope handle_scope(isolate);
|
1366
|
-
|
1367
|
-
Local<Context> context = context_info->context->Get(isolate);
|
1368
|
-
Context::Scope context_scope(context);
|
1369
|
-
|
1370
|
-
Local<String> v8_str =
|
1371
|
-
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1372
|
-
NewStringType::kNormal, RSTRING_LENINT(name))
|
1373
|
-
.ToLocalChecked();
|
1374
|
-
|
1375
|
-
// Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
|
1376
|
-
// not a T_DATA type like most other classes in this file
|
1377
|
-
Local<Value> external = External::New(isolate, (void *)self);
|
1378
|
-
|
1379
|
-
if (parent_object == Qnil) {
|
1380
|
-
Maybe<bool> success = context->Global()->Set(
|
1381
|
-
context,
|
1382
|
-
v8_str,
|
1383
|
-
FunctionTemplate::New(isolate, ruby_callback, external)
|
1384
|
-
->GetFunction(context)
|
1385
|
-
.ToLocalChecked());
|
1386
|
-
(void)success;
|
1387
|
-
|
1388
|
-
} else {
|
1389
|
-
Local<String> eval =
|
1390
|
-
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
1391
|
-
NewStringType::kNormal,
|
1392
|
-
RSTRING_LENINT(parent_object_eval))
|
1393
|
-
.ToLocalChecked();
|
1394
|
-
|
1395
|
-
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1396
|
-
if (parsed_script.IsEmpty()) {
|
1397
|
-
parse_error = true;
|
1398
|
-
} else {
|
1399
|
-
MaybeLocal<Value> maybe_value =
|
1400
|
-
parsed_script.ToLocalChecked()->Run(context);
|
1401
|
-
attach_error = true;
|
1402
|
-
|
1403
|
-
if (!maybe_value.IsEmpty()) {
|
1404
|
-
Local<Value> value = maybe_value.ToLocalChecked();
|
1405
|
-
if (value->IsObject()) {
|
1406
|
-
Maybe<bool> success = value.As<Object>()->Set(
|
1407
|
-
context,
|
1408
|
-
v8_str,
|
1409
|
-
FunctionTemplate::New(isolate, ruby_callback, external)
|
1410
|
-
->GetFunction(context)
|
1411
|
-
.ToLocalChecked());
|
1412
|
-
(void)success;
|
1413
|
-
attach_error = false;
|
1414
|
-
}
|
1415
|
-
}
|
1416
|
-
}
|
1417
|
-
}
|
1418
|
-
}
|
1419
|
-
|
1420
|
-
// always raise out of V8 context
|
1421
|
-
if (parse_error) {
|
1422
|
-
rb_raise(rb_eParseError, "Invalid object %" PRIsVALUE, parent_object);
|
1423
|
-
}
|
1424
|
-
|
1425
|
-
if (attach_error) {
|
1426
|
-
rb_raise(rb_eParseError, "Was expecting %" PRIsVALUE" to be an object", parent_object);
|
1427
|
-
}
|
1428
|
-
|
1429
|
-
return Qnil;
|
1430
|
-
}
|
1431
|
-
|
1432
|
-
static VALUE rb_context_isolate_mutex(VALUE self) {
|
1433
|
-
ContextInfo* context_info;
|
1434
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1435
|
-
|
1436
|
-
if (!context_info->isolate_info) {
|
1437
|
-
rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
|
1438
|
-
}
|
1439
|
-
|
1440
|
-
return context_info->isolate_info->mutex;
|
1441
|
-
}
|
1442
|
-
|
1443
|
-
IsolateInfo::~IsolateInfo() {
|
1444
|
-
if (isolate) {
|
1445
|
-
if (this->interrupted) {
|
1446
|
-
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
1447
|
-
"it can not be disposed and memory will not be "
|
1448
|
-
"reclaimed till the Ruby process exits.\n");
|
1449
|
-
} else {
|
1450
|
-
if (this->pid != getpid() && !single_threaded) {
|
1451
|
-
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1452
|
-
"it can not be disposed and "
|
1453
|
-
"memory will not be reclaimed "
|
1454
|
-
"till the Ruby process exits.\n"
|
1455
|
-
"It is VERY likely your process will hang.\n"
|
1456
|
-
"If you wish to use v8 in forked environment "
|
1457
|
-
"please ensure the platform is initialized with:\n"
|
1458
|
-
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1459
|
-
);
|
1460
|
-
} else {
|
1461
|
-
isolate->Dispose();
|
1462
|
-
}
|
1463
|
-
}
|
1464
|
-
isolate = nullptr;
|
1465
|
-
}
|
1466
|
-
|
1467
|
-
if (startup_data) {
|
1468
|
-
delete[] startup_data->data;
|
1469
|
-
delete startup_data;
|
1470
|
-
}
|
1471
|
-
|
1472
|
-
delete allocator;
|
1473
|
-
}
|
1474
|
-
|
1475
|
-
static void free_context_raw(void *arg) {
|
1476
|
-
ContextInfo* context_info = (ContextInfo*)arg;
|
1477
|
-
IsolateInfo* isolate_info = context_info->isolate_info;
|
1478
|
-
Persistent<Context>* context = context_info->context;
|
1479
|
-
|
1480
|
-
if (context && isolate_info && isolate_info->isolate) {
|
1481
|
-
Locker lock(isolate_info->isolate);
|
1482
|
-
v8::Isolate::Scope isolate_scope(isolate_info->isolate);
|
1483
|
-
context->Reset();
|
1484
|
-
delete context;
|
1485
|
-
}
|
1486
|
-
|
1487
|
-
if (isolate_info) {
|
1488
|
-
isolate_info->release();
|
1489
|
-
}
|
1490
|
-
}
|
1491
|
-
|
1492
|
-
static void *free_context_thr(void* arg) {
|
1493
|
-
if (ruby_exiting.load() == 0) {
|
1494
|
-
free_context_raw(arg);
|
1495
|
-
xfree(arg);
|
1496
|
-
}
|
1497
|
-
return NULL;
|
1498
|
-
}
|
1499
|
-
|
1500
|
-
// destroys everything except freeing the ContextInfo struct (see deallocate())
|
1501
|
-
static void free_context(ContextInfo* context_info) {
|
1502
|
-
IsolateInfo* isolate_info = context_info->isolate_info;
|
1503
|
-
|
1504
|
-
if (isolate_info && isolate_info->refs() > 1) {
|
1505
|
-
pthread_t free_context_thread;
|
1506
|
-
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
1507
|
-
|
1508
|
-
context_info_copy->isolate_info = context_info->isolate_info;
|
1509
|
-
context_info_copy->context = context_info->context;
|
1510
|
-
if (pthread_create(&free_context_thread, thread_attr_p,
|
1511
|
-
free_context_thr, (void*)context_info_copy)) {
|
1512
|
-
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1513
|
-
xfree(context_info_copy);
|
1514
|
-
}
|
1515
|
-
} else {
|
1516
|
-
free_context_raw(context_info);
|
1517
|
-
}
|
1518
|
-
|
1519
|
-
context_info->context = NULL;
|
1520
|
-
context_info->isolate_info = NULL;
|
1521
|
-
}
|
1522
|
-
|
1523
|
-
static void deallocate_isolate(void* data) {
|
1524
|
-
|
1525
|
-
IsolateInfo* isolate_info = (IsolateInfo*) data;
|
1526
|
-
|
1527
|
-
isolate_info->release();
|
1528
|
-
}
|
1529
|
-
|
1530
|
-
static void mark_isolate(void* data) {
|
1531
|
-
IsolateInfo* isolate_info = (IsolateInfo*) data;
|
1532
|
-
isolate_info->mark();
|
1533
|
-
}
|
1534
|
-
|
1535
|
-
static size_t isolate_memsize(const void *ptr) {
|
1536
|
-
const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
|
1537
|
-
return sizeof(*isolate_info);
|
1538
|
-
}
|
1539
|
-
|
1540
|
-
static void deallocate(void* data) {
|
1541
|
-
ContextInfo* context_info = (ContextInfo*)data;
|
1542
|
-
|
1543
|
-
free_context(context_info);
|
1544
|
-
|
1545
|
-
xfree(data);
|
1546
|
-
}
|
1547
|
-
|
1548
|
-
static size_t context_memsize(const void *ptr)
|
1549
|
-
{
|
1550
|
-
return sizeof(ContextInfo);
|
1551
|
-
}
|
1552
|
-
|
1553
|
-
static void mark_context(void* data) {
|
1554
|
-
ContextInfo* context_info = (ContextInfo*)data;
|
1555
|
-
if (context_info->isolate_info) {
|
1556
|
-
context_info->isolate_info->mark();
|
1557
|
-
}
|
1558
|
-
}
|
1559
|
-
|
1560
|
-
static void deallocate_snapshot(void * data) {
|
1561
|
-
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1562
|
-
delete[] snapshot_info->data;
|
1563
|
-
xfree(snapshot_info);
|
1564
|
-
}
|
1565
|
-
|
1566
|
-
static size_t snapshot_memsize(const void *data) {
|
1567
|
-
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1568
|
-
return sizeof(*snapshot_info) + snapshot_info->raw_size;
|
1569
|
-
}
|
1570
|
-
|
1571
|
-
static VALUE allocate(VALUE klass) {
|
1572
|
-
ContextInfo* context_info;
|
1573
|
-
return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
|
1574
|
-
}
|
1575
|
-
|
1576
|
-
static VALUE allocate_snapshot(VALUE klass) {
|
1577
|
-
SnapshotInfo* snapshot_info;
|
1578
|
-
|
1579
|
-
return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
|
1580
|
-
}
|
1581
|
-
|
1582
|
-
static VALUE allocate_isolate(VALUE klass) {
|
1583
|
-
IsolateInfo* isolate_info = new IsolateInfo();
|
1584
|
-
|
1585
|
-
return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
|
1586
|
-
}
|
1587
|
-
|
1588
|
-
static VALUE
|
1589
|
-
rb_heap_stats(VALUE self) {
|
1590
|
-
|
1591
|
-
ContextInfo* context_info;
|
1592
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1593
|
-
Isolate* isolate;
|
1594
|
-
v8::HeapStatistics stats;
|
1595
|
-
|
1596
|
-
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1597
|
-
|
1598
|
-
VALUE rval = rb_hash_new();
|
1599
|
-
|
1600
|
-
if (!isolate) {
|
1601
|
-
|
1602
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("total_physical_size")), ULONG2NUM(0));
|
1603
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size_executable")), ULONG2NUM(0));
|
1604
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size")), ULONG2NUM(0));
|
1605
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("used_heap_size")), ULONG2NUM(0));
|
1606
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("heap_size_limit")), ULONG2NUM(0));
|
1607
|
-
|
1608
|
-
} else {
|
1609
|
-
isolate->GetHeapStatistics(&stats);
|
1610
|
-
|
1611
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("total_physical_size")), ULONG2NUM(stats.total_physical_size()));
|
1612
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size_executable")), ULONG2NUM(stats.total_heap_size_executable()));
|
1613
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size")), ULONG2NUM(stats.total_heap_size()));
|
1614
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("used_heap_size")), ULONG2NUM(stats.used_heap_size()));
|
1615
|
-
rb_hash_aset(rval, ID2SYM(rb_intern("heap_size_limit")), ULONG2NUM(stats.heap_size_limit()));
|
1616
|
-
}
|
1617
|
-
|
1618
|
-
return rval;
|
1619
|
-
}
|
1620
|
-
|
1621
|
-
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1622
|
-
class FileOutputStream : public OutputStream {
|
1623
|
-
public:
|
1624
|
-
int err;
|
1625
|
-
|
1626
|
-
FileOutputStream(int fd) : fd(fd) { err = 0; }
|
1627
|
-
|
1628
|
-
virtual int GetChunkSize() {
|
1629
|
-
return 65536;
|
1630
|
-
}
|
1631
|
-
|
1632
|
-
virtual void EndOfStream() {}
|
1633
|
-
|
1634
|
-
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1635
|
-
size_t len = static_cast<size_t>(size);
|
1636
|
-
|
1637
|
-
while (len) {
|
1638
|
-
ssize_t w = write(fd, data, len);
|
1639
|
-
|
1640
|
-
if (w > 0) {
|
1641
|
-
data += w;
|
1642
|
-
len -= w;
|
1643
|
-
} else if (w < 0) {
|
1644
|
-
err = errno;
|
1645
|
-
return kAbort;
|
1646
|
-
} else { /* w == 0, could be out-of-space */
|
1647
|
-
err = -1;
|
1648
|
-
return kAbort;
|
1649
|
-
}
|
1650
|
-
}
|
1651
|
-
return kContinue;
|
1652
|
-
}
|
1653
|
-
|
1654
|
-
private:
|
1655
|
-
int fd;
|
1656
|
-
};
|
1657
|
-
|
1658
|
-
|
1659
|
-
static VALUE
|
1660
|
-
rb_heap_snapshot(VALUE self, VALUE file) {
|
1661
|
-
|
1662
|
-
rb_io_t *fptr;
|
1663
|
-
|
1664
|
-
fptr = RFILE(file)->fptr;
|
1665
|
-
|
1666
|
-
if (!fptr) return Qfalse;
|
1667
|
-
|
1668
|
-
// prepare for unbuffered write(2) below:
|
1669
|
-
rb_funcall(file, rb_intern("flush"), 0);
|
1670
|
-
|
1671
|
-
ContextInfo* context_info;
|
1672
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1673
|
-
Isolate* isolate;
|
1674
|
-
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1675
|
-
|
1676
|
-
if (!isolate) return Qfalse;
|
1677
|
-
|
1678
|
-
Locker lock(isolate);
|
1679
|
-
Isolate::Scope isolate_scope(isolate);
|
1680
|
-
HandleScope handle_scope(isolate);
|
1681
|
-
|
1682
|
-
HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
1683
|
-
|
1684
|
-
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1685
|
-
|
1686
|
-
FileOutputStream stream(fptr->fd);
|
1687
|
-
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1688
|
-
|
1689
|
-
const_cast<HeapSnapshot*>(snap)->Delete();
|
1690
|
-
|
1691
|
-
/* TODO: perhaps rb_sys_fail here */
|
1692
|
-
if (stream.err) return Qfalse;
|
1693
|
-
|
1694
|
-
return Qtrue;
|
1695
|
-
}
|
1696
|
-
|
1697
|
-
static VALUE
|
1698
|
-
rb_context_stop(VALUE self) {
|
1699
|
-
|
1700
|
-
ContextInfo* context_info;
|
1701
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1702
|
-
|
1703
|
-
Isolate* isolate = context_info->isolate_info->isolate;
|
1704
|
-
|
1705
|
-
// flag for termination
|
1706
|
-
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1707
|
-
|
1708
|
-
isolate->TerminateExecution();
|
1709
|
-
rb_funcall(self, rb_intern("stop_attached"), 0);
|
1710
|
-
|
1711
|
-
return Qnil;
|
1712
|
-
}
|
1713
|
-
|
1714
|
-
static VALUE
|
1715
|
-
rb_context_dispose(VALUE self) {
|
1716
|
-
|
1717
|
-
ContextInfo* context_info;
|
1718
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1719
|
-
|
1720
|
-
free_context(context_info);
|
1721
|
-
|
1722
|
-
return Qnil;
|
1723
|
-
}
|
1724
|
-
|
1725
|
-
static void*
|
1726
|
-
nogvl_context_call(void *args) {
|
1727
|
-
|
1728
|
-
FunctionCall *call = (FunctionCall *) args;
|
1729
|
-
if (!call) {
|
1730
|
-
return NULL;
|
1731
|
-
}
|
1732
|
-
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1733
|
-
Isolate* isolate = isolate_info->isolate;
|
1734
|
-
|
1735
|
-
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1736
|
-
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1737
|
-
|
1738
|
-
if (call->max_memory > 0) {
|
1739
|
-
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1740
|
-
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1741
|
-
if (!isolate_info->added_gc_cb) {
|
1742
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
1743
|
-
isolate_info->added_gc_cb = true;
|
1744
|
-
}
|
1745
|
-
}
|
1746
|
-
|
1747
|
-
if (call->marshal_stackdepth > 0) {
|
1748
|
-
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1749
|
-
}
|
1750
|
-
|
1751
|
-
Isolate::Scope isolate_scope(isolate);
|
1752
|
-
EscapableHandleScope handle_scope(isolate);
|
1753
|
-
TryCatch trycatch(isolate);
|
1754
|
-
|
1755
|
-
Local<Context> context = call->context_info->context->Get(isolate);
|
1756
|
-
Context::Scope context_scope(context);
|
1757
|
-
|
1758
|
-
Local<Function> fun = call->fun;
|
1759
|
-
|
1760
|
-
EvalResult& eval_res = call->result;
|
1761
|
-
eval_res.parsed = true;
|
1762
|
-
|
1763
|
-
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1764
|
-
prepare_result(res, trycatch, isolate, context, eval_res);
|
1765
|
-
|
1766
|
-
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1767
|
-
|
1768
|
-
return NULL;
|
1769
|
-
}
|
1770
|
-
|
1771
|
-
static void unblock_function(void *args) {
|
1772
|
-
FunctionCall *call = (FunctionCall *) args;
|
1773
|
-
call->context_info->isolate_info->interrupted = true;
|
1774
|
-
}
|
1775
|
-
|
1776
|
-
static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
1777
|
-
ContextInfo* context_info;
|
1778
|
-
FunctionCall call;
|
1779
|
-
VALUE *call_argv = NULL;
|
1780
|
-
|
1781
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1782
|
-
Isolate* isolate = context_info->isolate_info->isolate;
|
1783
|
-
|
1784
|
-
if (argc < 1) {
|
1785
|
-
rb_raise(rb_eArgError, "need at least one argument %d", argc);
|
1786
|
-
}
|
1787
|
-
|
1788
|
-
VALUE function_name = argv[0];
|
1789
|
-
Check_Type(function_name, T_STRING);
|
1790
|
-
|
1791
|
-
char *fname = RSTRING_PTR(function_name);
|
1792
|
-
if (!fname) {
|
1793
|
-
return Qnil;
|
1794
|
-
}
|
1795
|
-
|
1796
|
-
call.context_info = context_info;
|
1797
|
-
call.error = false;
|
1798
|
-
call.function_name = fname;
|
1799
|
-
call.argc = argc - 1;
|
1800
|
-
call.argv = NULL;
|
1801
|
-
if (call.argc > 0) {
|
1802
|
-
// skip first argument which is the function name
|
1803
|
-
call_argv = argv + 1;
|
1804
|
-
}
|
1805
|
-
|
1806
|
-
call.max_memory = 0;
|
1807
|
-
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1808
|
-
if (mem_softlimit != Qnil) {
|
1809
|
-
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1810
|
-
call.max_memory = (size_t)sl_int;
|
1811
|
-
}
|
1812
|
-
|
1813
|
-
call.marshal_stackdepth = 0;
|
1814
|
-
VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
|
1815
|
-
if (marshal_stackdepth != Qnil) {
|
1816
|
-
unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
|
1817
|
-
call.marshal_stackdepth = (size_t)sl_int;
|
1818
|
-
}
|
1819
|
-
|
1820
|
-
bool missingFunction = false;
|
1821
|
-
{
|
1822
|
-
Locker lock(isolate);
|
1823
|
-
Isolate::Scope isolate_scope(isolate);
|
1824
|
-
HandleScope handle_scope(isolate);
|
1825
|
-
|
1826
|
-
Local<Context> context = context_info->context->Get(isolate);
|
1827
|
-
Context::Scope context_scope(context);
|
1828
|
-
|
1829
|
-
// examples of such usage can be found in
|
1830
|
-
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1831
|
-
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
1832
|
-
MaybeLocal<v8::Value> val;
|
1833
|
-
if (!fname.IsEmpty()) {
|
1834
|
-
val = context->Global()->Get(context, fname.ToLocalChecked());
|
1835
|
-
}
|
1836
|
-
|
1837
|
-
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1838
|
-
missingFunction = true;
|
1839
|
-
} else {
|
1840
|
-
Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
|
1841
|
-
VALUE tmp;
|
1842
|
-
call.fun = fun;
|
1843
|
-
call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
|
1844
|
-
for(int i=0; i < call.argc; i++) {
|
1845
|
-
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1846
|
-
}
|
1847
|
-
rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
|
1848
|
-
RB_ALLOCV_END(tmp);
|
1849
|
-
}
|
1850
|
-
}
|
1851
|
-
|
1852
|
-
if (missingFunction) {
|
1853
|
-
rb_raise(rb_eScriptRuntimeError, "Unknown JavaScript method invoked");
|
1854
|
-
}
|
1855
|
-
|
1856
|
-
return convert_result_to_ruby(self, call.result);
|
1857
|
-
}
|
1858
|
-
|
1859
|
-
static VALUE rb_context_create_isolate_value(VALUE self) {
|
1860
|
-
ContextInfo* context_info;
|
1861
|
-
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1862
|
-
IsolateInfo *isolate_info = context_info->isolate_info;
|
1863
|
-
|
1864
|
-
if (!isolate_info) {
|
1865
|
-
return Qnil;
|
1866
|
-
}
|
1867
|
-
|
1868
|
-
isolate_info->hold();
|
1869
|
-
return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
|
1870
|
-
}
|
1871
|
-
|
1872
|
-
static void set_ruby_exiting(VALUE value) {
|
1873
|
-
(void)value;
|
1874
|
-
|
1875
|
-
ruby_exiting.store(1);
|
1876
|
-
}
|
1877
|
-
|
1878
|
-
extern "C" {
|
1879
|
-
|
1880
|
-
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
1881
|
-
{
|
1882
|
-
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
1883
|
-
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1884
|
-
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1885
|
-
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
1886
|
-
VALUE rb_cPlatform = rb_define_class_under(rb_mMiniRacer, "Platform", rb_cObject);
|
1887
|
-
|
1888
|
-
VALUE rb_eError = rb_define_class_under(rb_mMiniRacer, "Error", rb_eStandardError);
|
1889
|
-
|
1890
|
-
VALUE rb_eEvalError = rb_define_class_under(rb_mMiniRacer, "EvalError", rb_eError);
|
1891
|
-
rb_eScriptTerminatedError = rb_define_class_under(rb_mMiniRacer, "ScriptTerminatedError", rb_eEvalError);
|
1892
|
-
rb_eV8OutOfMemoryError = rb_define_class_under(rb_mMiniRacer, "V8OutOfMemoryError", rb_eEvalError);
|
1893
|
-
rb_eParseError = rb_define_class_under(rb_mMiniRacer, "ParseError", rb_eEvalError);
|
1894
|
-
rb_eScriptRuntimeError = rb_define_class_under(rb_mMiniRacer, "RuntimeError", rb_eEvalError);
|
1895
|
-
|
1896
|
-
rb_cJavaScriptFunction = rb_define_class_under(rb_mMiniRacer, "JavaScriptFunction", rb_cObject);
|
1897
|
-
rb_eSnapshotError = rb_define_class_under(rb_mMiniRacer, "SnapshotError", rb_eError);
|
1898
|
-
rb_ePlatformAlreadyInitializedError = rb_define_class_under(rb_mMiniRacer, "PlatformAlreadyInitialized", rb_eError);
|
1899
|
-
rb_cFailedV8Conversion = rb_define_class_under(rb_mMiniRacer, "FailedV8Conversion", rb_cObject);
|
1900
|
-
rb_mJSON = rb_define_module("JSON");
|
1901
|
-
|
1902
|
-
VALUE rb_cExternalFunction = rb_define_class_under(rb_cContext, "ExternalFunction", rb_cObject);
|
1903
|
-
|
1904
|
-
rb_define_method(rb_cContext, "stop", (VALUE(*)(...))&rb_context_stop, 0);
|
1905
|
-
rb_define_method(rb_cContext, "dispose_unsafe", (VALUE(*)(...))&rb_context_dispose, 0);
|
1906
|
-
rb_define_method(rb_cContext, "heap_stats", (VALUE(*)(...))&rb_heap_stats, 0);
|
1907
|
-
rb_define_method(rb_cContext, "write_heap_snapshot_unsafe", (VALUE(*)(...))&rb_heap_snapshot, 1);
|
1908
|
-
|
1909
|
-
rb_define_private_method(rb_cContext, "create_isolate_value",(VALUE(*)(...))&rb_context_create_isolate_value, 0);
|
1910
|
-
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
|
1911
|
-
rb_define_private_method(rb_cContext, "call_unsafe", (VALUE(*)(...))&rb_context_call_unsafe, -1);
|
1912
|
-
rb_define_private_method(rb_cContext, "isolate_mutex", (VALUE(*)(...))&rb_context_isolate_mutex, 0);
|
1913
|
-
rb_define_private_method(rb_cContext, "init_unsafe",(VALUE(*)(...))&rb_context_init_unsafe, 2);
|
1914
|
-
|
1915
|
-
rb_define_alloc_func(rb_cContext, allocate);
|
1916
|
-
rb_define_alloc_func(rb_cSnapshot, allocate_snapshot);
|
1917
|
-
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
1918
|
-
|
1919
|
-
rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
|
1920
|
-
|
1921
|
-
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
1922
|
-
rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
|
1923
|
-
rb_define_method(rb_cSnapshot, "warmup_unsafe!", (VALUE(*)(...))&rb_snapshot_warmup_unsafe, 1);
|
1924
|
-
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1925
|
-
|
1926
|
-
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1927
|
-
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1928
|
-
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1929
|
-
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1930
|
-
|
1931
|
-
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
1932
|
-
|
1933
|
-
rb_set_end_proc(set_ruby_exiting, Qnil);
|
1934
|
-
|
1935
|
-
static pthread_attr_t attr;
|
1936
|
-
if (pthread_attr_init(&attr) == 0) {
|
1937
|
-
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
1938
|
-
thread_attr_p = &attr;
|
1939
|
-
}
|
1940
|
-
}
|
1941
|
-
}
|
1942
|
-
}
|