ruby_box 0.0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 24712c9c81020b44d9311a1cb883279c29d2cbd2
4
- data.tar.gz: a5bdfa62602acb7c577cd4c8f4248ac010fd6f1d
3
+ metadata.gz: ad18cf425468583ca9d68b028decf40c0cab4c8d
4
+ data.tar.gz: a396ac061daca869cc7614703ab9446a2fd2ab6e
5
5
  SHA512:
6
- metadata.gz: 2c08690552b840b62dc8566d827b2cc218fbb7e4c38803fc3eade15fae4b5d37f08d2eba6bdf421c0ca6f2a4ea57c80ec67091ffcfe2e4d61974817087b479d6
7
- data.tar.gz: 743bf09d576224b268caebdd9dc95dd2d54acf2df7fa35c247d746cc1d46cc7960a591ef56351b43af41946b1d2e11e12b7c2357b89fc77409301f8eed76d120
6
+ metadata.gz: 276d89862d95255ef094c8f906c154be97ff20e1d31d7139ea3f183f5a79eca6bec87a05ca06daa5321996c5a9ae5493347b5106cc13870373520088987fa02d
7
+ data.tar.gz: e8959d5af99de60849293ec31e7ecfee684006f22a6856f849f06f89b4cd0a90ca52fb5bc9e975372e01f22ce2bc317ed4b74f5b96fd54136510f3c0a23057cc
@@ -42,7 +42,7 @@ module RubyBox
42
42
  handle = "Opal.RubyBox.CurrentBoxProxy.__exposed_method_#{method_name}"
43
43
 
44
44
  wrapper = ->(serialized_args) do
45
- args = JSON.parse(serialized_args)
45
+ args = JSON.parse(serialized_args, quirks_mode: true)
46
46
  value = send(method_name, *args)
47
47
  value.to_json
48
48
  end
@@ -1,3 +1,3 @@
1
1
  module RubyBox
2
- VERSION = '0.0.0.1'
2
+ VERSION = '1.0.0'
3
3
  end
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_box
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alec Larsen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-26 00:00:00.000000000 Z
11
+ date: 2017-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: libv8
14
+ name: mini_racer
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.1'
19
+ version: '0.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.1'
27
- - !ruby/object:Gem::Dependency
28
- name: rake-compiler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
26
+ version: '0.1'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: opal
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -70,16 +56,16 @@ dependencies:
70
56
  name: bundler
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - "~>"
59
+ - - ">="
74
60
  - !ruby/object:Gem::Version
75
- version: '1.10'
61
+ version: '0'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - "~>"
66
+ - - ">="
81
67
  - !ruby/object:Gem::Version
82
- version: '1.10'
68
+ version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: rake
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -154,8 +140,7 @@ description:
154
140
  email:
155
141
  - aleclarsen42@gmail.com
156
142
  executables: []
157
- extensions:
158
- - vendor/gems/mini_racer/ext/mini_racer_extension/extconf.rb
143
+ extensions: []
159
144
  extra_rdoc_files: []
160
145
  files:
161
146
  - lib/ruby_box.rb
@@ -171,11 +156,6 @@ files:
171
156
  - lib/ruby_box/thread_safety.rb
172
157
  - lib/ruby_box/timeout_error.rb
173
158
  - lib/ruby_box/version.rb
174
- - vendor/gems/mini_racer/ext/mini_racer_extension/extconf.rb
175
- - vendor/gems/mini_racer/ext/mini_racer_extension/mini_racer_extension.cc
176
- - vendor/gems/mini_racer/lib/mini_racer.rb
177
- - vendor/gems/mini_racer/lib/mini_racer/version.rb
178
- - vendor/gems/mini_racer/lib/mini_racer_extension.bundle
179
159
  homepage: https://github.com/anarchocurious/ruby_box
180
160
  licenses:
181
161
  - MIT
@@ -184,8 +164,6 @@ post_install_message:
184
164
  rdoc_options: []
185
165
  require_paths:
186
166
  - lib
187
- - vendor/gems/mini_racer/lib
188
- - vendor/gems/mini_racer/ext
189
167
  required_ruby_version: !ruby/object:Gem::Requirement
190
168
  requirements:
191
169
  - - ">="
@@ -198,9 +176,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
176
  version: '0'
199
177
  requirements: []
200
178
  rubyforge_project:
201
- rubygems_version: 2.4.6
179
+ rubygems_version: 2.5.1
202
180
  signing_key:
203
181
  specification_version: 4
204
182
  summary: RubyBox allows the execution of untrusted Ruby code safely in a sandbox.
205
183
  test_files: []
