mini_racer 0.4.0 → 0.5.0.pre

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: b0a067ca868ffe75b89fc350d6d62b6e4d2670a4319eda9f6cd05fe424c762ec
4
+ data.tar.gz: 26c865123cceec38530a9be40e32c1042063f3cbb400172438308a7eb0e06568
5
5
  SHA512:
6
- metadata.gz: 52a0799d84be10bb03774789b3d12d4f0fe2d4159d310b9c6a1ae3dcf878a99cd6b7c786fae813f29754a894ecf5fcc6b7e07834c0aba994a16da5c54d75595b
7
- data.tar.gz: bc478bdb582d7b786d28754200063d7ef185396e0da23840f006679bc1af6707f54c7a719918360afa9b24c2b4ab083ca081e2b2d6853095ecd5949a9482ecbd
6
+ metadata.gz: 0e1de5f130d4cf5ddca606f57cf7864f9d05d87c9775cd6873d6bb6151983216f74bc7a7eb3e20a9382e5cf95a2ccd8958c7a548ddb491ac8cb9cb89d1c17fd8
7
+ data.tar.gz: 7df5b7f82afbc9040a6d317bdd8d8600d6656a965bcb44c3ce4f7b6f02007ae1e9fc088022e6c5c10c4755aa4c0dea9badbd4ca8087e3e9185f6f93f42835775
@@ -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/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
 
@@ -11,9 +11,9 @@ $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
11
11
  $CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
12
12
  $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
13
13
  $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
14
- $CPPFLAGS += " -std=c++0x"
14
+ $CPPFLAGS += " -std=c++14"
15
15
  $CPPFLAGS += " -fpermissive"
16
- $CPPFLAGS += " -DV8_COMPRESS_POINTERS"
16
+ #$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
17
17
  $CPPFLAGS += " -fvisibility=hidden "
18
18
 
19
19
  $CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
@@ -115,6 +115,7 @@ typedef struct {
115
115
  useconds_t timeout;
116
116
  EvalResult* result;
117
117
  size_t max_memory;
118
+ size_t marshal_stackdepth;
118
119
  } EvalParams;
119
120
 
