mini_racer 0.4.0 → 0.6.1
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 +21 -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 +332 -118
- data/ext/mini_racer_loader/extconf.rb +1 -1
- data/lib/mini_racer/version.rb +2 -2
- data/lib/mini_racer.rb +15 -6
- data/mini_racer.gemspec +1 -3
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d56d838af805799a79c79077af28cd6f141a236407b974d74fb2a9b9ccc012a
|
4
|
+
data.tar.gz: 930da203d2f0a9370a586f838cde68306fa3de03bb1114d887426457277c4682
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: caa253f750407e8bb4500105dcbe4318eafc8bf8bdf3dd4727efe5540fd273bd8e97d4f4f235b3d4c58e318687a255e88881c0dfe2b47ab67c91bfe801c8741a
|
7
|
+
data.tar.gz: 6dd05e37d3fc792f8f0b3e417023a0fd8e5dc3322a03528ac0b474b603a5d0a3551f791b57b83332ca24ea6596795ee9aaafa9e0d40e5f80e833a02e22ee3038
|
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,5 +1,26 @@
|
|
1
|
+
- 31-12-2021
|
2
|
+
|
3
|
+
- 0.6.1
|
4
|
+
|
5
|
+
- Added support for single threaded platform: `MiniRacer::Platform.set_flags! :single_threaded`
|
6
|
+
must be called prior to booting ANY MiniRacer::Context
|
7
|
+
|
8
|
+
- 0.6.0
|
9
|
+
|
10
|
+
- Ruby 3.1 support
|
11
|
+
- Fixes memory leak in heap snapshotting
|
12
|
+
- Improved compilation ergonomics in clang
|
13
|
+
- Migrated internal storage in c extension to TypedData
|
14
|
+
|
1
15
|
- 11-04-2021
|
2
16
|
|
17
|
+
- 0.5.0
|
18
|
+
- Fixes issues on aarch (Apple M1)
|
19
|
+
- Update to use libv8-node 16.x (#210) [Loic Nageleisen]
|
20
|
+
- FEATURE: Configurable max marshal stack depth (#202) [seanmakesgames]
|
21
|
+
- FEATURE: Configurable max marshal stack depth (#202) [seanmakesgames]
|
22
|
+
- Ruby 2.3 and 2.4 are EOL, we no longer support them
|
23
|
+
|
3
24
|
- 0.4.0
|
4
25
|
|
5
26
|
- FEATURE: upgrade to libv8 node 15.14.0 (v8 8.6.395.17)
|
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
|
|
@@ -196,7 +360,11 @@ static void init_v8() {
|
|
196
360
|
|
197
361
|
if (current_platform == NULL) {
|
198
362
|
V8::InitializeICU();
|
199
|
-
|
363
|
+
if (single_threaded) {
|
364
|
+
current_platform = platform::NewSingleThreadedDefaultPlatform();
|
365
|
+
} else {
|
366
|
+
current_platform = platform::NewDefaultPlatform();
|
367
|
+
}
|
200
368
|
V8::InitializePlatform(current_platform.get());
|
201
369
|
V8::Initialize();
|
202
370
|
}
|
@@ -205,16 +373,18 @@ static void init_v8() {
|
|
205
373
|
}
|
206
374
|
|
207
375
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
208
|
-
if((
|
376
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
377
|
+
return;
|
378
|
+
}
|
209
379
|
|
210
|
-
size_t softlimit =
|
380
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
211
381
|
|
212
382
|
HeapStatistics stats;
|
213
383
|
isolate->GetHeapStatistics(&stats);
|
214
384
|
size_t used = stats.used_heap_size();
|
215
385
|
|
216
386
|
if(used > softlimit) {
|
217
|
-
isolate
|
387
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
218
388
|
isolate->TerminateExecution();
|
219
389
|
}
|
220
390
|
}
|
@@ -311,7 +481,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
311
481
|
}
|
312
482
|
}
|
313
483
|
|
314
|
-
void*
|
484
|
+
static void*
|
315
485
|
nogvl_context_eval(void* arg) {
|
316
486
|
|
317
487
|
EvalParams* eval_params = (EvalParams*)arg;
|
@@ -326,14 +496,15 @@ nogvl_context_eval(void* arg) {
|
|
326
496
|
Context::Scope context_scope(context);
|
327
497
|
v8::ScriptOrigin *origin = NULL;
|
328
498
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
499
|
+
IsolateData::Init(isolate);
|
500
|
+
|
501
|
+
if (eval_params->max_memory > 0) {
|
502
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
|
503
|
+
if (!isolate_info->added_gc_cb) {
|
504
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
505
|
+
isolate_info->added_gc_cb = true;
|
506
|
+
}
|
507
|
+
}
|
337
508
|
|
338
509
|
MaybeLocal<Script> parsed_script;
|
339
510
|
|
@@ -359,12 +530,8 @@ nogvl_context_eval(void* arg) {
|
|
359
530
|
result->message->Reset(isolate, trycatch.Exception());
|
360
531
|
} else {
|
361
532
|
// 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
|
-
}
|
533
|
+
if (eval_params->marshal_stackdepth > 0) {
|
534
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
368
535
|
}
|
369
536
|
|
370
537
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -372,7 +539,7 @@ nogvl_context_eval(void* arg) {
|
|
372
539
|
|
373
540
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
374
541
|
|
375
|
-
isolate
|
542
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
376
543
|
|
377
544
|
return NULL;
|
378
545
|
}
|
@@ -390,6 +557,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
390
557
|
Isolate::Scope isolate_scope(isolate);
|
391
558
|
HandleScope scope(isolate);
|
392
559
|
|
560
|
+
StackCounter stackCounter(isolate);
|
561
|
+
|
562
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
|
563
|
+
return Qnil;
|
564
|
+
}
|
565
|
+
|
566
|
+
if (stackCounter.IsTooDeep()) {
|
567
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
568
|
+
isolate->TerminateExecution();
|
569
|
+
return Qnil;
|
570
|
+
}
|
571
|
+
|
393
572
|
if (value->IsNull() || value->IsUndefined()){
|
394
573
|
return Qnil;
|
395
574
|
}
|
@@ -478,7 +657,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
478
657
|
rb_enc_find("utf-8")
|
479
658
|
);
|
480
659
|
|
481
|
-
return
|
660
|
+
return rb_str_intern(str_symbol);
|
482
661
|
}
|
483
662
|
|
484
663
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
@@ -543,7 +722,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
543
722
|
length = RARRAY_LEN(value);
|
544
723
|
array = Array::New(isolate, (int)length);
|
545
724
|
for(i=0; i<length; i++) {
|
546
|
-
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
725
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
726
|
+
(void)(success);
|
547
727
|
}
|
548
728
|
return scope.Escape(array);
|
549
729
|
case T_HASH:
|
@@ -552,8 +732,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
552
732
|
length = RARRAY_LEN(hash_as_array);
|
553
733
|
for(i=0; i<length; i++) {
|
554
734
|
pair = rb_ary_entry(hash_as_array, i);
|
555
|
-
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
735
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
556
736
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
737
|
+
(void)(success);
|
557
738
|
}
|
558
739
|
return scope.Escape(object);
|
559
740
|
case T_SYMBOL:
|
@@ -633,6 +814,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
|
633
814
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
634
815
|
}
|
635
816
|
|
817
|
+
static
|
636
818
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
637
819
|
const char *warmup_source) {
|
638
820
|
// Use following steps to create a warmed up snapshot blob from a cold one:
|
@@ -668,14 +850,14 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
668
850
|
|
669
851
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
670
852
|
SnapshotInfo* snapshot_info;
|
671
|
-
|
853
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
672
854
|
|
673
855
|
return INT2NUM(snapshot_info->raw_size);
|
674
856
|
}
|
675
857
|
|
676
858
|
static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
677
859
|
SnapshotInfo* snapshot_info;
|
678
|
-
|
860
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
679
861
|
|
680
862
|
if(TYPE(str) != T_STRING) {
|
681
863
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
@@ -698,14 +880,14 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
698
880
|
|
699
881
|
static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
|
700
882
|
SnapshotInfo* snapshot_info;
|
701
|
-
|
883
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
702
884
|
|
703
885
|
return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
|
704
886
|
}
|
705
887
|
|
706
888
|
static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
707
889
|
SnapshotInfo* snapshot_info;
|
708
|
-
|
890
|
+
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
709
891
|
|
710
892
|
if(TYPE(str) != T_STRING) {
|
711
893
|
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
@@ -752,13 +934,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
|
|
752
934
|
|
753
935
|
static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
754
936
|
IsolateInfo* isolate_info;
|
755
|
-
|
937
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
756
938
|
|
757
939
|
init_v8();
|
758
940
|
|
759
941
|
SnapshotInfo* snapshot_info = nullptr;
|
760
942
|
if (!NIL_P(snapshot)) {
|
761
|
-
|
943
|
+
TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
|
762
944
|
}
|
763
945
|
|
764
946
|
isolate_info->init(snapshot_info);
|
@@ -769,7 +951,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
|
|
769
951
|
|
770
952
|
static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
771
953
|
IsolateInfo* isolate_info;
|
772
|
-
|
954
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
773
955
|
|
774
956
|
if (current_platform == NULL) return Qfalse;
|
775
957
|
|
@@ -780,7 +962,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
780
962
|
|
781
963
|
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
782
964
|
IsolateInfo* isolate_info;
|
783
|
-
|
965
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
784
966
|
|
785
967
|
if (current_platform == NULL) return Qfalse;
|
786
968
|
|
@@ -790,7 +972,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
|
790
972
|
|
791
973
|
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
792
974
|
IsolateInfo* isolate_info;
|
793
|
-
|
975
|
+
TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
|
794
976
|
|
795
977
|
if (current_platform == NULL) return Qfalse;
|
796
978
|
|
@@ -803,7 +985,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
|
803
985
|
|
804
986
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
805
987
|
ContextInfo* context_info;
|
806
|
-
|
988
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
807
989
|
|
808
990
|
init_v8();
|
809
991
|
|
@@ -814,11 +996,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
814
996
|
|
815
997
|
SnapshotInfo *snapshot_info = nullptr;
|
816
998
|
if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
|
817
|
-
|
999
|
+
TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
|
818
1000
|
}
|
819
1001
|
isolate_info->init(snapshot_info);
|
820
1002
|
} else { // given isolate or snapshot
|
821
|
-
|
1003
|
+
TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
|
822
1004
|
}
|
823
1005
|
|
824
1006
|
context_info->isolate_info = isolate_info;
|
@@ -848,7 +1030,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
|
848
1030
|
static VALUE convert_result_to_ruby(VALUE self /* context */,
|
849
1031
|
EvalResult& result) {
|
850
1032
|
ContextInfo *context_info;
|
851
|
-
|
1033
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
852
1034
|
|
853
1035
|
Isolate *isolate = context_info->isolate_info->isolate;
|
854
1036
|
Persistent<Context> *p_ctx = context_info->context;
|
@@ -884,9 +1066,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
884
1066
|
if (!result.executed) {
|
885
1067
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
886
1068
|
if (ruby_exception == Qnil) {
|
887
|
-
bool mem_softlimit_reached = (
|
1069
|
+
bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
|
1070
|
+
bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
|
888
1071
|
// If we were terminated or have the memory softlimit flag set
|
889
|
-
if (
|
1072
|
+
if (marshal_stack_maxdepth_reached) {
|
1073
|
+
ruby_exception = rb_eScriptRuntimeError;
|
1074
|
+
std::string msg = std::string("Marshal object depth too deep. Script terminated.");
|
1075
|
+
message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
|
1076
|
+
} else if (result.terminated || mem_softlimit_reached) {
|
890
1077
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
891
1078
|
} else {
|
892
1079
|
ruby_exception = rb_eScriptRuntimeError;
|
@@ -921,6 +1108,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
921
1108
|
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
922
1109
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
923
1110
|
} else {
|
1111
|
+
StackCounter::Reset(isolate);
|
924
1112
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
925
1113
|
}
|
926
1114
|
|
@@ -943,7 +1131,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
943
1131
|
EvalResult eval_result;
|
944
1132
|
ContextInfo* context_info;
|
945
1133
|
|
946
|
-
|
1134
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
947
1135
|
Isolate* isolate = context_info->isolate_info->isolate;
|
948
1136
|
|
949
1137
|
if(TYPE(str) != T_STRING) {
|
@@ -978,6 +1166,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
978
1166
|
eval_params.result = &eval_result;
|
979
1167
|
eval_params.timeout = 0;
|
980
1168
|
eval_params.max_memory = 0;
|
1169
|
+
eval_params.marshal_stackdepth = 0;
|
981
1170
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
982
1171
|
if (timeout != Qnil) {
|
983
1172
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
@@ -988,6 +1177,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
988
1177
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
989
1178
|
}
|
990
1179
|
|
1180
|
+
VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
|
1181
|
+
if (stack_depth != Qnil) {
|
1182
|
+
eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
|
1183
|
+
}
|
1184
|
+
|
991
1185
|
eval_result.message = NULL;
|
992
1186
|
eval_result.backtrace = NULL;
|
993
1187
|
|
@@ -1025,7 +1219,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
|
|
1025
1219
|
return exception;
|
1026
1220
|
}
|
1027
1221
|
|
1028
|
-
void*
|
1222
|
+
static void*
|
1029
1223
|
gvl_ruby_callback(void* data) {
|
1030
1224
|
|
1031
1225
|
FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
|
@@ -1041,7 +1235,7 @@ gvl_ruby_callback(void* data) {
|
|
1041
1235
|
HandleScope scope(args->GetIsolate());
|
1042
1236
|
Local<External> external = Local<External>::Cast(args->Data());
|
1043
1237
|
|
1044
|
-
self =
|
1238
|
+
self = (VALUE)(external->Value());
|
1045
1239
|
callback = rb_iv_get(self, "@callback");
|
1046
1240
|
|
1047
1241
|
parent = rb_iv_get(self, "@parent");
|
@@ -1049,7 +1243,7 @@ gvl_ruby_callback(void* data) {
|
|
1049
1243
|
return NULL;
|
1050
1244
|
}
|
1051
1245
|
|
1052
|
-
|
1246
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1053
1247
|
|
1054
1248
|
if (length > 0) {
|
1055
1249
|
ruby_args = rb_ary_tmp_new(length);
|
@@ -1057,6 +1251,7 @@ gvl_ruby_callback(void* data) {
|
|
1057
1251
|
|
1058
1252
|
for (int i = 0; i < length; i++) {
|
1059
1253
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1254
|
+
StackCounter::Reset(args->GetIsolate());
|
1060
1255
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1061
1256
|
*context_info->context, value);
|
1062
1257
|
rb_ary_push(ruby_args, tmp);
|
@@ -1070,20 +1265,22 @@ gvl_ruby_callback(void* data) {
|
|
1070
1265
|
callback_data.ruby_args = ruby_args;
|
1071
1266
|
callback_data.failed = false;
|
1072
1267
|
|
1073
|
-
if ((
|
1268
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1074
1269
|
args->GetIsolate()->ThrowException(
|
1075
1270
|
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
1271
|
"Terminated execution during transition from Ruby to JS"));
|
1077
1272
|
args->GetIsolate()->TerminateExecution();
|
1078
1273
|
if (length > 0) {
|
1079
1274
|
rb_ary_clear(ruby_args);
|
1080
|
-
rb_gc_force_recycle(ruby_args);
|
1081
1275
|
}
|
1082
1276
|
return NULL;
|
1083
1277
|
}
|
1084
1278
|
|
1085
|
-
|
1086
|
-
|
1279
|
+
VALUE callback_data_value = (VALUE)&callback_data;
|
1280
|
+
|
1281
|
+
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1282
|
+
result = rb_rescue2(protected_callback, callback_data_value,
|
1283
|
+
rescue_callback, callback_data_value, rb_eException, (VALUE)0);
|
1087
1284
|
|
1088
1285
|
if(callback_data.failed) {
|
1089
1286
|
rb_iv_set(parent, "@current_exception", result);
|
@@ -1097,26 +1294,24 @@ gvl_ruby_callback(void* data) {
|
|
1097
1294
|
|
1098
1295
|
if (length > 0) {
|
1099
1296
|
rb_ary_clear(ruby_args);
|
1100
|
-
rb_gc_force_recycle(ruby_args);
|
1101
1297
|
}
|
1102
1298
|
|
1103
|
-
if ((
|
1104
|
-
|
1105
|
-
isolate->TerminateExecution();
|
1299
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1300
|
+
args->GetIsolate()->TerminateExecution();
|
1106
1301
|
}
|
1107
1302
|
|
1108
1303
|
return NULL;
|
1109
1304
|
}
|
1110
1305
|
|
1111
1306
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1112
|
-
bool has_gvl = (
|
1307
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1113
1308
|
|
1114
1309
|
if(has_gvl) {
|
1115
1310
|
gvl_ruby_callback((void*)&args);
|
1116
1311
|
} else {
|
1117
|
-
args.GetIsolate()
|
1312
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1118
1313
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1119
|
-
args.GetIsolate()
|
1314
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1120
1315
|
}
|
1121
1316
|
}
|
1122
1317
|
|
@@ -1133,7 +1328,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1133
1328
|
bool parse_error = false;
|
1134
1329
|
bool attach_error = false;
|
1135
1330
|
|
1136
|
-
|
1331
|
+
TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
|
1137
1332
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1138
1333
|
|
1139
1334
|
{
|
@@ -1149,20 +1344,18 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1149
1344
|
NewStringType::kNormal, (int)RSTRING_LEN(name))
|
1150
1345
|
.ToLocalChecked();
|
1151
1346
|
|
1152
|
-
//
|
1153
|
-
|
1154
|
-
|
1155
|
-
*self_copy = self;
|
1156
|
-
|
1157
|
-
Local<Value> external = External::New(isolate, self_copy);
|
1347
|
+
// Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
|
1348
|
+
// not a T_DATA type like most other classes in this file
|
1349
|
+
Local<Value> external = External::New(isolate, (void *)self);
|
1158
1350
|
|
1159
1351
|
if (parent_object == Qnil) {
|
1160
|
-
context->Global()->Set(
|
1352
|
+
Maybe<bool> success = context->Global()->Set(
|
1161
1353
|
context,
|
1162
1354
|
v8_str,
|
1163
1355
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1164
1356
|
->GetFunction(context)
|
1165
1357
|
.ToLocalChecked());
|
1358
|
+
(void)success;
|
1166
1359
|
|
1167
1360
|
} else {
|
1168
1361
|
Local<String> eval =
|
@@ -1182,12 +1375,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1182
1375
|
if (!maybe_value.IsEmpty()) {
|
1183
1376
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1184
1377
|
if (value->IsObject()) {
|
1185
|
-
value.As<Object>()->Set(
|
1378
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
1186
1379
|
context,
|
1187
1380
|
v8_str,
|
1188
1381
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1189
1382
|
->GetFunction(context)
|
1190
1383
|
.ToLocalChecked());
|
1384
|
+
(void)success;
|
1191
1385
|
attach_error = false;
|
1192
1386
|
}
|
1193
1387
|
}
|
@@ -1209,7 +1403,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1209
1403
|
|
1210
1404
|
static VALUE rb_context_isolate_mutex(VALUE self) {
|
1211
1405
|
ContextInfo* context_info;
|
1212
|
-
|
1406
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1213
1407
|
|
1214
1408
|
if (!context_info->isolate_info) {
|
1215
1409
|
rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
|
@@ -1319,6 +1513,11 @@ static void mark_isolate(void* data) {
|
|
1319
1513
|
isolate_info->mark();
|
1320
1514
|
}
|
1321
1515
|
|
1516
|
+
static size_t isolate_memsize(const void *ptr) {
|
1517
|
+
const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
|
1518
|
+
return sizeof(*isolate_info);
|
1519
|
+
}
|
1520
|
+
|
1322
1521
|
static void deallocate(void* data) {
|
1323
1522
|
ContextInfo* context_info = (ContextInfo*)data;
|
1324
1523
|
|
@@ -1327,6 +1526,11 @@ static void deallocate(void* data) {
|
|
1327
1526
|
xfree(data);
|
1328
1527
|
}
|
1329
1528
|
|
1529
|
+
static size_t context_memsize(const void *ptr)
|
1530
|
+
{
|
1531
|
+
return sizeof(ContextInfo);
|
1532
|
+
}
|
1533
|
+
|
1330
1534
|
static void mark_context(void* data) {
|
1331
1535
|
ContextInfo* context_info = (ContextInfo*)data;
|
1332
1536
|
if (context_info->isolate_info) {
|
@@ -1334,48 +1538,39 @@ static void mark_context(void* data) {
|
|
1334
1538
|
}
|
1335
1539
|
}
|
1336
1540
|
|
1337
|
-
static void deallocate_external_function(void * data) {
|
1338
|
-
xfree(data);
|
1339
|
-
}
|
1340
|
-
|
1341
1541
|
static void deallocate_snapshot(void * data) {
|
1342
1542
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1343
1543
|
delete[] snapshot_info->data;
|
1344
1544
|
xfree(snapshot_info);
|
1345
1545
|
}
|
1346
1546
|
|
1347
|
-
static
|
1348
|
-
|
1349
|
-
return
|
1547
|
+
static size_t snapshot_memsize(const void *data) {
|
1548
|
+
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1549
|
+
return sizeof(*snapshot_info) + snapshot_info->raw_size;
|
1350
1550
|
}
|
1351
1551
|
|
1352
1552
|
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);
|
1553
|
+
ContextInfo* context_info;
|
1554
|
+
return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
|
1358
1555
|
}
|
1359
1556
|
|
1360
1557
|
static VALUE allocate_snapshot(VALUE klass) {
|
1361
|
-
SnapshotInfo* snapshot_info
|
1362
|
-
snapshot_info->data = NULL;
|
1363
|
-
snapshot_info->raw_size = 0;
|
1558
|
+
SnapshotInfo* snapshot_info;
|
1364
1559
|
|
1365
|
-
return
|
1560
|
+
return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
|
1366
1561
|
}
|
1367
1562
|
|
1368
1563
|
static VALUE allocate_isolate(VALUE klass) {
|
1369
1564
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1370
1565
|
|
1371
|
-
return
|
1566
|
+
return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
|
1372
1567
|
}
|
1373
1568
|
|
1374
1569
|
static VALUE
|
1375
1570
|
rb_heap_stats(VALUE self) {
|
1376
1571
|
|
1377
1572
|
ContextInfo* context_info;
|
1378
|
-
|
1573
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1379
1574
|
Isolate* isolate;
|
1380
1575
|
v8::HeapStatistics stats;
|
1381
1576
|
|
@@ -1407,7 +1602,9 @@ rb_heap_stats(VALUE self) {
|
|
1407
1602
|
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1408
1603
|
class FileOutputStream : public OutputStream {
|
1409
1604
|
public:
|
1410
|
-
|
1605
|
+
int err;
|
1606
|
+
|
1607
|
+
FileOutputStream(int fd) : fd(fd) { err = 0; }
|
1411
1608
|
|
1412
1609
|
virtual int GetChunkSize() {
|
1413
1610
|
return 65536;
|
@@ -1416,17 +1613,27 @@ class FileOutputStream : public OutputStream {
|
|
1416
1613
|
virtual void EndOfStream() {}
|
1417
1614
|
|
1418
1615
|
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1616
|
+
size_t len = static_cast<size_t>(size);
|
1617
|
+
|
1618
|
+
while (len) {
|
1619
|
+
ssize_t w = write(fd, data, len);
|
1620
|
+
|
1621
|
+
if (w > 0) {
|
1622
|
+
data += w;
|
1623
|
+
len -= w;
|
1624
|
+
} else if (w < 0) {
|
1625
|
+
err = errno;
|
1626
|
+
return kAbort;
|
1627
|
+
} else { /* w == 0, could be out-of-space */
|
1628
|
+
err = -1;
|
1629
|
+
return kAbort;
|
1630
|
+
}
|
1631
|
+
}
|
1632
|
+
return kContinue;
|
1426
1633
|
}
|
1427
1634
|
|
1428
1635
|
private:
|
1429
|
-
|
1636
|
+
int fd;
|
1430
1637
|
};
|
1431
1638
|
|
1432
1639
|
|
@@ -1439,13 +1646,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1439
1646
|
|
1440
1647
|
if (!fptr) return Qfalse;
|
1441
1648
|
|
1442
|
-
|
1443
|
-
|
1444
|
-
if (fp == NULL) return Qfalse;
|
1445
|
-
|
1649
|
+
// prepare for unbuffered write(2) below:
|
1650
|
+
rb_funcall(file, rb_intern("flush"), 0);
|
1446
1651
|
|
1447
1652
|
ContextInfo* context_info;
|
1448
|
-
|
1653
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1449
1654
|
Isolate* isolate;
|
1450
1655
|
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1451
1656
|
|
@@ -1459,13 +1664,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1459
1664
|
|
1460
1665
|
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1461
1666
|
|
1462
|
-
FileOutputStream stream(
|
1667
|
+
FileOutputStream stream(fptr->fd);
|
1463
1668
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1464
1669
|
|
1465
|
-
fflush(fp);
|
1466
|
-
|
1467
1670
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1468
1671
|
|
1672
|
+
/* TODO: perhaps rb_sys_fail here */
|
1673
|
+
if (stream.err) return Qfalse;
|
1674
|
+
|
1469
1675
|
return Qtrue;
|
1470
1676
|
}
|
1471
1677
|
|
@@ -1473,12 +1679,12 @@ static VALUE
|
|
1473
1679
|
rb_context_stop(VALUE self) {
|
1474
1680
|
|
1475
1681
|
ContextInfo* context_info;
|
1476
|
-
|
1682
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1477
1683
|
|
1478
1684
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1479
1685
|
|
1480
1686
|
// flag for termination
|
1481
|
-
isolate
|
1687
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1482
1688
|
|
1483
1689
|
isolate->TerminateExecution();
|
1484
1690
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
@@ -1490,7 +1696,7 @@ static VALUE
|
|
1490
1696
|
rb_context_dispose(VALUE self) {
|
1491
1697
|
|
1492
1698
|
ContextInfo* context_info;
|
1493
|
-
|
1699
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1494
1700
|
|
1495
1701
|
free_context(context_info);
|
1496
1702
|
|
@@ -1507,18 +1713,20 @@ nogvl_context_call(void *args) {
|
|
1507
1713
|
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
1714
|
Isolate* isolate = isolate_info->isolate;
|
1509
1715
|
|
1510
|
-
|
1511
|
-
isolate
|
1512
|
-
// terminate ASAP
|
1513
|
-
isolate->SetData(DO_TERMINATE, (void*)false);
|
1716
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1717
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1514
1718
|
|
1515
1719
|
if (call->max_memory > 0) {
|
1516
|
-
isolate
|
1517
|
-
isolate
|
1720
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1721
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1518
1722
|
if (!isolate_info->added_gc_cb) {
|
1519
|
-
|
1723
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1520
1724
|
isolate_info->added_gc_cb = true;
|
1725
|
+
}
|
1521
1726
|
}
|
1727
|
+
|
1728
|
+
if (call->marshal_stackdepth > 0) {
|
1729
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1522
1730
|
}
|
1523
1731
|
|
1524
1732
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1536,7 +1744,7 @@ nogvl_context_call(void *args) {
|
|
1536
1744
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1537
1745
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
1538
1746
|
|
1539
|
-
isolate
|
1747
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1540
1748
|
|
1541
1749
|
return NULL;
|
1542
1750
|
}
|
@@ -1551,7 +1759,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1551
1759
|
FunctionCall call;
|
1552
1760
|
VALUE *call_argv = NULL;
|
1553
1761
|
|
1554
|
-
|
1762
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1555
1763
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1556
1764
|
|
1557
1765
|
if (argc < 1) {
|
@@ -1584,6 +1792,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1584
1792
|
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1585
1793
|
call.max_memory = (size_t)sl_int;
|
1586
1794
|
}
|
1795
|
+
|
1796
|
+
call.marshal_stackdepth = 0;
|
1797
|
+
VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
|
1798
|
+
if (marshal_stackdepth != Qnil) {
|
1799
|
+
unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
|
1800
|
+
call.marshal_stackdepth = (size_t)sl_int;
|
1801
|
+
}
|
1587
1802
|
|
1588
1803
|
bool missingFunction = false;
|
1589
1804
|
{
|
@@ -1634,7 +1849,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1634
1849
|
|
1635
1850
|
static VALUE rb_context_create_isolate_value(VALUE self) {
|
1636
1851
|
ContextInfo* context_info;
|
1637
|
-
|
1852
|
+
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1638
1853
|
IsolateInfo *isolate_info = context_info->isolate_info;
|
1639
1854
|
|
1640
1855
|
if (!isolate_info) {
|
@@ -1642,7 +1857,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1642
1857
|
}
|
1643
1858
|
|
1644
1859
|
isolate_info->hold();
|
1645
|
-
return
|
1860
|
+
return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
|
1646
1861
|
}
|
1647
1862
|
|
1648
1863
|
static void set_ruby_exiting(VALUE value) {
|
@@ -1698,7 +1913,6 @@ extern "C" {
|
|
1698
1913
|
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
|
1699
1914
|
|
1700
1915
|
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
1916
|
|
1703
1917
|
rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
|
1704
1918
|
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.1
|
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:
|
11
|
+
date: 2022-01-10 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.1/CHANGELOG
|
132
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.6.1
|
133
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.6.1
|
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
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
|