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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e73ac3193df3f33b51463ba1125ac4eee312caecd47833c545e098ba91d11ece
4
- data.tar.gz: d6d754829b36953d239ba85e84d2fc88c0af3ccbadd3f8d2e4cd9759bd24fab2
3
+ metadata.gz: 5d56d838af805799a79c79077af28cd6f141a236407b974d74fb2a9b9ccc012a
4
+ data.tar.gz: 930da203d2f0a9370a586f838cde68306fa3de03bb1114d887426457277c4682
5
5
  SHA512:
6
- metadata.gz: 52a0799d84be10bb03774789b3d12d4f0fe2d4159d310b9c6a1ae3dcf878a99cd6b7c786fae813f29754a894ecf5fcc6b7e07834c0aba994a16da5c54d75595b
7
- data.tar.gz: bc478bdb582d7b786d28754200063d7ef185396e0da23840f006679bc1af6707f54c7a719918360afa9b24c2b4ab083ca081e2b2d6853095ecd5949a9482ecbd
6
+ metadata.gz: caa253f750407e8bb4500105dcbe4318eafc8bf8bdf3dd4727efe5540fd273bd8e97d4f4f235b3d4c58e318687a255e88881c0dfe2b47ab67c91bfe801c8741a
7
+ data.tar.gz: 6dd05e37d3fc792f8f0b3e417023a0fd8e5dc3322a03528ac0b474b603a5d0a3551f791b57b83332ca24ea6596795ee9aaafa9e0d40e5f80e833a02e22ee3038
@@ -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,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 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
 
@@ -196,7 +360,11 @@ static void init_v8() {
196
360
 
197
361
  if (current_platform == NULL) {
198
362
  V8::InitializeICU();
199
- current_platform = platform::NewDefaultPlatform();
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((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
376
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
377
+ return;
378
+ }
209
379
 
210
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
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->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
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
- // 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);
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->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
- }
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->SetData(IN_GVL, (void*)true);
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 ID2SYM(rb_intern_str(str_symbol));
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- Data_Get_Struct(snap, SnapshotInfo, snapshot_info);
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
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 = (bool)isolate->GetData(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 (result.terminated || mem_softlimit_reached) {
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 = *(VALUE*)(external->Value());
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
- Data_Get_Struct(parent, ContextInfo, context_info);
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 ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
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
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1086
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
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 ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1104
- Isolate* isolate = args->GetIsolate();
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 = (bool)args.GetIsolate()->GetData(IN_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()->SetData(IN_GVL, (void*)true);
1312
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1118
1313
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1119
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
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
- Data_Get_Struct(parent, ContextInfo, context_info);
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
- // 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);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 VALUE allocate_external_function(VALUE klass) {
1348
- VALUE* self = ALLOC(VALUE);
1349
- return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
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 = 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);
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 = ALLOC(SnapshotInfo);
1362
- snapshot_info->data = NULL;
1363
- snapshot_info->raw_size = 0;
1558
+ SnapshotInfo* snapshot_info;
1364
1559
 
1365
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
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 Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- FileOutputStream(FILE* stream) : stream_(stream) {}
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
- 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;
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
- FILE* stream_;
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
- FILE* fp;
1443
- fp = fdopen(fptr->fd, "w");
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
- Data_Get_Struct(self, ContextInfo, context_info);
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(fp);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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->SetData(DO_TERMINATE, (void*)true);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- // in gvl flag
1511
- isolate->SetData(IN_GVL, (void*)false);
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->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1517
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
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
- isolate->AddGCEpilogueCallback(gc_callback);
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->SetData(IN_GVL, (void*)true);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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
- Data_Get_Struct(self, ContextInfo, context_info);
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 Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
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);
@@ -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"
5
- LIBV8_NODE_VERSION = "~> 15.14.0.0"
4
+ VERSION = "0.6.1"
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
4
+ version: 0.6.1
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-12 00:00:00.000000000 Z
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: 15.14.0.0
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.14.0.0
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/CHANGELOG
132
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.4.0
133
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.4.0
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.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
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