120
121
  typedef struct {
@@ -126,13 +127,152 @@ typedef struct {
126
127
  Local<Value> *argv;
127
128
  EvalResult result;
128
129
  size_t max_memory;
130
+ size_t marshal_stackdepth;
129
131
  } FunctionCall;
130
132
 
131
- enum IsolateFlags {
132
- IN_GVL,
133
- DO_TERMINATE,
134
- MEM_SOFTLIMIT_VALUE,
135
- MEM_SOFTLIMIT_REACHED,
133
+ class IsolateData {
134
+ public:
135
+ enum Flag {
136
+ // first flags are bitfield
137
+ // max count: sizeof(uintptr_t) * 8
138
+ IN_GVL, // whether we are inside of ruby gvl or not
139
+ DO_TERMINATE, // terminate as soon as possible
140
+ MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit
141
+ MEM_SOFTLIMIT_MAX, // maximum memory value
142
+ MARSHAL_STACKDEPTH_REACHED, // we've hit our max stack depth
143
+ MARSHAL_STACKDEPTH_VALUE, // current stackdepth
144
+ MARSHAL_STACKDEPTH_MAX, // maximum stack depth during marshal
145
+ };
146
+
147
+ static void Init(Isolate *isolate) {
148
+ // zero out all fields in the bitfield
149
+ isolate->SetData(0, 0);
150
+ }
151
+
152
+ static uintptr_t Get(Isolate *isolate, Flag flag) {
153
+ Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
154
+ switch (flag) {
155
+ case IN_GVL: return u.IN_GVL;
156
+ case DO_TERMINATE: return u.DO_TERMINATE;
157
+ case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED;
158
+ case MEM_SOFTLIMIT_MAX: return static_cast<uintptr_t>(u.MEM_SOFTLIMIT_MAX) << 10;
159
+ case MARSHAL_STACKDEPTH_REACHED: return u.MARSHAL_STACKDEPTH_REACHED;
160
+ case MARSHAL_STACKDEPTH_VALUE: return u.MARSHAL_STACKDEPTH_VALUE;
161
+ case MARSHAL_STACKDEPTH_MAX: return u.MARSHAL_STACKDEPTH_MAX;
162
+ }
163
+
164
+ // avoid compiler warning
165
+ return u.IN_GVL;
166
+ }
167
+
168
+ static void Set(Isolate *isolate, Flag flag, uintptr_t value) {
169
+ Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
170
+ switch (flag) {
171
+ case IN_GVL: u.IN_GVL = value; break;
172
+ case DO_TERMINATE: u.DO_TERMINATE = value; break;
173
+ case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break;
174
+ // drop least significant 10 bits 'store memory amount in kb'
175
+ case MEM_SOFTLIMIT_MAX: u.MEM_SOFTLIMIT_MAX = value >> 10; break;
176
+ case MARSHAL_STACKDEPTH_REACHED: u.MARSHAL_STACKDEPTH_REACHED = value; break;
177
+ case MARSHAL_STACKDEPTH_VALUE: u.MARSHAL_STACKDEPTH_VALUE = value; break;
178
+ case MARSHAL_STACKDEPTH_MAX: u.MARSHAL_STACKDEPTH_MAX = value; break;
179
+ }
180
+ isolate->SetData(0, reinterpret_cast<void*>(u.dataPtr));
181
+ }
182
+
183
+ private:
184
+ struct Bitfield {
185
+ // WARNING: this would explode on platforms below 64 bit ptrs
186
+ // compiler will fail here, making it clear for them.
187
+ // Additionally, using the other part of the union to reinterpret the
188
+ // memory is undefined behavior according to spec, but is / has been stable
189
+ // across major compilers for decades.
190
+ static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit.");
191
+ union {
192
+ uint64_t dataPtr: 64;
193
+ // order in this struct matters. For cpu performance keep larger subobjects
194
+ // aligned on their boundaries (8 16 32), try not to straddle
195
+ struct {
196
+ size_t MEM_SOFTLIMIT_MAX:22;
197
+ bool IN_GVL:1;
198
+ bool DO_TERMINATE:1;
199
+ bool MEM_SOFTLIMIT_REACHED:1;
200
+ bool MARSHAL_STACKDEPTH_REACHED:1;
201
+ uint8_t :0; // align to next 8bit bound
202
+ size_t MARSHAL_STACKDEPTH_VALUE:10;
203
+ uint8_t :0; // align to next 8bit bound
204
+ size_t MARSHAL_STACKDEPTH_MAX:10;
205
+ };
206
+ };
207
+ };
208
+ };
209
+
210
+ struct StackCounter {
211
+ static void Reset(Isolate* isolate) {
212
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0) {
213
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
214
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
215
+ }
216
+ }
217
+
218
+ static void SetMax(Isolate* isolate, size_t marshalMaxStackDepth) {
219
+ if (marshalMaxStackDepth > 0) {
220
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX, marshalMaxStackDepth);
221
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
222
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
223
+ }
224
+ }
225
+
226
+ StackCounter(Isolate* isolate) {
227
+ this->isActive = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0;
228
+
229
+ if (this->isActive) {
230
+ this->isolate = isolate;
231
+ this->IncDepth(1);
232
+ }
233
+ }
234
+
235
+ bool IsTooDeep() {
236
+ if (!this->IsActive()) {
237
+ return false;
238
+ }
239
+
240
+ size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
241
+ size_t maxDepth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_MAX);
242
+ if (depth > maxDepth) {
243
+ IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, true);
244
+ return true;
245
+ }
246
+
247
+ return false;
248
+ }
249
+
250
+ bool IsActive() {
251
+ return this->isActive && !IsolateData::Get(this->isolate, IsolateData::DO_TERMINATE);
252
+ }
253
+
254
+ ~StackCounter() {
255
+ if (this->IsActive()) {
256
+ this->IncDepth(-1);
257
+ }
258
+ }
259
+
260
+ private:
261
+ Isolate* isolate;
262
+ bool isActive;
263
+
264
+ void IncDepth(int direction) {
265
+ int inc = direction > 0 ? 1 : -1;
266
+
267
+ size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
268
+
269
+ // don't decrement past 0
270
+ if (inc > 0 || depth > 0) {
271
+ depth += inc;
272
+ }
273
+
274
+ IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, depth);
275
+ }
136
276
  };