206
- has_rdoc:
@@ -1,54 +0,0 @@
1
- require 'mkmf'
2
- require 'libv8'
3
-
4
- have_library('pthread')
5
- have_library('objc') if RUBY_PLATFORM =~ /darwin/
6
- $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
7
- $CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
8
- $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
9
- $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or RUBY_PLATFORM =~ /darwin/
10
- $CPPFLAGS += " -std=c++0x"
11
- $CPPFLAGS += " -fpermissive"
12
-
13
- $LDFLAGS.insert 0, " -stdlib=libstdc++ " if RUBY_PLATFORM =~ /darwin/
14
-
15
- if ENV['CXX']
16
- puts "SETTING CXX"
17
- CONFIG['CXX'] = ENV['CXX']
18
- end
19
-
20
- CXX11_TEST = <<EOS
21
- #if __cplusplus <= 199711L
22
- # error A compiler that supports at least C++11 is required in order to compile this project.
23
- #endif
24
- EOS
25
-
26
- `echo "#{CXX11_TEST}" | #{CONFIG['CXX']} -std=c++0x -x c++ -E -`
27
- unless $?.success?
28
- warn <<EOS
29
-
30
-
31
- WARNING: C++11 support is required for compiling mini_racer. Please make sure
32
- you are using a compiler that supports at least C++11. Examples of such
33
- compilers are GCC 4.7+ and Clang 3.2+.
34
-
35
- If you are using Travis, consider either migrating your bulid to Ubuntu Trusty or
36
- installing GCC 4.8. See mini_racer's README.md for more information.
37
-
38
-
39
- EOS
40
- end
41
-
42
- CONFIG['LDSHARED'] = '$(CXX) -shared' unless RUBY_PLATFORM =~ /darwin/
43
- if CONFIG['warnflags']
44
- CONFIG['warnflags'].gsub!('-Wdeclaration-after-statement', '')
45
- CONFIG['warnflags'].gsub!('-Wimplicit-function-declaration', '')
46
- end
47
-
48
- if enable_config('debug')
49
- CONFIG['debugflags'] << ' -ggdb3 -O0'
50
- end
51
-
52
- Libv8.configure_makefile
53
-
54
- create_makefile 'mini_racer_extension'
@@ -1,910 +0,0 @@
1
- #include <stdio.h>
2
- #include <ruby.h>
3
- #include <ruby/thread.h>
4
- #include <v8.h>
5
- #include <libplatform/libplatform.h>
6
- #include <ruby/encoding.h>
7
- #include <pthread.h>
8
- #include <unistd.h>
9
- #include <mutex>
10
-
11
- using namespace v8;
12
-
13
- class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
14
- public:
15
- virtual void* Allocate(size_t length) {
16
- void* data = AllocateUninitialized(length);
17
- return data == NULL ? data : memset(data, 0, length);
18
- }
19
- virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
20
- virtual void Free(void* data, size_t) { free(data); }
21
- };
22
-
23
- typedef struct {
24
- const char* data;
25
- int raw_size;
26
- } SnapshotInfo;
27
-
28
- typedef struct {
29
- Isolate* isolate;
30
- ArrayBufferAllocator* allocator;
31
- StartupData* startup_data;
32
- bool interrupted;
33
-
34
- // how many references to this isolate exist
35
- // we can't rely on Ruby's GC for this, because when destroying
36
- // objects, Ruby will destroy ruby objects first, then call the
37
- // extenstion's deallocators. In this case, that means it would
38
- // call `deallocate_isolate` _before_ `deallocate`, causing a segfault
39
- int refs_count;
40
- } IsolateInfo;
41
-
42
- typedef struct {
43
- IsolateInfo* isolate_info;
44
- Persistent<Context>* context;
45
- } ContextInfo;
46
-
47
- typedef struct {
48
- bool parsed;
49
- bool executed;
50
- bool terminated;
51
- Persistent<Value>* value;
52
- Persistent<Value>* message;
53
- Persistent<Value>* backtrace;
54
- } EvalResult;
55
-
56
- typedef struct {
57
- ContextInfo* context_info;
58
- Local<String>* eval;
59
- useconds_t timeout;
60
- EvalResult* result;
61
- } EvalParams;
62
-
63
- static VALUE rb_eScriptTerminatedError;
64
- static VALUE rb_eParseError;
65
- static VALUE rb_eScriptRuntimeError;
66
- static VALUE rb_cJavaScriptFunction;
67
- static VALUE rb_eSnapshotError;
68
- static VALUE rb_ePlatformAlreadyInitializedError;
69
-
70
- static VALUE rb_cFailedV8Conversion;
71
- static VALUE rb_cDateTime = Qnil;
72
-
73
- static Platform* current_platform = NULL;
74
- static std::mutex platform_lock;
75
-
76
- static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
77
- bool platform_already_initialized = false;
78
-
79
- platform_lock.lock();
80
-
81
- if (current_platform == NULL) {
82
- V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
83
- } else {
84
- platform_already_initialized = true;
85
- }
86
-
87
- platform_lock.unlock();
88
-
89
- // important to raise outside of the lock
90
- if (platform_already_initialized) {
91
- rb_raise(rb_ePlatformAlreadyInitializedError, "The V8 platform is already initialized");
92
- }
93
-
94
- return Qnil;
95
- }
96
-
97
- static void init_v8() {
98
- // no need to wait for the lock if already initialized
99
- if (current_platform != NULL) return;
100
-
101
- platform_lock.lock();
102
-
103
- if (current_platform == NULL) {
104
- V8::InitializeICU();
105
- current_platform = platform::CreateDefaultPlatform();
106
- V8::InitializePlatform(current_platform);
107
- V8::Initialize();
108
- }
109
-
110
- platform_lock.unlock();
111
- }
112
-
113
- void*
114
- nogvl_context_eval(void* arg) {
115
-
116
- EvalParams* eval_params = (EvalParams*)arg;
117
- EvalResult* result = eval_params->result;
118
- Isolate* isolate = eval_params->context_info->isolate_info->isolate;
119
- Isolate::Scope isolate_scope(isolate);
120
- HandleScope handle_scope(isolate);
121
-
122
- TryCatch trycatch(isolate);
123
-
124
- Local<Context> context = eval_params->context_info->context->Get(isolate);
125
- Context::Scope context_scope(context);
126
-
127
- // in gvl flag
128
- isolate->SetData(0, (void*)false);
129
- // terminate ASAP
130
- isolate->SetData(1, (void*)false);
131
-
132
- MaybeLocal<Script> parsed_script = Script::Compile(context, *eval_params->eval);
133
- result->parsed = !parsed_script.IsEmpty();
134
- result->executed = false;
135
- result->terminated = false;
136
- result->value = NULL;
137
-
138
- if (!result->parsed) {
139
- result->message = new Persistent<Value>();
140
- result->message->Reset(isolate, trycatch.Exception());
141
- } else {
142
-
143
- MaybeLocal<Value> maybe_value = parsed_script.ToLocalChecked()->Run(context);
144
-
145
- result->executed = !maybe_value.IsEmpty();
146
-
147
- if (result->executed) {
148
- Persistent<Value>* persistent = new Persistent<Value>();
149
- persistent->Reset(isolate, maybe_value.ToLocalChecked());
150
- result->value = persistent;
151
- }
152
- }
153
-
154
- if (!result->executed || !result->parsed) {
155
- if (trycatch.HasCaught()) {
156
- if (!trycatch.Exception()->IsNull()) {
157
- result->message = new Persistent<Value>();
158
- Local<Message> message = trycatch.Message();
159
- char buf[1000];
160
- int len;
161
- len = snprintf(buf, sizeof(buf), "%s at %s:%i:%i", *String::Utf8Value(message->Get()),
162
- *String::Utf8Value(message->GetScriptResourceName()->ToString()),
163
- message->GetLineNumber(),
164
- message->GetStartColumn());
165
-
166
- Local<String> v8_message = String::NewFromUtf8(isolate, buf, NewStringType::kNormal, (int)len).ToLocalChecked();
167
- result->message->Reset(isolate, v8_message);
168
- } else if(trycatch.HasTerminated()) {
169
-
170
-
171
- result->terminated = true;
172
- result->message = new Persistent<Value>();
173
- Local<String> tmp = String::NewFromUtf8(isolate, "JavaScript was terminated (either by timeout or explicitly)");
174
- result->message->Reset(isolate, tmp);
175
- }
176
- if (!trycatch.StackTrace().IsEmpty()) {
177
- result->backtrace = new Persistent<Value>();
178
- result->backtrace->Reset(isolate, trycatch.StackTrace()->ToString());
179
- }
180
- }
181
- }
182
-
183
- isolate->SetData(0, (void*)true);
184
-
185
-
186
- return NULL;
187
- }
188
-
189
- static VALUE convert_v8_to_ruby(Isolate* isolate, Handle<Value> &value) {
190
-
191
- HandleScope scope(isolate);
192
-
193
- if (value->IsNull() || value->IsUndefined()){
194
- return Qnil;
195
- }
196
-
197
- if (value->IsInt32()) {
198
- return INT2FIX(value->Int32Value());
199
- }
200
-
201
- if (value->IsNumber()) {
202
- return rb_float_new(value->NumberValue());
203
- }
204
-
205
- if (value->IsTrue()) {
206
- return Qtrue;
207
- }
208
-
209
- if (value->IsFalse()) {
210
- return Qfalse;
211
- }
212
-
213
- if (value->IsArray()) {
214
- VALUE rb_array = rb_ary_new();
215
- Local<Array> arr = Local<Array>::Cast(value);
216
- for(uint32_t i=0; i < arr->Length(); i++) {
217
- Local<Value> element = arr->Get(i);
218
- VALUE rb_elem = convert_v8_to_ruby(isolate, element);
219
- if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
220
- return rb_elem;
221
- }
222
- rb_ary_push(rb_array, rb_elem);
223
- }
224
- return rb_array;
225
- }
226
-
227
- if (value->IsFunction()){
228
- return rb_funcall(rb_cJavaScriptFunction, rb_intern("new"), 0);
229
- }
230
-
231
- if (value->IsDate()){
232
- double ts = Local<Date>::Cast(value)->ValueOf();
233
- double secs = ts/1000;
234
- long nanos = round((secs - floor(secs)) * 1000000);
235
-
236
- return rb_time_new(secs, nanos);
237
- }
238
-
239
- if (value->IsObject()) {
240
-
241
- TryCatch trycatch(isolate);
242
-
243
- VALUE rb_hash = rb_hash_new();
244
- Local<Context> context = Context::New(isolate);
245
- Local<Object> object = value->ToObject();
246
- MaybeLocal<Array> maybe_props = object->GetOwnPropertyNames(context);
247
- if (!maybe_props.IsEmpty()) {
248
- Local<Array> props = maybe_props.ToLocalChecked();
249
- for(uint32_t i=0; i < props->Length(); i++) {
250
- Local<Value> key = props->Get(i);
251
- VALUE rb_key = convert_v8_to_ruby(isolate, key);
252
- Local<Value> value = object->Get(key);
253
- // this may have failed due to Get raising
254
-
255
- if (trycatch.HasCaught()) {
256
- // TODO isolate code that translates execption to ruby
257
- // exception so we can properly return it
258
- return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
259
- }
260
-
261
- VALUE rb_value = convert_v8_to_ruby(isolate, value);
262
- rb_hash_aset(rb_hash, rb_key, rb_value);
263
- }
264
- }
265
- return rb_hash;
266
- }
267
-
268
- Local<String> rstr = value->ToString();
269
- return rb_enc_str_new(*String::Utf8Value(rstr), rstr->Utf8Length(), rb_enc_find("utf-8"));
270
- }
271
-
272
- static Handle<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value) {
273
- EscapableHandleScope scope(isolate);
274
-
275
- Local<Array> array;
276
- Local<Object> object;
277
- VALUE hash_as_array;
278
- VALUE pair;
279
- int i;
280
- long length;
281
- long fixnum;
282
- VALUE klass;
283
-
284
- switch (TYPE(value)) {
285
- case T_FIXNUM:
286
- fixnum = NUM2LONG(value);
287
- if (fixnum > INT_MAX)
288
- {
289
- return scope.Escape(Number::New(isolate, (double)fixnum));
290
- }
291
- return scope.Escape(Integer::New(isolate, (int)fixnum));
292
- case T_FLOAT:
293
- return scope.Escape(Number::New(isolate, NUM2DBL(value)));
294
- case T_STRING:
295
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
296
- case T_NIL:
297
- return scope.Escape(Null(isolate));
298
- case T_TRUE:
299
- return scope.Escape(True(isolate));
300
- case T_FALSE:
301
- return scope.Escape(False(isolate));
302
- case T_ARRAY:
303
- length = RARRAY_LEN(value);
304
- array = Array::New(isolate, (int)length);
305
- for(i=0; i<length; i++) {
306
- array->Set(i, convert_ruby_to_v8(isolate, rb_ary_entry(value, i)));
307
- }
308
- return scope.Escape(array);
309
- case T_HASH:
310
- object = Object::New(isolate);
311
- hash_as_array = rb_funcall(value, rb_intern("to_a"), 0);
312
- length = RARRAY_LEN(hash_as_array);
313
- for(i=0; i<length; i++) {
314
- pair = rb_ary_entry(hash_as_array, i);
315
- object->Set(convert_ruby_to_v8(isolate, rb_ary_entry(pair, 0)),
316
- convert_ruby_to_v8(isolate, rb_ary_entry(pair, 1)));
317
- }
318
- return scope.Escape(object);
319
- case T_SYMBOL:
320
- value = rb_funcall(value, rb_intern("to_s"), 0);
321
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
322
- case T_DATA:
323
- klass = rb_funcall(value, rb_intern("class"), 0);
324
- if (klass == rb_cTime || klass == rb_cDateTime)
325
- {
326
- if (klass == rb_cDateTime)
327
- {
328
- value = rb_funcall(value, rb_intern("to_time"), 0);
329
- }
330
- value = rb_funcall(value, rb_intern("to_f"), 0);
331
- return scope.Escape(Date::New(isolate, NUM2DBL(value) * 1000));
332
- }
333
- case T_OBJECT:
334
- case T_CLASS:
335
- case T_ICLASS:
336
- case T_MODULE:
337
- case T_REGEXP:
338
- case T_MATCH:
339
- case T_STRUCT:
340
- case T_BIGNUM:
341
- case T_FILE:
342
- case T_UNDEF:
343
- case T_NODE:
344
- default:
345
- return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
346
- }
347
-
348
- }
349
-
350
- static void unblock_eval(void *ptr) {
351
- EvalParams* eval = (EvalParams*)ptr;
352
- eval->context_info->isolate_info->interrupted = true;
353
- }
354
-
355
- static VALUE rb_snapshot_size(VALUE self, VALUE str) {
356
- SnapshotInfo* snapshot_info;
357
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
358
-
359
- return INT2NUM(snapshot_info->raw_size);
360
- }
361
-
362
- static VALUE rb_snapshot_load(VALUE self, VALUE str) {
363
- SnapshotInfo* snapshot_info;
364
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
365
-
366
- init_v8();
367
-
368
- StartupData startup_data = V8::CreateSnapshotDataBlob(RSTRING_PTR(str));
369
-
370
- if (startup_data.data == NULL && startup_data.raw_size == 0) {
371
- rb_raise(rb_eSnapshotError, "Could not create snapshot, most likely the source is incorrect");
372
- }
373
-
374
- snapshot_info->data = startup_data.data;
375
- snapshot_info->raw_size = startup_data.raw_size;
376
-
377
- return Qnil;
378
- }
379
-
380
- static VALUE rb_snapshot_warmup(VALUE self, VALUE str) {
381
- SnapshotInfo* snapshot_info;
382
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
383
-
384
- init_v8();
385
-
386
- StartupData cold_startup_data = {snapshot_info->data, snapshot_info->raw_size};
387
- StartupData warm_startup_data = V8::WarmUpSnapshotDataBlob(cold_startup_data, RSTRING_PTR(str));
388
-
389
- if (warm_startup_data.data == NULL && warm_startup_data.raw_size == 0) {
390
- rb_raise(rb_eSnapshotError, "Could not warm up snapshot, most likely the source is incorrect");
391
- } else {
392
- delete[] snapshot_info->data;
393
-
394
- snapshot_info->data = warm_startup_data.data;
395
- snapshot_info->raw_size = warm_startup_data.raw_size;
396
- }
397
-
398
- return self;
399
- }
400
-
401
- static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
402
- IsolateInfo* isolate_info;
403
- Data_Get_Struct(self, IsolateInfo, isolate_info);
404
-
405
- init_v8();
406
-
407
- isolate_info->allocator = new ArrayBufferAllocator();
408
- isolate_info->interrupted = false;
409
- isolate_info->refs_count = 1;
410
-
411
- Isolate::CreateParams create_params;
412
- create_params.array_buffer_allocator = isolate_info->allocator;
413
-
414
- StartupData* startup_data = NULL;
415
- if (!NIL_P(snapshot)) {
416
- SnapshotInfo* snapshot_info;
417
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
418
-
419
- int raw_size = snapshot_info->raw_size;
420
- char* data = new char[raw_size];
421
- memcpy(data, snapshot_info->data, sizeof(char) * raw_size);
422
-
423
- startup_data = new StartupData;
424
- startup_data->data = data;
425
- startup_data->raw_size = raw_size;
426
-
427
- create_params.snapshot_blob = startup_data;
428
- }
429
-
430
- isolate_info->startup_data = startup_data;
431
- isolate_info->isolate = Isolate::New(create_params);
432
-
433
- return Qnil;
434
- }
435
-
436
- static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
437
- IsolateInfo* isolate_info;
438
- Data_Get_Struct(self, IsolateInfo, isolate_info);
439
-
440
- return isolate_info->isolate->IdleNotification(NUM2INT(idle_time_in_ms)) ? Qtrue : Qfalse;
441
- }
442
-
443
- static VALUE rb_context_init_with_isolate(VALUE self, VALUE isolate) {
444
- ContextInfo* context_info;
445
- Data_Get_Struct(self, ContextInfo, context_info);
446
-
447
- init_v8();
448
-
449
- IsolateInfo* isolate_info;
450
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
451
-
452
- context_info->isolate_info = isolate_info;
453
- isolate_info->refs_count++;
454
-
455
- {
456
- Locker lock(isolate_info->isolate);
457
- Isolate::Scope isolate_scope(isolate_info->isolate);
458
- HandleScope handle_scope(isolate_info->isolate);
459
-
460
- Local<Context> context = Context::New(isolate_info->isolate);
461
-
462
- context_info->context = new Persistent<Context>();
463
- context_info->context->Reset(isolate_info->isolate, context);
464
- }
465
-
466
- if (Qnil == rb_cDateTime && rb_funcall(rb_cObject, rb_intern("const_defined?"), 1, rb_str_new2("DateTime")) == Qtrue)
467
- {
468
- rb_cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
469
- }
470
-
471
- return Qnil;
472
- }
473
-
474
- static VALUE rb_context_eval_unsafe(VALUE self, VALUE str) {
475
-
476
- EvalParams eval_params;
477
- EvalResult eval_result;
478
- ContextInfo* context_info;
479
- VALUE result;
480
-
481
- VALUE message = Qnil;
482
- VALUE backtrace = Qnil;
483
-
484
- Data_Get_Struct(self, ContextInfo, context_info);
485
- Isolate* isolate = context_info->isolate_info->isolate;
486
-
487
-
488
-
489
- {
490
- Locker lock(isolate);
491
- Isolate::Scope isolate_scope(isolate);
492
- HandleScope handle_scope(isolate);
493
-
494
- Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
495
- NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
496
-
497
- eval_params.context_info = context_info;
498
- eval_params.eval = &eval;
499
- eval_params.result = &eval_result;
500
- eval_params.timeout = 0;
501
- VALUE timeout = rb_iv_get(self, "@timeout");
502
- if (timeout != Qnil) {
503
- eval_params.timeout = (useconds_t)NUM2LONG(timeout);
504
- }
505
-
506
- eval_result.message = NULL;
507
- eval_result.backtrace = NULL;
508
-
509
- rb_thread_call_without_gvl(nogvl_context_eval, &eval_params, unblock_eval, &eval_params);
510
-
511
- if (eval_result.message != NULL) {
512
- Local<Value> tmp = Local<Value>::New(isolate, *eval_result.message);
513
- message = convert_v8_to_ruby(isolate, tmp);
514
- eval_result.message->Reset();
515
- delete eval_result.message;
516
- }
517
-
518
- if (eval_result.backtrace != NULL) {
519
- Local<Value> tmp = Local<Value>::New(isolate, *eval_result.backtrace);
520
- backtrace = convert_v8_to_ruby(isolate, tmp);
521
- eval_result.backtrace->Reset();
522
- delete eval_result.backtrace;
523
- }
524
- }
525
-
526
- // NOTE: this is very important, we can not do an rb_raise from within
527
- // a v8 scope, if we do the scope is never cleaned up properly and we leak
528
- if (!eval_result.parsed) {
529
- if(TYPE(message) == T_STRING) {
530
- rb_raise(rb_eParseError, "%s", RSTRING_PTR(message));
531
- } else {
532
- rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
533
- }
534
- }
535
-
536
- if (!eval_result.executed) {
537
- VALUE ruby_exception = rb_iv_get(self, "@current_exception");
538
- if (ruby_exception == Qnil) {
539
- ruby_exception = eval_result.terminated ? rb_eScriptTerminatedError : rb_eScriptRuntimeError;
540
- // exception report about what happened
541
- if(TYPE(backtrace) == T_STRING) {
542
- rb_raise(ruby_exception, "%s", RSTRING_PTR(backtrace));
543
- } else if(TYPE(message) == T_STRING) {
544
- rb_raise(ruby_exception, "%s", RSTRING_PTR(message));
545
- } else {
546
- rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
547
- }
548
- } else {
549
- VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
550
- rb_raise(CLASS_OF(ruby_exception), "%s", RSTRING_PTR(rb_str));
551
- }
552
- }
553
-
554
- // New scope for return value, must release GVL which
555
- {
556
- Locker lock(isolate);
557
- Isolate::Scope isolate_scope(isolate);
558
- HandleScope handle_scope(isolate);
559
-
560
- Local<Value> tmp = Local<Value>::New(isolate, *eval_result.value);
561
- result = convert_v8_to_ruby(isolate, tmp);
562
-
563
- eval_result.value->Reset();
564
- delete eval_result.value;
565
- }
566
-
567
- if (rb_funcall(result, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
568
- // TODO try to recover stack trace from the conversion error
569
- rb_raise(rb_eScriptRuntimeError, "Error converting JS object to Ruby object");
570
- }
571
-
572
-
573
- return result;
574
- }
575
-
576
- typedef struct {
577
- VALUE callback;
578
- int length;
579
- VALUE* args;
580
- bool failed;
581
- } protected_callback_data;
582
-
583
- static
584
- VALUE protected_callback(VALUE rdata) {
585
- protected_callback_data* data = (protected_callback_data*)rdata;
586
- VALUE result;
587
-
588
- if (data->length > 0) {
589
- result = rb_funcall2(data->callback, rb_intern("call"), data->length, data->args);
590
- } else {
591
- result = rb_funcall(data->callback, rb_intern("call"), 0);
592
- }
593
- return result;
594
- }
595
-
596
- static
597
- VALUE rescue_callback(VALUE rdata, VALUE exception) {
598
- protected_callback_data* data = (protected_callback_data*)rdata;
599
- data->failed = true;
600
- return exception;
601
- }
602
-
603
- void*
604
- gvl_ruby_callback(void* data) {
605
-
606
- FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
607
- VALUE* ruby_args = NULL;
608
- int length = args->Length();
609
- VALUE callback;
610
- VALUE result;
611
- VALUE self;
612
-
613
- {
614
- HandleScope scope(args->GetIsolate());
615
- Handle<External> external = Handle<External>::Cast(args->Data());
616
-
617
- VALUE* self_pointer = (VALUE*)(external->Value());
618
- self = *self_pointer;
619
- callback = rb_iv_get(self, "@callback");
620
-
621
- if (length > 0) {
622
- ruby_args = ALLOC_N(VALUE, length);
623
- }
624
-
625
-
626
- for (int i = 0; i < length; i++) {
627
- Local<Value> value = ((*args)[i]).As<Value>();
628
- ruby_args[i] = convert_v8_to_ruby(args->GetIsolate(), value);
629
- }
630
- }
631
-
632
- // may raise exception stay clear of handle scope
633
- protected_callback_data callback_data;
634
- callback_data.length = length;
635
- callback_data.callback = callback;
636
- callback_data.args = ruby_args;
637
- callback_data.failed = false;
638
-
639
- if ((bool)args->GetIsolate()->GetData(1) == true) {
640
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during tansition from Ruby to JS"));
641
- V8::TerminateExecution(args->GetIsolate());
642
- return NULL;
643
- }
644
-
645
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
646
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
647
-
648
- if(callback_data.failed) {
649
- VALUE parent = rb_iv_get(self, "@parent");
650
- rb_iv_set(parent, "@current_exception", result);
651
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Ruby exception"));
652
- }
653
- else {
654
- HandleScope scope(args->GetIsolate());
655
- Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), result);
656
- args->GetReturnValue().Set(v8_result);
657
- }
658
-
659
- if (length > 0) {
660
- xfree(ruby_args);
661
- }
662
-
663
- if ((bool)args->GetIsolate()->GetData(1) == true) {
664
- Isolate* isolate = args->GetIsolate();
665
- V8::TerminateExecution(isolate);
666
- }
667
-
668
- return NULL;
669
- }
670
-
671
- static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
672
-
673
- bool has_gvl = (bool)args.GetIsolate()->GetData(0);
674
-
675
- if(has_gvl) {
676
- gvl_ruby_callback((void*)&args);
677
- } else {
678
- rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
679
- }
680
- }
681
-
682
-
683
- static VALUE rb_external_function_notify_v8(VALUE self) {
684
-
685
- ContextInfo* context_info;
686
-
687
- VALUE parent = rb_iv_get(self, "@parent");
688
- VALUE name = rb_iv_get(self, "@name");
689
- VALUE parent_object = rb_iv_get(self, "@parent_object");
690
- VALUE parent_object_eval = rb_iv_get(self, "@parent_object_eval");
691
-
692
- bool parse_error = false;
693
- bool attach_error = false;
694
-
695
- Data_Get_Struct(parent, ContextInfo, context_info);
696
- Isolate* isolate = context_info->isolate_info->isolate;
697
-
698
- {
699
- Locker lock(isolate);
700
- Isolate::Scope isolate_scope(isolate);
701
- HandleScope handle_scope(isolate);
702
-
703
- Local<Context> context = context_info->context->Get(isolate);
704
- Context::Scope context_scope(context);
705
-
706
- Local<String> v8_str = String::NewFromUtf8(isolate, RSTRING_PTR(name),
707
- NewStringType::kNormal, (int)RSTRING_LEN(name)).ToLocalChecked();
708
-
709
- // copy self so we can access from v8 external
710
- VALUE* self_copy;
711
- Data_Get_Struct(self, VALUE, self_copy);
712
- *self_copy = self;
713
-
714
- Local<Value> external = External::New(isolate, self_copy);
715
-
716
- if (parent_object == Qnil) {
717
- context->Global()->Set(v8_str, FunctionTemplate::New(isolate, ruby_callback, external)->GetFunction());
718
- } else {
719
-
720
- Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
721
- NewStringType::kNormal, (int)RSTRING_LEN(parent_object_eval)).ToLocalChecked();
722
-
723
- MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
724
- if (parsed_script.IsEmpty()) {
725
- parse_error = true;
726
- } else {
727
- MaybeLocal<Value> maybe_value = parsed_script.ToLocalChecked()->Run(context);
728
- attach_error = true;
729
-
730
- if (!maybe_value.IsEmpty()) {
731
- Local<Value> value = maybe_value.ToLocalChecked();
732
- if (value->IsObject()){
733
- value.As<Object>()->Set(v8_str, FunctionTemplate::New(isolate, ruby_callback, external)->GetFunction());
734
- attach_error = false;
735
- }
736
- }
737
- }
738
- }
739
- }
740
-
741
- // always raise out of V8 context
742
- if (parse_error) {
743
- rb_raise(rb_eParseError, "Invalid object %s", RSTRING_PTR(parent_object));
744
- }
745
-
746
- if (attach_error) {
747
- rb_raise(rb_eParseError, "Was expecting %s to be an object", RSTRING_PTR(parent_object));
748
- }
749
-
750
- return Qnil;
751
- }
752
-
753
- void maybe_free_isolate_info(IsolateInfo* isolate_info) {
754
- // an isolate can only be freed if no Isolate or Context (ruby) object
755
- // still need it
756
- if (isolate_info == NULL || isolate_info->refs_count > 0) {
757
- return;
758
- }
759
-
760
- if (isolate_info->isolate) {
761
- Locker lock(isolate_info->isolate);
762
- }
763
-
764
- if (isolate_info->isolate) {
765
- if (isolate_info->interrupted) {
766
- fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.");
767
- } else {
768
-
769
- isolate_info->isolate->Dispose();
770
- }
771
- isolate_info->isolate = NULL;
772
- }
773
-
774
- if (isolate_info->startup_data) {
775
- delete[] isolate_info->startup_data->data;
776
- delete isolate_info->startup_data;
777
- }
778
-
779
- delete isolate_info->allocator;
780
- xfree(isolate_info);
781
- }
782
-
783
- void deallocate_isolate(void* data) {
784
- IsolateInfo* isolate_info = (IsolateInfo*) data;
785
-
786
- isolate_info->refs_count--;
787
-
788
- maybe_free_isolate_info(isolate_info);
789
- }
790
-
791
- void deallocate(void* data) {
792
- ContextInfo* context_info = (ContextInfo*)data;
793
- IsolateInfo* isolate_info = context_info->isolate_info;
794
-
795
- if (context_info->context && isolate_info && isolate_info->isolate) {
796
- Locker lock(isolate_info->isolate);
797
- v8::Isolate::Scope isolate_scope(isolate_info->isolate);
798
- context_info->context->Reset();
799
- delete context_info->context;
800
- }
801
-
802
- if (isolate_info) {
803
- isolate_info->refs_count--;
804
- maybe_free_isolate_info(isolate_info);
805
- }
806
- }
807
-
808
- void deallocate_external_function(void * data) {
809
- xfree(data);
810
- }
811
-
812
- void deallocate_snapshot(void * data) {
813
- SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
814
-
815
- delete[] snapshot_info->data;
816
-
817
- xfree(snapshot_info);
818
- }
819
-
820
- VALUE allocate_external_function(VALUE klass) {
821
- VALUE* self = ALLOC(VALUE);
822
- return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
823
- }
824
-
825
- VALUE allocate(VALUE klass) {
826
- ContextInfo* context_info = ALLOC(ContextInfo);
827
- context_info->isolate_info = NULL;
828
- context_info->context = NULL;
829
-
830
- return Data_Wrap_Struct(klass, NULL, deallocate, (void*)context_info);
831
- }
832
-
833
- VALUE allocate_snapshot(VALUE klass) {
834
- SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
835
- snapshot_info->data = NULL;
836
- snapshot_info->raw_size = 0;
837
-
838
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
839
- }
840
-
841
- VALUE allocate_isolate(VALUE klass) {
842
- IsolateInfo* isolate_info = ALLOC(IsolateInfo);
843
-
844
- isolate_info->isolate = NULL;
845
- isolate_info->allocator = NULL;
846
- isolate_info->startup_data = NULL;
847
- isolate_info->interrupted = false;
848
- isolate_info->refs_count = 0;
849
-
850
- return Data_Wrap_Struct(klass, NULL, deallocate_isolate, (void*)isolate_info);
851
- }
852
-
853
- static VALUE
854
- rb_context_stop(VALUE self) {
855
-
856
- ContextInfo* context_info;
857
- Data_Get_Struct(self, ContextInfo, context_info);
858
-
859
- Isolate* isolate = context_info->isolate_info->isolate;
860
-
861
- // flag for termination
862
- isolate->SetData(1, (void*)true);
863
-
864
- V8::TerminateExecution(isolate);
865
- rb_funcall(self, rb_intern("stop_attached"), 0);
866
-
867
- return Qnil;
868
- }
869
-
870
- extern "C" {
871
-
872
- void Init_mini_racer_extension ( void )
873
- {
874
- VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
875
- VALUE rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
876
- VALUE rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
877
- VALUE rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
878
- VALUE rb_cPlatform = rb_define_class_under(rb_mMiniRacer, "Platform", rb_cObject);
879
-
880
- VALUE rb_eEvalError = rb_define_class_under(rb_mMiniRacer, "EvalError", rb_eStandardError);
881
- rb_eScriptTerminatedError = rb_define_class_under(rb_mMiniRacer, "ScriptTerminatedError", rb_eEvalError);
882
- rb_eParseError = rb_define_class_under(rb_mMiniRacer, "ParseError", rb_eEvalError);
883
- rb_eScriptRuntimeError = rb_define_class_under(rb_mMiniRacer, "RuntimeError", rb_eEvalError);
884
- rb_cJavaScriptFunction = rb_define_class_under(rb_mMiniRacer, "JavaScriptFunction", rb_cObject);
885
- rb_eSnapshotError = rb_define_class_under(rb_mMiniRacer, "SnapshotError", rb_eStandardError);
886
- rb_ePlatformAlreadyInitializedError = rb_define_class_under(rb_mMiniRacer, "PlatformAlreadyInitialized", rb_eStandardError);
887
- rb_cFailedV8Conversion = rb_define_class_under(rb_mMiniRacer, "FailedV8Conversion", rb_cObject);
888
-
889
- VALUE rb_cExternalFunction = rb_define_class_under(rb_cContext, "ExternalFunction", rb_cObject);
890
- rb_define_method(rb_cContext, "stop", (VALUE(*)(...))&rb_context_stop, 0);
891
- rb_define_alloc_func(rb_cContext, allocate);
892
- rb_define_alloc_func(rb_cSnapshot, allocate_snapshot);
893
- rb_define_alloc_func(rb_cIsolate, allocate_isolate);
894
-
895
- rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 1);
896
- rb_define_private_method(rb_cContext, "init_with_isolate",(VALUE(*)(...))&rb_context_init_with_isolate, 1);
897
- rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
898
- rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
899
-
900
- rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
901
- rb_define_method(rb_cSnapshot, "warmup!", (VALUE(*)(...))&rb_snapshot_warmup, 1);
902
- rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
903
-
904
- rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
905
- rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
906
-
907
- rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
908
- }
909
-
910
- }
@@ -1,261 +0,0 @@
1
- require "mini_racer/version"
2
- require "mini_racer_extension"
3
- require "thread"
4
-
5
- module MiniRacer
6
-
7
- class EvalError < StandardError; end
8
- class ScriptTerminatedError < EvalError; end
9
- class ParseError < EvalError; end
10
- class SnapshotError < StandardError; end
11
- class PlatformAlreadyInitialized < StandardError; end
12
-
13
- class FailedV8Conversion
14
- attr_reader :info
15
- def initialize(info)
16
- @info = info
17
- end
18
- end
19
-
20
- class RuntimeError < EvalError
21
- def initialize(message)
22
- message, js_backtrace = message.split("\n", 2)
23
- if js_backtrace && !js_backtrace.empty?
24
- @js_backtrace = js_backtrace.split("\n")
25
- @js_backtrace.map!{|f| "JavaScript #{f.strip}"}
26
- else
27
- @js_backtrace = nil
28
- end
29
- super(message)
30
- end
31
-
32
- def backtrace
33
- val = super
34
- return unless val
35
- if @js_backtrace
36
- @js_backtrace + val
37
- else
38
- val
39
- end
40
- end
41
- end
42
-
43
- # helper class returned when we have a JavaScript function
44
- class JavaScriptFunction
45
- def to_s
46
- "JavaScript Function"
47
- end
48
- end
49
-
50
- class Isolate
51
- def initialize(snapshot = nil)
52
- unless snapshot.nil? || snapshot.is_a?(Snapshot)
53
- raise ArgumentError, "snapshot must be a Snapshot object, passed a #{snapshot.inspect}"
54
- end
55
-
56
- @lock = Mutex.new
57
-
58
- # defined in the C class
59
- init_with_snapshot(snapshot)
60
- end
61
-
62
- def with_lock
63
- @lock.synchronize { yield }
64
- end
65
- end
66
-
67
- class Platform
68
- class << self
69
- def set_flags!(*args, **kwargs)
70
- flags_to_strings([args, kwargs]).each do |flag|
71
- # defined in the C class
72
- set_flag_as_str!(flag)
73
- end
74
- end
75
-
76
- private
77
-
78
- def flags_to_strings(flags)
79
- flags.flatten.map { |flag| flag_to_string(flag) }.flatten
80
- end
81
-
82
- # normalize flags to strings, and adds leading dashes if needed
83
- def flag_to_string(flag)
84
- if flag.is_a?(Hash)
85
- flag.map do |key, value|
86
- "#{flag_to_string(key)} #{value}"
87
- end
88
- else
89
- str = flag.to_s
90
- str = "--#{str}" unless str.start_with?('--')
91
- str
92
- end
93
- end
94
- end
95
- end
96
-
97
- # eval is defined in the C class
98
- class Context
99
-
100
- class ExternalFunction
101
- def initialize(name, callback, parent)
102
- unless String === name
103
- raise ArgumentError, "parent_object must be a String"
104
- end
105
- parent_object, _ , @name = name.rpartition(".")
106
- @callback = callback
107
- @parent = parent
108
- @parent_object_eval = nil
109
- @parent_object = nil
110
-
111
- unless parent_object.empty?
112
- @parent_object = parent_object
113
-
114
- @parent_object_eval = ""
115
- prev = ""
116
- first = true
117
- parent_object.split(".").each do |obj|
118
- prev << obj
119
- if first
120
- @parent_object_eval << "if (typeof #{prev} === 'undefined') { #{prev} = {} };\n"
121
- else
122
- @parent_object_eval << "#{prev} = #{prev} || {};\n"
123
- end
124
- prev << "."
125
- first = false
126
- end
127
- @parent_object_eval << "#{parent_object};"
128
- end
129
- notify_v8
130
- end
131
- end
132
-
133
- attr_reader :isolate
134
-
135
- def initialize(options = nil)
136
- options ||= {}
137
-
138
- check_init_options!(options)
139
-
140
- @functions = {}
141
- @timeout = nil
142
- @current_exception = nil
143
- @timeout = options[:timeout]
144
- @isolate = options[:isolate] || Isolate.new(options[:snapshot])
145
-
146
- @callback_mutex = Mutex.new
147
- @callback_running = false
148
- @thread_raise_called = false
149
- @eval_thread = nil
150
-
151
- isolate.with_lock do
152
- # defined in the C class
153
- init_with_isolate(@isolate)
154
- end
155
- end
156
-
157
- def load(filename)
158
- # TODO do this native cause no need to allocate VALUE here
159
- eval(File.read(filename))
160
- end
161
-
162
- def eval(str)
163
- @eval_thread = Thread.current
164
- isolate.with_lock do
165
- @current_exception = nil
166
- timeout do
167
- eval_unsafe(str)
168
- end
169
- end
170
- ensure
171
- @eval_thread = nil
172
- end
173
-
174
-
175
- def attach(name, callback)
176
-
177
- wrapped = lambda do |*args|
178
- begin
179
- @callback_mutex.synchronize{
180
- @callback_running = true
181
- }
182
-
183
- callback.call(*args)
184
- ensure
185
- @callback_mutex.synchronize {
186
- @callback_running = false
187
-
188
- # this is some odd code, but it is required
189
- # if we raised on this thread we better wait for it
190
- # otherwise we may end up raising in an unsafe spot
191
- if @thread_raise_called
192
- sleep 0.1
193
- end
194
- @thread_raise_called = false
195
- }
196
- end
197
- end
198
-
199
- isolate.with_lock do
200
- external = ExternalFunction.new(name, wrapped, self)
201
- @functions["#{name}"] = external
202
- end
203
- end
204
-
205
- private
206
-
207
- def stop_attached
208
- @callback_mutex.synchronize{
209
- if @callback_running
210
- @eval_thread.raise ScriptTerminatedError, "Terminated during callback"
211
- @thread_raise_called = true
212
- end
213
- }
214
- end
215
-
216
- def timeout(&blk)
217
- return blk.call unless @timeout
218
-
219
- _,wp = IO.pipe
220
-
221
- Thread.new do
222
- begin
223
- result = IO.select([wp],[],[],(@timeout/1000.0))
224
- if !result
225
- stop
226
- end
227
- rescue
228
- STDERR.puts "FAILED TO TERMINATE DUE TO TIMEOUT"
229
- end
230
- end
231
-
232
- rval = blk.call
233
- wp.write("done")
234
-
235
- rval
236
- end
237
-
238
- def check_init_options!(options)
239
- assert_option_is_nil_or_a('isolate', options[:isolate], Isolate)
240
- assert_option_is_nil_or_a('snapshot', options[:snapshot], Snapshot)
241
-
242
- if options[:isolate] && options[:snapshot]
243
- raise ArgumentError, 'can only pass one of isolate and snapshot options'
244
- end
245
- end
246
-
247
- def assert_option_is_nil_or_a(option_name, object, klass)
248
- unless object.nil? || object.is_a?(klass)
249
- raise ArgumentError, "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
250
- end
251
- end
252
- end
253
-
254
- # `size` and `warmup!` public methods are defined in the C class
255
- class Snapshot
256
- def initialize(str = '')
257
- # defined in the C class
258
- load(str)
259
- end
260
- end
261
- end
@@ -1,3 +0,0 @@
1
- module MiniRacer
2
- VERSION = "0.1.4"
3
- end