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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c805c80e637874adae6cd80514e54a7e56bccb744ba3f3951fb1b8a575dd306f
4
- data.tar.gz: 25721453b4d1fb59056bec90ca12363ce5f05c78fc984b5ee0c3007311d96b8c
3
+ metadata.gz: 179c0a63082cf1ec6bc7f8ead94fbcba3c41255ee91b1639482570d1ec3f7646
4
+ data.tar.gz: cdef6f06a6ab72060aa43b21518c6f0103bf80c034bee09b7cf1d6f215620c31
5
5
  SHA512:
6
- metadata.gz: ad2122baf3beb6e14861a5c1d5a8d50884ea803e6a59a6c62e72e6041b010da075d260c76e18f913cdcf30974d47ed013321f2660de204a06f8140d8cb313d87
7
- data.tar.gz: 1462cae8c8d61310db4e6a1484c2d7526c2b69005b6fd42254dde1682c75f8e3d1d8eda1bac59811dbce8aae89ba195f0d4f808c2315906110b99e031d73369b
6
+ metadata.gz: 0efd1873f139141ac489ed24ebcad3e289092ea67ea7823d0b94fe07a3acfcd602ed64c287b39b556d370db5c17b094d38a9c5ff06f31f546dddda7694d01c81
7
+ data.tar.gz: 1ccc24b53320211be2b3cd887ac1193ee0654e0b54c0d2b3da636a382ce00c95d4af3ba5acd23a931436d08e0707dc0b4d348b6bbd0863cb56df9fb2fbd2678e
@@ -29,8 +29,6 @@ jobs:
29
29
  fail-fast: false
30
30
  matrix:
31
31
  ruby:
32
- - '2.4'
33
- - '2.5'
34
32
  - '2.6'
35
33
  - '2.7'
36
34
  - '3.0'
data/.gitignore CHANGED
@@ -10,3 +10,4 @@ Gemfile.lock
10
10
  lib/mini_racer_extension.so
11
11
  lib/mini_racer_loader.so
12
12
  *.bundle
13
+ .vscode/
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 gcc 4.8 (or later).
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 gcc that supports C++11 (gcc 4.8) this is included by default in ubuntu trusty based images.
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 4.8 on precise the simpler approach is to opt for the trusty based image.
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 #$CPPFLAGS", conf)
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
- $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
11
- $CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
12
- $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
13
- $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
14
- $CPPFLAGS += " -std=c++0x"
15
- $CPPFLAGS += " -fpermissive"
16
- $CPPFLAGS += " -DV8_COMPRESS_POINTERS"
17
- $CPPFLAGS += " -fvisibility=hidden "
18
-
19
- $CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
20
-
21
- $LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
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
- $CPPFLAGS.insert(0, " -fsanitize=address ")
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
- enum IsolateFlags {
132
- IN_GVL,
133
- DO_TERMINATE,
134
- MEM_SOFTLIMIT_VALUE,
135
- MEM_SOFTLIMIT_REACHED,
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((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
372
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
373
+ return;
374
+ }
209
375
 
210
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
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->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
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
- // in gvl flag
330
- isolate->SetData(IN_GVL, (void*)false);
331
- // terminate ASAP
332
- isolate->SetData(DO_TERMINATE, (void*)false);
333
- // Memory softlimit
334
- isolate->SetData(MEM_SOFTLIMIT_VALUE, (void*)false);
335
- // Memory softlimit hit flag
336
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
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->max_memory > 0) {
363
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
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->SetData(IN_GVL, (void*)true);
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 ID2SYM(rb_intern_str(str_symbol));
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- Data_Get_Struct(snap, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 = (bool)isolate->GetData(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 (result.terminated || mem_softlimit_reached) {
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 = *(VALUE*)(external->Value());
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
- Data_Get_Struct(parent, ContextInfo, context_info);
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 ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
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
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1086
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
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 ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1104
- Isolate* isolate = args->GetIsolate();
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 = (bool)args.GetIsolate()->GetData(IN_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()->SetData(IN_GVL, (void*)true);
1308
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1118
1309
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1119
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
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
- Data_Get_Struct(parent, ContextInfo, context_info);
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
- // copy self so we can access from v8 external
1153
- VALUE* self_copy;
1154
- Data_Get_Struct(self, VALUE, self_copy);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 VALUE allocate_external_function(VALUE klass) {
1348
- VALUE* self = ALLOC(VALUE);
1349
- return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
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 = ALLOC(ContextInfo);
1354
- context_info->isolate_info = NULL;
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 = ALLOC(SnapshotInfo);
1362
- snapshot_info->data = NULL;
1363
- snapshot_info->raw_size = 0;
1554
+ SnapshotInfo* snapshot_info;
1364
1555
 
1365
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
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 Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- FileOutputStream(FILE* stream) : stream_(stream) {}
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
- const size_t len = static_cast<size_t>(size);
1420
- size_t off = 0;
1421
-
1422
- while (off < len && !feof(stream_) && !ferror(stream_))
1423
- off += fwrite(data + off, 1, len - off, stream_);
1424
-
1425
- return off == len ? kContinue : kAbort;
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
- FILE* stream_;
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
- FILE* fp;
1443
- fp = fdopen(fptr->fd, "w");
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
- Data_Get_Struct(self, ContextInfo, context_info);
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(fp);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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->SetData(DO_TERMINATE, (void*)true);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- // in gvl flag
1511
- isolate->SetData(IN_GVL, (void*)false);
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->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1517
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
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
- isolate->AddGCEpilogueCallback(gc_callback);
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->SetData(IN_GVL, (void*)true);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
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);
@@ -3,6 +3,6 @@ require 'mkmf'
3
3
  extension_name = 'mini_racer_loader'
4
4
  dir_config extension_name
5
5
 
6
- $CPPFLAGS += " -fvisibility=hidden "
6
+ $CXXFLAGS += " -fvisibility=hidden "
7
7
 
8
8
  create_makefile extension_name
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniRacer
4
- VERSION = "0.4.0.beta1"
5
- LIBV8_NODE_VERSION = "~> 15.12.0.0.beta1"
4
+ VERSION = "0.6.0"
5
+ LIBV8_NODE_VERSION = "~> 16.10.0.0"
6
6
  end
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.3'
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.0.beta1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-08 00:00:00.000000000 Z
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: 15.12.0.0.beta1
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: 15.12.0.0.beta1
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.4.0.beta1/CHANGELOG
132
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.4.0.beta1
133
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.4.0.beta1
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.3'
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: 1.3.1
148
+ version: '0'
149
149
  requirements: []
150
- rubygems_version: 3.2.2
150
+ rubygems_version: 3.1.6
151
151
  signing_key:
152
152
  specification_version: 4
153
153
  summary: Minimal embedded v8 for Ruby