137
277
 
138
278
  static VALUE rb_cContext;
@@ -205,16 +345,18 @@ static void init_v8() {
205
345
  }
206
346
 
207
347
  static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
208
- if((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
348
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
349
+ return;
350
+ }
209
351
 
210
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
352
+ size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
211
353
 
212
354
  HeapStatistics stats;
213
355
  isolate->GetHeapStatistics(&stats);
214
356
  size_t used = stats.used_heap_size();
215
357
 
216
358
  if(used > softlimit) {
217
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
359
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
218
360
  isolate->TerminateExecution();
219
361
  }
220
362
  }
@@ -326,14 +468,15 @@ nogvl_context_eval(void* arg) {
326
468
  Context::Scope context_scope(context);
327
469
  v8::ScriptOrigin *origin = NULL;
328
470
 
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);
471
+ IsolateData::Init(isolate);
472
+
473
+ if (eval_params->max_memory > 0) {
474
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
475
+ if (!isolate_info->added_gc_cb) {
476
+ isolate->AddGCEpilogueCallback(gc_callback);
477
+ isolate_info->added_gc_cb = true;
478
+ }
479
+ }
337
480
 
338
481
  MaybeLocal<Script> parsed_script;
339
482
 
@@ -359,12 +502,8 @@ nogvl_context_eval(void* arg) {
359
502
  result->message->Reset(isolate, trycatch.Exception());
360
503
  } else {
361
504
  // 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
- }
505
+ if (eval_params->marshal_stackdepth > 0) {
506
+ StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
368
507
  }
369
508
 
370
509
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -372,7 +511,7 @@ nogvl_context_eval(void* arg) {
372
511
 
373
512
  prepare_result(maybe_value, trycatch, isolate, context, *result);
374
513
 
375
- isolate->SetData(IN_GVL, (void*)true);
514
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
376
515
 
377
516
  return NULL;
378
517
  }
@@ -390,6 +529,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
390
529
  Isolate::Scope isolate_scope(isolate);
391
530
  HandleScope scope(isolate);
392
531
 
532
+ StackCounter stackCounter(isolate);
533
+
534
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
535
+ return Qnil;
536
+ }
537
+
538
+ if (stackCounter.IsTooDeep()) {
539
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
540
+ isolate->TerminateExecution();
541
+ return Qnil;
542
+ }
543
+
393
544
  if (value->IsNull() || value->IsUndefined()){
394
545
  return Qnil;
395
546
  }
@@ -478,7 +629,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
478
629
  rb_enc_find("utf-8")
479
630
  );
480
631
 
481
- return ID2SYM(rb_intern_str(str_symbol));
632
+ return rb_str_intern(str_symbol);
482
633
  }
483
634
 
484
635
  MaybeLocal<String> rstr_maybe = value->ToString(context);
@@ -543,7 +694,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
543
694
  length = RARRAY_LEN(value);
544
695
  array = Array::New(isolate, (int)length);
545
696
  for(i=0; i<length; i++) {
546
- array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
697
+ Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
698
+ (void)(success);
547
699
  }
548
700
  return scope.Escape(array);
549
701
  case T_HASH:
@@ -552,8 +704,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
552
704
  length = RARRAY_LEN(hash_as_array);
553
705
  for(i=0; i<length; i++) {
554
706
  pair = rb_ary_entry(hash_as_array, i);
555
- object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
707
+ Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
556
708
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
709
+ (void)(success);
557
710
  }
558
711
  return scope.Escape(object);
559
712
  case T_SYMBOL:
