mini_racer 0.4.0.beta1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -2
- data/.gitignore +1 -0
- data/.travis.yml +0 -8
- data/CHANGELOG +23 -0
- data/README.md +16 -3
- data/Rakefile +1 -1
- data/ext/mini_racer_extension/extconf.rb +20 -13
- data/ext/mini_racer_extension/mini_racer_extension.cc +327 -117
- data/ext/mini_racer_loader/extconf.rb +1 -1
- data/lib/mini_racer/version.rb +2 -2
- data/lib/mini_racer.rb +15 -6
- data/mini_racer.gemspec +1 -3
- metadata +12 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 179c0a63082cf1ec6bc7f8ead94fbcba3c41255ee91b1639482570d1ec3f7646
|
4
|
+
data.tar.gz: cdef6f06a6ab72060aa43b21518c6f0103bf80c034bee09b7cf1d6f215620c31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0efd1873f139141ac489ed24ebcad3e289092ea67ea7823d0b94fe07a3acfcd602ed64c287b39b556d370db5c17b094d38a9c5ff06f31f546dddda7694d01c81
|
7
|
+
data.tar.gz: 1ccc24b53320211be2b3cd887ac1193ee0654e0b54c0d2b3da636a382ce00c95d4af3ba5acd23a931436d08e0707dc0b4d348b6bbd0863cb56df9fb2fbd2678e
|
data/.github/workflows/ci.yml
CHANGED
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
language: ruby
|
2
2
|
os: linux
|
3
3
|
rvm:
|
4
|
-
- 2.4
|
5
|
-
- 2.5
|
6
4
|
- 2.6
|
7
5
|
- 2.7
|
8
6
|
- 3.0
|
@@ -12,9 +10,6 @@ arch:
|
|
12
10
|
- arm64
|
13
11
|
jobs:
|
14
12
|
include:
|
15
|
-
- rvm: 2.5
|
16
|
-
os: osx
|
17
|
-
osx_image: xcode9.4
|
18
13
|
- rvm: 2.6
|
19
14
|
os: osx
|
20
15
|
osx_image: xcode11.3
|
@@ -25,7 +20,4 @@ jobs:
|
|
25
20
|
os: osx
|
26
21
|
osx_image: xcode12.2
|
27
22
|
dist: xenial
|
28
|
-
before_install:
|
29
|
-
- gem update --system
|
30
|
-
- gem install bundler -v 1.16.2
|
31
23
|
cache: bundler
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
- 31-12-2021
|
2
|
+
|
3
|
+
- 0.6.0
|
4
|
+
|
5
|
+
- Ruby 3.1 support
|
6
|
+
- Fixes memory leak in heap snapshotting
|
7
|
+
- Improved compilation ergonomics in clang
|
8
|
+
- Migrated internal storage in c extension to TypedData
|
9
|
+
|
10
|
+
- 11-04-2021
|
11
|
+
|
12
|
+
- 0.5.0
|
13
|
+
- Fixes issues on aarch (Apple M1)
|
14
|
+
- Update to use libv8-node 16.x (#210) [Loic Nageleisen]
|
15
|
+
- FEATURE: Configurable max marshal stack depth (#202) [seanmakesgames]
|
16
|
+
- FEATURE: Configurable max marshal stack depth (#202) [seanmakesgames]
|
17
|
+
- Ruby 2.3 and 2.4 are EOL, we no longer support them
|
18
|
+
|
19
|
+
- 0.4.0
|
20
|
+
|
21
|
+
- FEATURE: upgrade to libv8 node 15.14.0 (v8 8.6.395.17)
|
22
|
+
- Promote 0.4.0.beta1 to release, using libv8-node release path
|
23
|
+
|
1
24
|
- 08-04-2021
|
2
25
|
|
3
26
|
- 0.4.0.beta1
|
data/README.md
CHANGED
@@ -85,6 +85,19 @@ context.eval 'var a = new Array(10000); while(true) {a = a.concat(new Array(1000
|
|
85
85
|
# => V8OutOfMemoryError is raised
|
86
86
|
```
|
87
87
|
|
88
|
+
### Object marshal max stackdepth support
|
89
|
+
|
90
|
+
Contexts can specify a stack depth limit for object marshalling. Max depth is unbounded by default.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
# terminates script if stack depth exceeds max during marshal
|
94
|
+
context = MiniRacer::Context.new(marshal_stack_depth: 500)
|
95
|
+
context.attach("a", proc{|a| a})
|
96
|
+
|
97
|
+
context.eval("let arr = []; arr.push(arr); a(arr)")
|
98
|
+
# => RuntimeError is raised
|
99
|
+
```
|
100
|
+
|
88
101
|
### Rich debugging with "filename" support
|
89
102
|
|
90
103
|
```ruby
|
@@ -403,14 +416,14 @@ Or install it yourself as:
|
|
403
416
|
$ gem install mini_racer
|
404
417
|
|
405
418
|
|
406
|
-
**Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or
|
419
|
+
**Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or GCC 6.3 (or later).
|
407
420
|
|
408
421
|
|
409
422
|
## Travis-ci
|
410
423
|
|
411
|
-
To install `mini-racer` you will need a version of
|
424
|
+
To install `mini-racer` you will need a version of GCC that supports C++11 (GCC 6.3) this is included by default in ubuntu trusty based images.
|
412
425
|
|
413
|
-
Travis today ships by default with a precise based image. Precise Pangolin (12.04 LTS) was first released in August 2012. Even though you can install GCC
|
426
|
+
Travis today ships by default with a precise based image. Precise Pangolin (12.04 LTS) was first released in August 2012. Even though you can install GCC 6.3 on precise the simpler approach is to opt for the trusty based image.
|
414
427
|
|
415
428
|
Add this to your .travis.yml file:
|
416
429
|
|
data/Rakefile
CHANGED
@@ -92,5 +92,5 @@ task :lint do
|
|
92
92
|
portability-*
|
93
93
|
readability-*).join(',')
|
94
94
|
|
95
|
-
sh RbConfig::expand("clang-tidy -checks='#{checks}' ext/mini_racer_extension/mini_racer_extension.cc -- #$INCFLAGS #$
|
95
|
+
sh RbConfig::expand("clang-tidy -checks='#{checks}' ext/mini_racer_extension/mini_racer_extension.cc -- #$INCFLAGS #$CXXFLAGS", conf)
|
96
96
|
end
|
@@ -7,18 +7,25 @@ IS_DARWIN = RUBY_PLATFORM =~ /darwin/
|
|
7
7
|
|
8
8
|
have_library('pthread')
|
9
9
|
have_library('objc') if IS_DARWIN
|
10
|
-
$
|
11
|
-
$
|
12
|
-
$
|
13
|
-
$
|
14
|
-
$
|
15
|
-
$
|
16
|
-
|
17
|
-
$
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
$CXXFLAGS += " -Wall" unless $CXXFLAGS.split.include? "-Wall"
|
11
|
+
$CXXFLAGS += " -g" unless $CXXFLAGS.split.include? "-g"
|
12
|
+
$CXXFLAGS += " -rdynamic" unless $CXXFLAGS.split.include? "-rdynamic"
|
13
|
+
$CXXFLAGS += " -fPIC" unless $CXXFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
14
|
+
$CXXFLAGS += " -std=c++14"
|
15
|
+
$CXXFLAGS += " -fpermissive"
|
16
|
+
#$CXXFLAGS += " -DV8_COMPRESS_POINTERS"
|
17
|
+
$CXXFLAGS += " -fvisibility=hidden "
|
18
|
+
|
19
|
+
# __declspec gets used by clang via ruby 3.x headers...
|
20
|
+
$CXXFLAGS += " -fms-extensions"
|
21
|
+
|
22
|
+
$CXXFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
23
|
+
|
24
|
+
if IS_DARWIN
|
25
|
+
$LDFLAGS.insert(0, " -stdlib=libc++ ")
|
26
|
+
else
|
27
|
+
$LDFLAGS.insert(0, " -lstdc++ ")
|
28
|
+
end
|
22
29
|
|
23
30
|
# check for missing symbols at link time
|
24
31
|
# $LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
|
@@ -64,7 +71,7 @@ end
|
|
64
71
|
Libv8::Node.configure_makefile
|
65
72
|
|
66
73
|
if enable_config('asan')
|
67
|
-
$
|
74
|
+
$CXXFLAGS.insert(0, " -fsanitize=address ")
|
68
75
|
$LDFLAGS.insert(0, " -fsanitize=address ")
|
69
76
|
end
|
70
77
|
|
@@ -11,6 +11,7 @@
|
|
11
11
|
#include <mutex>
|
12
12
|
#include <atomic>
|
13
13
|
#include <math.h>
|
14
|
+
#include <errno.h>
|
14
15
|
|
15
16
|
using namespace v8;
|
16
17
|
|
@@ -115,6 +116,7 @@ typedef struct {
|
|
115
116
|
useconds_t timeout;
|
116
117
|
EvalResult* result;
|
117
118
|
size_t max_memory;
|
119
|
+
size_t marshal_stackdepth;
|
118
120
|
} EvalParams;
|
119
121
|
|
120
122
|
typedef struct {
|
@@ -126,13 +128,152 @@ typedef struct {
|
|
126
128
|
Local<Value> *argv;
|
127
129
|
EvalResult result;
|
128
130
|
size_t max_memory;
|
131
|
+
size_t marshal_stackdepth;
|
129
132
|
} FunctionCall;
|
130
133
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
134
|
+
class IsolateData {
|
135
|
+
public:
|
136
|
+
enum Flag {
|
137
|
+
// first flags are bitfield
|
138
|
+
// max count: sizeof(uintptr_t) * 8
|
139
|
+
IN_GVL, // whether we are inside of ruby gvl or not
|
140
|
+
DO_TERMINATE, // terminate as soon as possible
|
141
|
+
MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit
|
142
|
+
MEM_SOFTLIMIT_MAX, // maximum memory value
|
143
|
+
MARSHAL_STACKDEPTH_REACHED, // we've hit our max stack depth
|
144
|
+
MARSHAL_STACKDEPTH_VALUE, // current stackdepth
|
145
|
+
MARSHAL_STACKDEPTH_MAX, // maximum stack depth during marshal
|
146
|
+
};
|
147
|
+
|
148
|
+
static void Init(Isolate *isolate) {
|
149
|
+
// zero out all fields in the bitfield
|
150
|
+
isolate->SetData(0, 0);
|
151
|
+
}
|
152
|
+
|
153
|
+
static uintptr_t Get(Isolate *isolate, Flag flag) {
|
154
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
155
|
+
switch (flag) {
|
156
|
+
case IN_GVL: return u.IN_GVL;
|
157
|
+
case DO_TERMINATE: return u.DO_TERMINATE;
|
158
|
+
case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED;
|
159
|
+
case MEM_SOFTLIMIT_MAX: return static_cast<uintptr_t>(u.MEM_SOFTLIMIT_MAX) << 10;
|
160
|
+
case MARSHAL_STACKDEPTH_REACHED: return u.MARSHAL_STACKDEPTH_REACHED;
|
161
|
+
case MARSHAL_STACKDEPTH_VALUE: return u.MARSHAL_STACKDEPTH_VALUE;
|
162
|
+
case MARSHAL_STACKDEPTH_MAX: return u.MARSHAL_STACKDEPTH_MAX;
|
163
|
+
}
|
164
|
+
|
165
|
+
// avoid compiler warning
|
166
|
+
return u.IN_GVL;
|
167
|
+
}
|
168
|
+
|
169
|
+
static void Set(Isolate *isolate, Flag flag, uintptr_t value) {
|
170
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
171
|
+
switch (flag) {
|
172
|
+
case IN_GVL: u.IN_GVL = value; break;
|
173
|
+
case DO_TERMINATE: u.DO_TERMINATE = value; break;
|
174
|
+
case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break;
|
175
|
+
// drop least significant 10 bits 'store memory amount in kb'
|
176
|
+
case MEM_SOFTLIMIT_MAX: u.MEM_SOFTLIMIT_MAX = value >> 10; break;
|
177
|
+
case MARSHAL_STACKDEPTH_REACHED: u.MARSHAL_STACKDEPTH_REACHED = value; break;
|
178
|
+
case MARSHAL_STACKDEPTH_VALUE: u.MARSHAL_STACKDEPTH_VALUE = value; break;
|
179
|
+
case MARSHAL_STACKDEPTH_MAX: u.MARSHAL_STACKDEPTH_MAX = value; break;
|
180
|
+
}
|
181
|
+
isolate->SetData(0, reinterpret_cast<void*>(u.dataPtr));
|
182
|
+
}
|
183
|
+
|
184
|
+
private:
|
185
|
+
struct Bitfield {
|
186
|
+
// WARNING: this would explode on platforms below 64 bit ptrs
|
187
|
+
// compiler will fail here, making it clear for them.
|
188
|
+
// Additionally, using the other part of the union to reinterpret the
|
189
|
+
// memory is undefined behavior according to spec, but is / has been stable
|
190
|
+
// across major compilers for decades.
|
191
|
+
static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit.");
|
192
|
+
union {
|
193
|
+
uint64_t dataPtr: 64;
|
194
|
+
// order in this struct matters. For cpu performance keep larger subobjects
|
195
|
+
// aligned on their boundaries (8 16 32), try not to straddle
|
196
|
+
struct {
|
197
|
+
size_t MEM_SOFTLIMIT_MAX:22;
|
198
|
+
bool IN_GVL:1;
|
199
|
+
bool DO_TERMINATE:1;
|
200
|
+
bool MEM_SOFTLIMIT_REACHED:1;
|
201
|
+
bool MARSHAL_STACKDEPTH_REACHED:1;
|
202
|
+
uint8_t :0; // align to next 8bit bound
|
203
|
+
size_t MARSHAL_STACKDEPTH_VALUE:10;
|
204
|
+
uint8_t :0; // align to next 8bit bound
|
205
|
+
size_t MARSHAL_STACKDEPTH_MAX:10;
|
206
|
+
};
|
207
|
+
};
|
208
|
+
};
|
209
|
+
};
|
210
|
+
|
211
|
+
struct StackCounter {
|
212
|
+
static void Reset(Isolate* isolate) {
|
213
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0) {
|
214
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
215
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
static void SetMax(Isolate* isolate, size_t marshalMaxStackDepth) {
|
220
|
+
if (marshalMaxStackDepth > 0) {
|
221
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX, marshalMaxStackDepth);
|
222
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
223
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
StackCounter(Isolate* isolate) {
|
228
|
+
this->isActive = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0;
|
229
|
+
|
230
|
+
if (this->isActive) {
|
231
|
+
this->isolate = isolate;
|
232
|
+
this->IncDepth(1);
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
bool IsTooDeep() {
|
237
|
+
if (!this->IsActive()) {
|
238
|
+
return false;
|
239
|
+
}
|
240
|
+
|
241
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
242
|
+
size_t maxDepth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_MAX);
|
243
|
+
if (depth > maxDepth) {
|
244
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, true);
|
245
|
+
return true;
|
246
|
+
}
|
247
|
+
|
248
|
+
return false;
|
249
|
+
}
|
250
|
+
|
251
|
+
bool IsActive() {
|
252
|
+
return this->isActive && !IsolateData::Get(this->isolate, IsolateData::DO_TERMINATE);
|
253
|
+
}
|
254
|
+
|
255
|
+
~StackCounter() {
|
256
|
+
if (this->IsActive()) {
|
257
|
+
this->IncDepth(-1);
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
private:
|
262
|
+
Isolate* isolate;
|
263
|
+
bool isActive;
|
264
|
+
|
265
|
+
void IncDepth(int direction) {
|
266
|
+
int inc = direction > 0 ? 1 : -1;
|
267
|
+
|
268
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
269
|
+
|
270
|
+
// don't decrement past 0
|
271
|
+
if (inc > 0 || depth > 0) {
|
272
|
+
depth += inc;
|
273
|
+
}
|
274
|
+
|
275
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, depth);
|
276
|
+
}
|
136
277
|
};
|
137
278
|
|
138
279
|
static VALUE rb_cContext;
|
@@ -159,6 +300,29 @@ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
|
159
300
|
static bool ruby_exiting = false; // guarded by exit_lock
|
160
301
|
static bool single_threaded = false;
|
161
302
|
|
303
|
+
static void mark_context(void *);
|
304
|
+
static void deallocate(void *);
|
305
|
+
static size_t context_memsize(const void *);
|
306
|
+
static const rb_data_type_t context_type = {
|
307
|
+
"mini_racer/context_info",
|
308
|
+
{ mark_context, deallocate, context_memsize }
|
309
|
+
};
|
310
|
+
|
311
|
+
static void deallocate_snapshot(void *);
|
312
|
+
static size_t snapshot_memsize(const void *);
|
313
|
+
static const rb_data_type_t snapshot_type = {
|
314
|
+
"mini_racer/snapshot_info",
|
315
|
+
{ NULL, deallocate_snapshot, snapshot_memsize }
|
316
|
+
};
|
317
|
+
|
318
|
+
static void mark_isolate(void *);
|
319
|
+
static void deallocate_isolate(void *);
|
320
|
+
static size_t isolate_memsize(const void *);
|
321
|
+
static const rb_data_type_t isolate_type = {
|
322
|
+
"mini_racer/isolate_info",
|
323
|
+
{ mark_isolate, deallocate_isolate, isolate_memsize }
|
324
|
+
};
|
325
|
+
|
162
326
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
163
327
|
bool platform_already_initialized = false;
|
164
328
|
|
@@ -205,16 +369,18 @@ static void init_v8() {
|
|
205
369
|
}
|
206
370
|
|
207
371
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
208
|
-
if((
|
372
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
373
|
+
return;
|
374
|
+
}
|
209
375
|
|
210
|
-
size_t softlimit =
|
376
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
211
377
|
|
212
378
|
HeapStatistics stats;
|
213
379
|
isolate->GetHeapStatistics(&stats);
|
214
380
|
size_t used = stats.used_heap_size();
|
215
381
|
|
216
382
|
if(used > softlimit) {
|
217
|
-
isolate
|
383
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
218
384
|
isolate->TerminateExecution();
|
219
385
|
}
|
220
386
|
}
|
@@ -311,7 +477,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
311
477
|
}
|
312
478
|
}
|
313
479
|
|
314
|
-
void*
|
480
|
+
static void*
|
315
481
|
nogvl_context_eval(void* arg) {
|
316
482
|
|
317
483
|
EvalParams* eval_params = (EvalParams*)arg;
|
@@ -326,14 +492,15 @@ nogvl_context_eval(void* arg) {
|
|
326
492
|
Context::Scope context_scope(context);
|
327
493
|
v8::ScriptOrigin *origin = NULL;
|
328
494
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
495
|
+
IsolateData::Init(isolate);
|
496
|
+
|
497
|
+
if (eval_params->max_memory > 0) {
|
498
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
|
499
|
+
if (!isolate_info->added_gc_cb) {
|
500
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
501
|
+
isolate_info->added_gc_cb = true;
|
502
|
+
}
|
503
|
+
}
|
337
504
|
|
338
505
|
MaybeLocal<Script> parsed_script;
|
339
506
|
|
@@ -359,12 +526,8 @@ nogvl_context_eval(void* arg) {
|
|
359
526
|
result->message->Reset(isolate, trycatch.Exception());
|
360
527
|
} else {
|
361
528
|
// parsing successful
|
362
|
-
if (eval_params->
|
363
|
-
isolate
|
364
|
-
if (!isolate_info->added_gc_cb) {
|
365
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
366
|
-
isolate_info->added_gc_cb = true;
|
367
|
-
}
|
529
|
+
if (eval_params->marshal_stackdepth > 0) {
|
530
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
368
531
|
}
|
369
532
|
|
370
533
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -372,7 +535,7 @@ nogvl_context_eval(void* arg) {
|
|
372
535
|
|
373
536
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
374
537
|
|
375
|
-
isolate
|
538
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
376
539
|
|
377
540
|
return NULL;
|
378
541
|
}
|
@@ -390,6 +553,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
390
553
|
Isolate::Scope isolate_scope(isolate);
|
391
554
|
HandleScope scope(isolate);
|
392
555
|
|
556
|
+
StackCounter stackCounter(isolate);
|
557
|
+
|
558
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
|
559
|
+
return Qnil;
|
560
|
+
}
|
561
|
+
|
562
|
+
if (stackCounter.IsTooDeep()) {
|
563
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
564
|
+
isolate->TerminateExecution();
|
565
|
+
return Qnil;
|
566
|
+
}
|
567
|
+
|
393
568
|
if (value->IsNull() || value->IsUndefined()){
|
394
569
|
return Qnil;
|
395
570
|
}
|
@@ -478,7 +653,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
478
653
|
rb_enc_find("utf-8")
|
479
654
|
);
|
480
655
|
|
481
|
-
return
|
656
|
+
return rb_str_intern(str_symbol);
|
482
657
|
}
|
483
658
|
|
484
659
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
@@ -543,7 +718,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
543
718
|
length = RARRAY_LEN(value);
|
544
719
|
array = Array::New(isolate, (int)length);
|
545
720
|
for(i=0; i<length; i++) {
|
546
|
-
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
721
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
722
|
+
(void)(success);
|
547
723
|
}
|
548
724
|
return scope.Escape(array);
|
549
725
|
case T_HASH:
|
@@ -552,8 +728,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
552
728
|
length = RARRAY_LEN(hash_as_array);
|
553
729
|
for(i=0; i<length; i++) {
|
554
730
|
pair = rb_ary_entry(hash_as_array, i);
|
555
|
-
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
731
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
556
732
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
733
|
+
(void)(success);
|
557
734
|
}
|
558
735
|
return scope.Escape(object);
|
559
736
|
case T_SYMBOL:
|
@@ -633,6 +810,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
|
633
810
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
634
811
|
}
|
635
812
|
|
813
|
+
static
|
636
814
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
637
815
|
const char *warmup_source) {
|
638
816
|
// Use following steps to create a warmed up snapshot blob from a cold one:
|
@@ -668,14 +846,14 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
668
846
|
|
669
847
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
670
848
|
SnapshotInfo* snapshot_info;
|
671
|
-
|
849
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
672
850
|
|
673
851
|
return INT2NUM(snapshot_info->raw_size);
|
674
852
|
}
|
675
853
|
|
676
854
|
static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
677
855
|
SnapshotInfo* snapshot_info;
|
678
|
-
|
856
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
679
857
|
|
680
858
|
if(TYPE(str) != T_STRING) {
|
681
859
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
@@ -698,14 +876,14 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
698
876
|
|
699
877
|
static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
|
700
878
|
SnapshotInfo* snapshot_info;
|
701
|
-
|
879
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
702
880
|
|
703
881
|
return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
|
704
882
|
}
|
705
883
|
|
706
884
|
static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
707
885
|
SnapshotInfo* snapshot_info;
|
708
|
-
|
886
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
709
887
|
|
710
888
|
if(TYPE(str) != T_STRING) {
|
711
889
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
@@ -752,13 +930,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
|
|
752
930
|
|
753
931
|
static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
754
932
|
IsolateInfo* isolate_info;
|
755
|
-
|
933
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
756
934
|
|
757
935
|
init_v8();
|
758
936
|
|
759
937
|
SnapshotInfo* snapshot_info = nullptr;
|
760
938
|
if (!NIL_P(snapshot)) {
|
761
|
-
|
939
|
+
TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
|
762
940
|
}
|
763
941
|
|
764
942
|
isolate_info->init(snapshot_info);
|
@@ -769,7 +947,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
|
769
947
|
|
770
948
|
static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
771
949
|
IsolateInfo* isolate_info;
|
772
|
-
|
950
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
773
951
|
|
774
952
|
if (current_platform == NULL) return Qfalse;
|
775
953
|
|
@@ -780,7 +958,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
780
958
|
|
781
959
|
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
782
960
|
IsolateInfo* isolate_info;
|
783
|
-
|
961
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
784
962
|
|
785
963
|
if (current_platform == NULL) return Qfalse;
|
786
964
|
|
@@ -790,7 +968,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
|
790
968
|
|
791
969
|
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
792
970
|
IsolateInfo* isolate_info;
|
793
|
-
|
971
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
794
972
|
|
795
973
|
if (current_platform == NULL) return Qfalse;
|
796
974
|
|
@@ -803,7 +981,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
|
803
981
|
|
804
982
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
805
983
|
ContextInfo* context_info;
|
806
|
-
|
984
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
807
985
|
|
808
986
|
init_v8();
|
809
987
|
|
@@ -814,11 +992,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
814
992
|
|
815
993
|
SnapshotInfo *snapshot_info = nullptr;
|
816
994
|
if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
|
817
|
-
|
995
|
+
TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
|
818
996
|
}
|
819
997
|
isolate_info->init(snapshot_info);
|
820
998
|
} else { // given isolate or snapshot
|
821
|
-
|
999
|
+
TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
|
822
1000
|
}
|
823
1001
|
|
824
1002
|
context_info->isolate_info = isolate_info;
|
@@ -848,7 +1026,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
848
1026
|
static VALUE convert_result_to_ruby(VALUE self /* context */,
|
849
1027
|
EvalResult& result) {
|
850
1028
|
ContextInfo *context_info;
|
851
|
-
|
1029
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
852
1030
|
|
853
1031
|
Isolate *isolate = context_info->isolate_info->isolate;
|
854
1032
|
Persistent<Context> *p_ctx = context_info->context;
|
@@ -884,9 +1062,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
884
1062
|
if (!result.executed) {
|
885
1063
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
886
1064
|
if (ruby_exception == Qnil) {
|
887
|
-
bool mem_softlimit_reached = (
|
1065
|
+
bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
|
1066
|
+
bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
|
888
1067
|
// If we were terminated or have the memory softlimit flag set
|
889
|
-
if (
|
1068
|
+
if (marshal_stack_maxdepth_reached) {
|
1069
|
+
ruby_exception = rb_eScriptRuntimeError;
|
1070
|
+
std::string msg = std::string("Marshal object depth too deep. Script terminated.");
|
1071
|
+
message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
|
1072
|
+
} else if (result.terminated || mem_softlimit_reached) {
|
890
1073
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
891
1074
|
} else {
|
892
1075
|
ruby_exception = rb_eScriptRuntimeError;
|
@@ -921,6 +1104,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
921
1104
|
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
922
1105
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
923
1106
|
} else {
|
1107
|
+
StackCounter::Reset(isolate);
|
924
1108
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
925
1109
|
}
|
926
1110
|
|
@@ -943,7 +1127,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
943
1127
|
EvalResult eval_result;
|
944
1128
|
ContextInfo* context_info;
|
945
1129
|
|
946
|
-
|
1130
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
947
1131
|
Isolate* isolate = context_info->isolate_info->isolate;
|
948
1132
|
|
949
1133
|
if(TYPE(str) != T_STRING) {
|
@@ -978,6 +1162,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
978
1162
|
eval_params.result = &eval_result;
|
979
1163
|
eval_params.timeout = 0;
|
980
1164
|
eval_params.max_memory = 0;
|
1165
|
+
eval_params.marshal_stackdepth = 0;
|
981
1166
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
982
1167
|
if (timeout != Qnil) {
|
983
1168
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
@@ -988,6 +1173,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
988
1173
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
989
1174
|
}
|
990
1175
|
|
1176
|
+
VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
|
1177
|
+
if (stack_depth != Qnil) {
|
1178
|
+
eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
|
1179
|
+
}
|
1180
|
+
|
991
1181
|
eval_result.message = NULL;
|
992
1182
|
eval_result.backtrace = NULL;
|
993
1183
|
|
@@ -1025,7 +1215,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
|
|
1025
1215
|
return exception;
|
1026
1216
|
}
|
1027
1217
|
|
1028
|
-
void*
|
1218
|
+
static void*
|
1029
1219
|
gvl_ruby_callback(void* data) {
|
1030
1220
|
|
1031
1221
|
FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
|
@@ -1041,7 +1231,7 @@ gvl_ruby_callback(void* data) {
|
|
1041
1231
|
HandleScope scope(args->GetIsolate());
|
1042
1232
|
Local<External> external = Local<External>::Cast(args->Data());
|
1043
1233
|
|
1044
|
-
self =
|
1234
|
+
self = (VALUE)(external->Value());
|
1045
1235
|
callback = rb_iv_get(self, "@callback");
|
1046
1236
|
|
1047
1237
|
parent = rb_iv_get(self, "@parent");
|
@@ -1049,7 +1239,7 @@ gvl_ruby_callback(void* data) {
|
|
1049
1239
|
return NULL;
|
1050
1240
|
}
|
1051
1241
|
|
1052
|
-
|
1242
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1053
1243
|
|
1054
1244
|
if (length > 0) {
|
1055
1245
|
ruby_args = rb_ary_tmp_new(length);
|
@@ -1057,6 +1247,7 @@ gvl_ruby_callback(void* data) {
|
|
1057
1247
|
|
1058
1248
|
for (int i = 0; i < length; i++) {
|
1059
1249
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1250
|
+
StackCounter::Reset(args->GetIsolate());
|
1060
1251
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1061
1252
|
*context_info->context, value);
|
1062
1253
|
rb_ary_push(ruby_args, tmp);
|
@@ -1070,20 +1261,22 @@ gvl_ruby_callback(void* data) {
|
|
1070
1261
|
callback_data.ruby_args = ruby_args;
|
1071
1262
|
callback_data.failed = false;
|
1072
1263
|
|
1073
|
-
if ((
|
1264
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1074
1265
|
args->GetIsolate()->ThrowException(
|
1075
1266
|
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
1267
|
"Terminated execution during transition from Ruby to JS"));
|
1077
1268
|
args->GetIsolate()->TerminateExecution();
|
1078
1269
|
if (length > 0) {
|
1079
1270
|
rb_ary_clear(ruby_args);
|
1080
|
-
rb_gc_force_recycle(ruby_args);
|
1081
1271
|
}
|
1082
1272
|
return NULL;
|
1083
1273
|
}
|
1084
1274
|
|
1085
|
-
|
1086
|
-
|
1275
|
+
VALUE callback_data_value = (VALUE)&callback_data;
|
1276
|
+
|
1277
|
+
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1278
|
+
result = rb_rescue2(protected_callback, callback_data_value,
|
1279
|
+
rescue_callback, callback_data_value, rb_eException, (VALUE)0);
|
1087
1280
|
|
1088
1281
|
if(callback_data.failed) {
|
1089
1282
|
rb_iv_set(parent, "@current_exception", result);
|
@@ -1097,26 +1290,24 @@ gvl_ruby_callback(void* data) {
|
|
1097
1290
|
|
1098
1291
|
if (length > 0) {
|
1099
1292
|
rb_ary_clear(ruby_args);
|
1100
|
-
rb_gc_force_recycle(ruby_args);
|
1101
1293
|
}
|
1102
1294
|
|
1103
|
-
if ((
|
1104
|
-
|
1105
|
-
isolate->TerminateExecution();
|
1295
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1296
|
+
args->GetIsolate()->TerminateExecution();
|
1106
1297
|
}
|
1107
1298
|
|
1108
1299
|
return NULL;
|
1109
1300
|
}
|
1110
1301
|
|
1111
1302
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1112
|
-
bool has_gvl = (
|
1303
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1113
1304
|
|
1114
1305
|
if(has_gvl) {
|
1115
1306
|
gvl_ruby_callback((void*)&args);
|
1116
1307
|
} else {
|
1117
|
-
args.GetIsolate()
|
1308
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1118
1309
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1119
|
-
args.GetIsolate()
|
1310
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1120
1311
|
}
|
1121
1312
|
}
|
1122
1313
|
|
@@ -1133,7 +1324,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1133
1324
|
bool parse_error = false;
|
1134
1325
|
bool attach_error = false;
|
1135
1326
|
|
1136
|
-
|
1327
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1137
1328
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1138
1329
|
|
1139
1330
|
{
|
@@ -1149,20 +1340,18 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1149
1340
|
NewStringType::kNormal, (int)RSTRING_LEN(name))
|
1150
1341
|
.ToLocalChecked();
|
1151
1342
|
|
1152
|
-
//
|
1153
|
-
|
1154
|
-
|
1155
|
-
*self_copy = self;
|
1156
|
-
|
1157
|
-
Local<Value> external = External::New(isolate, self_copy);
|
1343
|
+
// Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
|
1344
|
+
// not a T_DATA type like most other classes in this file
|
1345
|
+
Local<Value> external = External::New(isolate, (void *)self);
|
1158
1346
|
|
1159
1347
|
if (parent_object == Qnil) {
|
1160
|
-
context->Global()->Set(
|
1348
|
+
Maybe<bool> success = context->Global()->Set(
|
1161
1349
|
context,
|
1162
1350
|
v8_str,
|
1163
1351
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1164
1352
|
->GetFunction(context)
|
1165
1353
|
.ToLocalChecked());
|
1354
|
+
(void)success;
|
1166
1355
|
|
1167
1356
|
} else {
|
1168
1357
|
Local<String> eval =
|
@@ -1182,12 +1371,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1182
1371
|
if (!maybe_value.IsEmpty()) {
|
1183
1372
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1184
1373
|
if (value->IsObject()) {
|
1185
|
-
value.As<Object>()->Set(
|
1374
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
1186
1375
|
context,
|
1187
1376
|
v8_str,
|
1188
1377
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1189
1378
|
->GetFunction(context)
|
1190
1379
|
.ToLocalChecked());
|
1380
|
+
(void)success;
|
1191
1381
|
attach_error = false;
|
1192
1382
|
}
|
1193
1383
|
}
|
@@ -1209,7 +1399,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1209
1399
|
|
1210
1400
|
static VALUE rb_context_isolate_mutex(VALUE self) {
|
1211
1401
|
ContextInfo* context_info;
|
1212
|
-
|
1402
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1213
1403
|
|
1214
1404
|
if (!context_info->isolate_info) {
|
1215
1405
|
rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
|
@@ -1319,6 +1509,11 @@ static void mark_isolate(void* data) {
|
|
1319
1509
|
isolate_info->mark();
|
1320
1510
|
}
|
1321
1511
|
|
1512
|
+
static size_t isolate_memsize(const void *ptr) {
|
1513
|
+
const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
|
1514
|
+
return sizeof(*isolate_info);
|
1515
|
+
}
|
1516
|
+
|
1322
1517
|
static void deallocate(void* data) {
|
1323
1518
|
ContextInfo* context_info = (ContextInfo*)data;
|
1324
1519
|
|
@@ -1327,6 +1522,11 @@ static void deallocate(void* data) {
|
|
1327
1522
|
xfree(data);
|
1328
1523
|
}
|
1329
1524
|
|
1525
|
+
static size_t context_memsize(const void *ptr)
|
1526
|
+
{
|
1527
|
+
return sizeof(ContextInfo);
|
1528
|
+
}
|
1529
|
+
|
1330
1530
|
static void mark_context(void* data) {
|
1331
1531
|
ContextInfo* context_info = (ContextInfo*)data;
|
1332
1532
|
if (context_info->isolate_info) {
|
@@ -1334,48 +1534,39 @@ static void mark_context(void* data) {
|
|
1334
1534
|
}
|
1335
1535
|
}
|
1336
1536
|
|
1337
|
-
static void deallocate_external_function(void * data) {
|
1338
|
-
xfree(data);
|
1339
|
-
}
|
1340
|
-
|
1341
1537
|
static void deallocate_snapshot(void * data) {
|
1342
1538
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1343
1539
|
delete[] snapshot_info->data;
|
1344
1540
|
xfree(snapshot_info);
|
1345
1541
|
}
|
1346
1542
|
|
1347
|
-
static
|
1348
|
-
|
1349
|
-
return
|
1543
|
+
static size_t snapshot_memsize(const void *data) {
|
1544
|
+
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1545
|
+
return sizeof(*snapshot_info) + snapshot_info->raw_size;
|
1350
1546
|
}
|
1351
1547
|
|
1352
1548
|
static VALUE allocate(VALUE klass) {
|
1353
|
-
ContextInfo* context_info
|
1354
|
-
|
1355
|
-
context_info->context = NULL;
|
1356
|
-
|
1357
|
-
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1549
|
+
ContextInfo* context_info;
|
1550
|
+
return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
|
1358
1551
|
}
|
1359
1552
|
|
1360
1553
|
static VALUE allocate_snapshot(VALUE klass) {
|
1361
|
-
SnapshotInfo* snapshot_info
|
1362
|
-
snapshot_info->data = NULL;
|
1363
|
-
snapshot_info->raw_size = 0;
|
1554
|
+
SnapshotInfo* snapshot_info;
|
1364
1555
|
|
1365
|
-
return
|
1556
|
+
return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
|
1366
1557
|
}
|
1367
1558
|
|
1368
1559
|
static VALUE allocate_isolate(VALUE klass) {
|
1369
1560
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1370
1561
|
|
1371
|
-
return
|
1562
|
+
return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
|
1372
1563
|
}
|
1373
1564
|
|
1374
1565
|
static VALUE
|
1375
1566
|
rb_heap_stats(VALUE self) {
|
1376
1567
|
|
1377
1568
|
ContextInfo* context_info;
|
1378
|
-
|
1569
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1379
1570
|
Isolate* isolate;
|
1380
1571
|
v8::HeapStatistics stats;
|
1381
1572
|
|
@@ -1407,7 +1598,9 @@ rb_heap_stats(VALUE self) {
|
|
1407
1598
|
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1408
1599
|
class FileOutputStream : public OutputStream {
|
1409
1600
|
public:
|
1410
|
-
|
1601
|
+
int err;
|
1602
|
+
|
1603
|
+
FileOutputStream(int fd) : fd(fd) { err = 0; }
|
1411
1604
|
|
1412
1605
|
virtual int GetChunkSize() {
|
1413
1606
|
return 65536;
|
@@ -1416,17 +1609,27 @@ class FileOutputStream : public OutputStream {
|
|
1416
1609
|
virtual void EndOfStream() {}
|
1417
1610
|
|
1418
1611
|
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1612
|
+
size_t len = static_cast<size_t>(size);
|
1613
|
+
|
1614
|
+
while (len) {
|
1615
|
+
ssize_t w = write(fd, data, len);
|
1616
|
+
|
1617
|
+
if (w > 0) {
|
1618
|
+
data += w;
|
1619
|
+
len -= w;
|
1620
|
+
} else if (w < 0) {
|
1621
|
+
err = errno;
|
1622
|
+
return kAbort;
|
1623
|
+
} else { /* w == 0, could be out-of-space */
|
1624
|
+
err = -1;
|
1625
|
+
return kAbort;
|
1626
|
+
}
|
1627
|
+
}
|
1628
|
+
return kContinue;
|
1426
1629
|
}
|
1427
1630
|
|
1428
1631
|
private:
|
1429
|
-
|
1632
|
+
int fd;
|
1430
1633
|
};
|
1431
1634
|
|
1432
1635
|
|
@@ -1439,13 +1642,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1439
1642
|
|
1440
1643
|
if (!fptr) return Qfalse;
|
1441
1644
|
|
1442
|
-
|
1443
|
-
|
1444
|
-
if (fp == NULL) return Qfalse;
|
1445
|
-
|
1645
|
+
// prepare for unbuffered write(2) below:
|
1646
|
+
rb_funcall(file, rb_intern("flush"), 0);
|
1446
1647
|
|
1447
1648
|
ContextInfo* context_info;
|
1448
|
-
|
1649
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1449
1650
|
Isolate* isolate;
|
1450
1651
|
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1451
1652
|
|
@@ -1459,13 +1660,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1459
1660
|
|
1460
1661
|
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1461
1662
|
|
1462
|
-
FileOutputStream stream(
|
1663
|
+
FileOutputStream stream(fptr->fd);
|
1463
1664
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1464
1665
|
|
1465
|
-
fflush(fp);
|
1466
|
-
|
1467
1666
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1468
1667
|
|
1668
|
+
/* TODO: perhaps rb_sys_fail here */
|
1669
|
+
if (stream.err) return Qfalse;
|
1670
|
+
|
1469
1671
|
return Qtrue;
|
1470
1672
|
}
|
1471
1673
|
|
@@ -1473,12 +1675,12 @@ static VALUE
|
|
1473
1675
|
rb_context_stop(VALUE self) {
|
1474
1676
|
|
1475
1677
|
ContextInfo* context_info;
|
1476
|
-
|
1678
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1477
1679
|
|
1478
1680
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1479
1681
|
|
1480
1682
|
// flag for termination
|
1481
|
-
isolate
|
1683
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1482
1684
|
|
1483
1685
|
isolate->TerminateExecution();
|
1484
1686
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
@@ -1490,7 +1692,7 @@ static VALUE
|
|
1490
1692
|
rb_context_dispose(VALUE self) {
|
1491
1693
|
|
1492
1694
|
ContextInfo* context_info;
|
1493
|
-
|
1695
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1494
1696
|
|
1495
1697
|
free_context(context_info);
|
1496
1698
|
|
@@ -1507,18 +1709,20 @@ nogvl_context_call(void *args) {
|
|
1507
1709
|
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
1710
|
Isolate* isolate = isolate_info->isolate;
|
1509
1711
|
|
1510
|
-
|
1511
|
-
isolate
|
1512
|
-
// terminate ASAP
|
1513
|
-
isolate->SetData(DO_TERMINATE, (void*)false);
|
1712
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1713
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1514
1714
|
|
1515
1715
|
if (call->max_memory > 0) {
|
1516
|
-
isolate
|
1517
|
-
isolate
|
1716
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1717
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1518
1718
|
if (!isolate_info->added_gc_cb) {
|
1519
|
-
|
1719
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1520
1720
|
isolate_info->added_gc_cb = true;
|
1721
|
+
}
|
1521
1722
|
}
|
1723
|
+
|
1724
|
+
if (call->marshal_stackdepth > 0) {
|
1725
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1522
1726
|
}
|
1523
1727
|
|
1524
1728
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1536,7 +1740,7 @@ nogvl_context_call(void *args) {
|
|
1536
1740
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1537
1741
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
1538
1742
|
|
1539
|
-
isolate
|
1743
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1540
1744
|
|
1541
1745
|
return NULL;
|
1542
1746
|
}
|
@@ -1551,7 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1551
1755
|
FunctionCall call;
|
1552
1756
|
VALUE *call_argv = NULL;
|
1553
1757
|
|
1554
|
-
|
1758
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1555
1759
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1556
1760
|
|
1557
1761
|
if (argc < 1) {
|
@@ -1584,6 +1788,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1584
1788
|
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1585
1789
|
call.max_memory = (size_t)sl_int;
|
1586
1790
|
}
|
1791
|
+
|
1792
|
+
call.marshal_stackdepth = 0;
|
1793
|
+
VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
|
1794
|
+
if (marshal_stackdepth != Qnil) {
|
1795
|
+
unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
|
1796
|
+
call.marshal_stackdepth = (size_t)sl_int;
|
1797
|
+
}
|
1587
1798
|
|
1588
1799
|
bool missingFunction = false;
|
1589
1800
|
{
|
@@ -1634,7 +1845,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1634
1845
|
|
1635
1846
|
static VALUE rb_context_create_isolate_value(VALUE self) {
|
1636
1847
|
ContextInfo* context_info;
|
1637
|
-
|
1848
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1638
1849
|
IsolateInfo *isolate_info = context_info->isolate_info;
|
1639
1850
|
|
1640
1851
|
if (!isolate_info) {
|
@@ -1642,7 +1853,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1642
1853
|
}
|
1643
1854
|
|
1644
1855
|
isolate_info->hold();
|
1645
|
-
return
|
1856
|
+
return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
|
1646
1857
|
}
|
1647
1858
|
|
1648
1859
|
static void set_ruby_exiting(VALUE value) {
|
@@ -1698,7 +1909,6 @@ extern "C" {
|
|
1698
1909
|
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
1699
1910
|
|
1700
1911
|
rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
|
1701
|
-
rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
|
1702
1912
|
|
1703
1913
|
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
1704
1914
|
rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
|
data/lib/mini_racer/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
@@ -15,6 +15,9 @@ require "json"
|
|
15
15
|
|
16
16
|
module MiniRacer
|
17
17
|
|
18
|
+
MARSHAL_STACKDEPTH_DEFAULT = 2**9-2
|
19
|
+
MARSHAL_STACKDEPTH_MAX_VALUE = 2**10-2
|
20
|
+
|
18
21
|
class Error < ::StandardError; end
|
19
22
|
|
20
23
|
class ContextDisposedError < Error; end
|
@@ -140,10 +143,10 @@ module MiniRacer
|
|
140
143
|
end
|
141
144
|
end
|
142
145
|
|
143
|
-
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
|
146
|
+
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil, marshal_stack_depth: nil)
|
144
147
|
options ||= {}
|
145
148
|
|
146
|
-
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
149
|
+
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, marshal_stack_depth: marshal_stack_depth, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
147
150
|
|
148
151
|
@functions = {}
|
149
152
|
@timeout = nil
|
@@ -151,6 +154,7 @@ module MiniRacer
|
|
151
154
|
@current_exception = nil
|
152
155
|
@timeout = timeout
|
153
156
|
@max_memory = max_memory
|
157
|
+
@marshal_stack_depth = marshal_stack_depth
|
154
158
|
|
155
159
|
# false signals it should be fetched if requested
|
156
160
|
@isolate = isolate || false
|
@@ -379,11 +383,12 @@ module MiniRacer
|
|
379
383
|
rp.close if rp
|
380
384
|
end
|
381
385
|
|
382
|
-
def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
|
386
|
+
def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
|
383
387
|
assert_option_is_nil_or_a('isolate', isolate, Isolate)
|
384
388
|
assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
|
385
389
|
|
386
|
-
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
|
390
|
+
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000, max_value: 2**32-1)
|
391
|
+
assert_numeric_or_nil('marshal_stack_depth', marshal_stack_depth, min_value: 1, max_value: MARSHAL_STACKDEPTH_MAX_VALUE)
|
387
392
|
assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
|
388
393
|
assert_numeric_or_nil('timeout', timeout, min_value: 1)
|
389
394
|
|
@@ -392,9 +397,13 @@ module MiniRacer
|
|
392
397
|
end
|
393
398
|
end
|
394
399
|
|
395
|
-
def assert_numeric_or_nil(option_name, object, min_value:)
|
400
|
+
def assert_numeric_or_nil(option_name, object, min_value:, max_value: nil)
|
401
|
+
if max_value && object.is_a?(Numeric) && object > max_value
|
402
|
+
raise ArgumentError, "#{option_name} must be less than or equal to #{max_value}"
|
403
|
+
end
|
404
|
+
|
396
405
|
if object.is_a?(Numeric) && object < min_value
|
397
|
-
raise ArgumentError, "#{option_name} must be larger than #{min_value}"
|
406
|
+
raise ArgumentError, "#{option_name} must be larger than or equal to #{min_value}"
|
398
407
|
end
|
399
408
|
|
400
409
|
if !object.nil? && !object.is_a?(Numeric)
|
data/mini_racer.gemspec
CHANGED
@@ -22,8 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
}
|
23
23
|
|
24
24
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(benchmark|test|spec|features|examples)/}) }
|
25
|
-
spec.bindir = "exe"
|
26
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
25
|
spec.require_paths = ["lib"]
|
28
26
|
|
29
27
|
spec.add_development_dependency "bundler"
|
@@ -37,5 +35,5 @@ Gem::Specification.new do |spec|
|
|
37
35
|
|
38
36
|
spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
|
39
37
|
|
40
|
-
spec.required_ruby_version = '>= 2.
|
38
|
+
spec.required_ruby_version = '>= 2.6'
|
41
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_racer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 16.10.0.0
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 16.10.0.0
|
97
97
|
description: Minimal embedded v8 engine for Ruby
|
98
98
|
email:
|
99
99
|
- sam.saffron@gmail.com
|
@@ -128,9 +128,9 @@ licenses:
|
|
128
128
|
- MIT
|
129
129
|
metadata:
|
130
130
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
131
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.
|
132
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
133
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
131
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.6.0/CHANGELOG
|
132
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.6.0
|
133
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.6.0
|
134
134
|
post_install_message:
|
135
135
|
rdoc_options: []
|
136
136
|
require_paths:
|
@@ -140,14 +140,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
140
|
requirements:
|
141
141
|
- - ">="
|
142
142
|
- !ruby/object:Gem::Version
|
143
|
-
version: '2.
|
143
|
+
version: '2.6'
|
144
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
145
|
requirements:
|
146
|
-
- - "
|
146
|
+
- - ">="
|
147
147
|
- !ruby/object:Gem::Version
|
148
|
-
version:
|
148
|
+
version: '0'
|
149
149
|
requirements: []
|
150
|
-
rubygems_version: 3.
|
150
|
+
rubygems_version: 3.1.6
|
151
151
|
signing_key:
|
152
152
|
specification_version: 4
|
153
153
|
summary: Minimal embedded v8 for Ruby
|