@@ -884,9 +1037,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
884
1037
  if (!result.executed) {
885
1038
  VALUE ruby_exception = rb_iv_get(self, "@current_exception");
886
1039
  if (ruby_exception == Qnil) {
887
- bool mem_softlimit_reached = (bool)isolate->GetData(MEM_SOFTLIMIT_REACHED);
1040
+ bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
1041
+ bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
888
1042
  // If we were terminated or have the memory softlimit flag set
889
- if (result.terminated || mem_softlimit_reached) {
1043
+ if (marshal_stack_maxdepth_reached) {
1044
+ ruby_exception = rb_eScriptRuntimeError;
1045
+ std::string msg = std::string("Marshal object depth too deep. Script terminated.");
1046
+ message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
1047
+ } else if (result.terminated || mem_softlimit_reached) {
890
1048
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
891
1049
  } else {
892
1050
  ruby_exception = rb_eScriptRuntimeError;
@@ -921,6 +1079,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
921
1079
  VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
922
1080
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
923
1081
  } else {
1082
+ StackCounter::Reset(isolate);
924
1083
  ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
925
1084
  }
926
1085
 
@@ -978,6 +1137,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
978
1137
  eval_params.result = &eval_result;
979
1138
  eval_params.timeout = 0;
980
1139
  eval_params.max_memory = 0;
1140
+ eval_params.marshal_stackdepth = 0;
981
1141
  VALUE timeout = rb_iv_get(self, "@timeout");
982
1142
  if (timeout != Qnil) {
983
1143
  eval_params.timeout = (useconds_t)NUM2LONG(timeout);
@@ -988,6 +1148,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
988
1148
  eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
989
1149
  }
990
1150
 
1151
+ VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
1152
+ if (stack_depth != Qnil) {
1153
+ eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
1154
+ }
1155
+
991
1156
  eval_result.message = NULL;
992
1157
  eval_result.backtrace = NULL;
993
1158
 
@@ -1057,6 +1222,7 @@ gvl_ruby_callback(void* data) {
1057
1222
 
1058
1223
  for (int i = 0; i < length; i++) {
1059
1224
  Local<Value> value = ((*args)[i]).As<Value>();
1225
+ StackCounter::Reset(args->GetIsolate());
1060
1226
  VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
1061
1227
  *context_info->context, value);
1062
1228
  rb_ary_push(ruby_args, tmp);
@@ -1070,7 +1236,7 @@ gvl_ruby_callback(void* data) {
1070
1236
  callback_data.ruby_args = ruby_args;
1071
1237
  callback_data.failed = false;
1072
1238
 
1073
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1239
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1074
1240
  args->GetIsolate()->ThrowException(
1075
1241
  String::NewFromUtf8Literal(args->GetIsolate(),
1076
1242
  "Terminated execution during transition from Ruby to JS"));
@@ -1082,8 +1248,11 @@ gvl_ruby_callback(void* data) {
1082
1248
  return NULL;
1083
1249
  }
1084
1250
 
1085
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1086
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
1251
+ VALUE callback_data_value = (VALUE)&callback_data;
1252
+
1253
+ // TODO: use rb_vrescue2 in Ruby 2.7 and above
1254
+ result = rb_rescue2(RUBY_METHOD_FUNC(protected_callback), callback_data_value,
1255
+ RUBY_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
1087
1256
 
1088
1257
  if(callback_data.failed) {
1089
1258
  rb_iv_set(parent, "@current_exception", result);
@@ -1100,23 +1269,22 @@ gvl_ruby_callback(void* data) {
1100
1269
  rb_gc_force_recycle(ruby_args);
1101
1270
  }
1102
1271
 
1103
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1104
- Isolate* isolate = args->GetIsolate();
1105
- isolate->TerminateExecution();
1272
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1273
+ args->GetIsolate()->TerminateExecution();
1106
1274
  }
1107
1275
 
1108
1276
  return NULL;
1109
1277
  }
1110
1278
 
1111
1279
  static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
1112
- bool has_gvl = (bool)args.GetIsolate()->GetData(IN_GVL);
1280
+ bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
1113
1281
 
1114
1282
  if(has_gvl) {
1115
1283
  gvl_ruby_callback((void*)&args);
1116
1284
  } else {
1117
- args.GetIsolate()->SetData(IN_GVL, (void*)true);
1285
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1118
1286
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1119
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
1287
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
1120
1288
  }
1121
1289
  }
1122
1290
 
@@ -1157,12 +1325,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1157
1325
  Local<Value> external = External::New(isolate, self_copy);
1158
1326
 
1159
1327
  if (parent_object == Qnil) {
1160
- context->Global()->Set(
1328
+ Maybe<bool> success = context->Global()->Set(
1161
1329
  context,
1162
1330
  v8_str,
1163
1331
  FunctionTemplate::New(isolate, ruby_callback, external)
1164
1332
  ->GetFunction(context)
1165
1333
  .ToLocalChecked());
1334
+ (void)success;
1166
1335
 
1167
1336
  } else {
1168
1337
  Local<String> eval =
@@ -1182,12 +1351,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1182
1351
  if (!maybe_value.IsEmpty()) {
1183
1352
  Local<Value> value = maybe_value.ToLocalChecked();
1184
1353
  if (value->IsObject()) {
1185
- value.As<Object>()->Set(
1354
+ Maybe<bool> success = value.As<Object>()->Set(
1186
1355
  context,
1187
1356
  v8_str,
1188
1357
  FunctionTemplate::New(isolate, ruby_callback, external)
1189
1358
  ->GetFunction(context)
1190
1359
  .ToLocalChecked());
1360
+ (void)success;
1191
1361
  attach_error = false;
1192
1362
  }
1193
1363
  }
@@ -1478,7 +1648,7 @@ rb_context_stop(VALUE self) {
1478
1648
  Isolate* isolate = context_info->isolate_info->isolate;
1479
1649
 
1480
1650
  // flag for termination
1481
- isolate->SetData(DO_TERMINATE, (void*)true);
1651
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
1482
1652
 
1483
1653
  isolate->TerminateExecution();
1484
1654
  rb_funcall(self, rb_intern("stop_attached"), 0);
@@ -1507,18 +1677,20 @@ nogvl_context_call(void *args) {
1507
1677
  IsolateInfo *isolate_info = call->context_info->isolate_info;
1508
1678
  Isolate* isolate = isolate_info->isolate;
1509
1679
 
1510
- // in gvl flag
1511
- isolate->SetData(IN_GVL, (void*)false);
1512
- // terminate ASAP
1513
- isolate->SetData(DO_TERMINATE, (void*)false);
1680
+ IsolateData::Set(isolate, IsolateData::IN_GVL, false);
1681
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
1514
1682
 
1515
1683
  if (call->max_memory > 0) {
1516
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1517
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1684
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
1685
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
1518
1686
  if (!isolate_info->added_gc_cb) {
1519
- isolate->AddGCEpilogueCallback(gc_callback);
1687
+ isolate->AddGCEpilogueCallback(gc_callback);
1520
1688
  isolate_info->added_gc_cb = true;
1689
+ }
1521
1690
  }
1691
+
1692
+ if (call->marshal_stackdepth > 0) {
1693
+ StackCounter::SetMax(isolate, call->marshal_stackdepth);
1522
1694
  }
1523
1695
 
1524
1696
  Isolate::Scope isolate_scope(isolate);
@@ -1536,7 +1708,7 @@ nogvl_context_call(void *args) {
1536
1708
  MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
1537
1709
  prepare_result(res, trycatch, isolate, context, eval_res);
1538
1710
 
1539
- isolate->SetData(IN_GVL, (void*)true);
1711
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
1540
1712
 
1541
1713
  return NULL;
1542
1714
  }
@@ -1584,6 +1756,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1584
1756
  unsigned long sl_int = NUM2ULONG(mem_softlimit);
1585
1757
  call.max_memory = (size_t)sl_int;
1586
1758
  }
1759
+
1760
+ call.marshal_stackdepth = 0;
1761
+ VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
1762
+ if (marshal_stackdepth != Qnil) {
1763
+ unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
1764
+ call.marshal_stackdepth = (size_t)sl_int;
1765
+ }
1587
1766
 
1588
1767
  bool missingFunction = false;
1589
1768
  {
@@ -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.5.0.pre"
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.5.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
- autorequire:
9
- bindir: exe
8
+ autorequire:
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-12 00:00:00.000000000 Z
11
+ date: 2021-11-03 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,10 +128,10 @@ 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
134
- post_install_message:
131
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.5.0.pre/CHANGELOG
132
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.5.0.pre
133
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.5.0.pre
134
+ post_install_message:
135
135
  rdoc_options: []
136
136
  require_paths:
137
137
  - lib
@@ -140,15 +140,15 @@ 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: '0'
148
+ version: 1.3.1
149
149
  requirements: []
150
- rubygems_version: 3.2.2
151
- signing_key:
150
+ rubygems_version: 3.1.6
151
+ signing_key:
152
152
  specification_version: 4
153
153
  summary: Minimal embedded v8 for Ruby
154
154
  test_files: []