mini_racer 0.2.6 → 0.2.11

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: 0c16fa0feb3cbf1b8421934aa90d12326e054ae439355e205f419ae1b74f6ad5
4
- data.tar.gz: ae46660f1ae64a14a528d26d53e3a1a0d7229556124cd17e0b7f5fdb16e42267
3
+ metadata.gz: 276af6ac1a39ed9f00576b34e1944c0c27c9ee745871fc50de8010f06c642abf
4
+ data.tar.gz: 911e1bf55677699df6623afec3430be7eccba88664c60e085562fb37e5a62847
5
5
  SHA512:
6
- metadata.gz: 55eaf9550322b6d3151b0dc17e86e9faadff9fe48cf64765d722281a8d6cd2af42ab8d46614f1ac59946aeffe2c02dbb09673042006c164e4eb132224acfe069
7
- data.tar.gz: 2b367ff94b68ed24579c6d750aef1532398bacedcd42a6149c16fbf176a76d079eddb12e9c3f15b12fb141703b0f38d37ca6e017e46d685826db64891347475e
6
+ metadata.gz: 17494a1abe347480cd62671155bcde45e60c8d47fee115338606a2b793308d9fe06f756da795f2b4fc84bbf8fded92ec6f6c3a656164ba195298efc4f27144e3
7
+ data.tar.gz: 0c9cc0bad0cf4963582c00d4a6ac8df547fb1b655464f01ae115ec286c864c364521bba5d70ed2081c589d007328151af34e001ad48280f18d9afeca0af879c4
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.3
4
3
  - 2.4
5
4
  - 2.5
5
+ - 2.6
6
+ - 2.7
6
7
  - ruby-head
7
8
  matrix:
8
9
  include:
data/CHANGELOG CHANGED
@@ -1,3 +1,34 @@
1
+ - 14-05-2020
2
+
3
+ - 0.2.11
4
+
5
+ - FIX: dumping heap snapshots was not flushing the file leading to corrupt snapshots
6
+ - FIX: a use-after-free shutdown crash
7
+
8
+ - 0.2.10
9
+
10
+ - 22-04-2020
11
+
12
+ - FEATURE: memory softlimit support for nogvl_context_call
13
+
14
+ - 0.2.9
15
+
16
+ - 09-01-2020
17
+
18
+ - FIX: correct segfault when JS returns a Symbol and properly cast to ruby symbol
19
+
20
+ - 0.2.8
21
+
22
+ - 11-11-2019
23
+
24
+ - FIX: ensure thread live cycle is properly accounter for following file descriptor fix
25
+
26
+ - 0.2.7
27
+
28
+ - 11-11-2019
29
+
30
+ - FIX: release the file descriptor for timeout pipe earlier (this avoids holding too many files open in Ruby 2.7)
31
+
1
32
  - 14-05-2019
2
33
 
3
34
  - 0.2.6
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MiniRacer
2
2
 
3
- [![Build Status](https://travis-ci.org/discourse/mini_racer.svg?branch=master)](https://travis-ci.org/discourse/mini_racer)
3
+ [![Build Status](https://travis-ci.org/rubyjs/mini_racer.svg?branch=master)](https://travis-ci.org/rubyjs/mini_racer)
4
4
 
5
5
  Minimal, modern embedded V8 for Ruby.
6
6
 
@@ -97,6 +97,27 @@ context.eval('bar()', filename: 'a/bar.js')
97
97
 
98
98
  ```
99
99
 
100
+ ### Fork safety
101
+
102
+ Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe.
103
+ Sadly Ruby does not have support for fork notifications per [#5446](https://bugs.ruby-lang.org/issues/5446).
104
+
105
+ If you want to ensure your application does not leak memory after fork either:
106
+
107
+ 1. Ensure no MiniRacer::Context objects are created in the master process
108
+
109
+ Or
110
+
111
+ 2. Dispose manually of all MiniRacer::Context objects prior to forking
112
+
113
+ ```ruby
114
+ # before fork
115
+
116
+ require 'objspace'
117
+ ObjectSpace.each_object(MiniRacer::Context){|c| c.dispose}
118
+
119
+ # fork here
120
+ ```
100
121
 
101
122
  ### Threadsafe
102
123
 
@@ -252,7 +273,7 @@ context.eval js
252
273
  The same code without the harmony runtime flag results in a `MiniRacer::RuntimeError: RangeError: Maximum call stack size exceeded` exception.
253
274
  Please refer to http://node.green/ as a reference on other harmony features.
254
275
 
255
- A list of all V8 runtime flags can be found using `node --v8-options`, or else by perusing [the V8 source code for flags (make sure to use the right version of V8)](https://github.com/v8/v8/blob/master/src/flag-definitions.h).
276
+ A list of all V8 runtime flags can be found using `node --v8-options`, or else by perusing [the V8 source code for flags (make sure to use the right version of V8)](https://github.com/v8/v8/blob/master/src/flags/flag-definitions.h).
256
277
 
257
278
  Note that runtime flags must be set before any other operation (e.g. creating a context, a snapshot or an isolate), otherwise an exception will be thrown.
258
279
 
@@ -426,7 +447,7 @@ Add this to your .travis.yml file:
426
447
 
427
448
  ## Contributing
428
449
 
429
- Bug reports and pull requests are welcome on GitHub at https://github.com/discourse/mini_racer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
450
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rubyjs/mini_racer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
430
451
 
431
452
 
432
453
  ## License
data/Rakefile CHANGED
@@ -16,6 +16,22 @@ Rake::ExtensionTask.new( 'mini_racer_extension', gem )
16
16
 
17
17
  # via http://blog.flavorjon.es/2009/06/easily-valgrind-gdb-your-ruby-c.html
18
18
  namespace :test do
19
+ desc "run test suite with Address Sanitizer"
20
+ task :asan do
21
+ ENV["CONFIGURE_ARGS"] = [ENV["CONFIGURE_ARGS"], '--enable-asan'].compact.join(' ')
22
+ Rake::Task['compile'].invoke
23
+
24
+ asan_path = `ldconfig -N -p |grep libasan | grep -v 32 | sed 's/.* => \\(.*\\)$/\\1/'`.chomp.split("\n")[-1]
25
+
26
+
27
+ cmdline = "env LD_PRELOAD=\"#{asan_path}\" ruby test/test_leak.rb"
28
+ puts cmdline
29
+ system cmdline
30
+
31
+ cmdline = "env LD_PRELOAD=\"#{asan_path}\" rake test"
32
+ puts cmdline
33
+ system cmdline
34
+ end
19
35
  # partial-loads-ok and undef-value-errors necessary to ignore
20
36
  # spurious (and eminently ignorable) warnings from the ruby
21
37
  # interpreter
@@ -49,10 +49,15 @@ if CONFIG['warnflags']
49
49
  CONFIG['warnflags'].gsub!('-Wimplicit-function-declaration', '')
50
50
  end
51
51
 
52
- if enable_config('debug')
52
+ if enable_config('debug') || enable_config('asan')
53
53
  CONFIG['debugflags'] << ' -ggdb3 -O0'
54
54
  end
55
55
 
56
56
  Libv8.configure_makefile
57
57
 
58
+ if enable_config('asan')
59
+ $CPPFLAGS.insert(0, " -fsanitize=address ")
60
+ $LDFLAGS.insert(0, " -fsanitize=address ")
61
+ end
62
+
58
63
  create_makefile 'mini_racer_extension'
@@ -127,6 +127,7 @@ typedef struct {
127
127
  Local<Function> fun;
128
128
  Local<Value> *argv;
129
129
  EvalResult result;
130
+ size_t max_memory;
130
131
  } FunctionCall;
131
132
 
132
133
  enum IsolateFlags {
@@ -152,9 +153,13 @@ static VALUE rb_mJSON;
152
153
  static VALUE rb_cFailedV8Conversion;
153
154
  static VALUE rb_cDateTime = Qnil;
154
155
 
155
- static Platform* current_platform = NULL;
156
+ static std::unique_ptr<Platform> current_platform = NULL;
156
157
  static std::mutex platform_lock;
157
158
 
159
+ static pthread_attr_t *thread_attr_p;
160
+ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
161
+ static bool ruby_exiting; // guarded by exit_lock
162
+
158
163
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
159
164
  bool platform_already_initialized = false;
160
165
 
@@ -189,8 +194,8 @@ static void init_v8() {
189
194
 
190
195
  if (current_platform == NULL) {
191
196
  V8::InitializeICU();
192
- current_platform = platform::CreateDefaultPlatform();
193
- V8::InitializePlatform(current_platform);
197
+ current_platform = platform::NewDefaultPlatform();
198
+ V8::InitializePlatform(current_platform.get());
194
199
  V8::Initialize();
195
200
  }
196
201
 
@@ -241,7 +246,7 @@ static void prepare_result(MaybeLocal v8res,
241
246
  Local<Object> object = local_value->ToObject(context).ToLocalChecked();
242
247
  const unsigned argc = 1;
243
248
  Local<Value> argv[argc] = { object };
244
- MaybeLocal<Value> json = stringify->Call(JSON, argc, argv);
249
+ MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
245
250
 
246
251
  if (json.IsEmpty()) {
247
252
  evalRes.executed = false;
@@ -444,8 +449,27 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local context,
444
449
  return rb_hash;
445
450
  }
446
451
 
447
- Local<String> rstr = value->ToString(context).ToLocalChecked();
448
- return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
452
+ if (value->IsSymbol()) {
453
+ v8::String::Utf8Value symbol_name(isolate,
454
+ Local<Symbol>::Cast(value)->Name());
455
+
456
+ VALUE str_symbol = rb_enc_str_new(
457
+ *symbol_name,
458
+ symbol_name.length(),
459
+ rb_enc_find("utf-8")
460
+ );
461
+
462
+ return ID2SYM(rb_intern_str(str_symbol));
463
+ }
464
+
465
+ MaybeLocal<String> rstr_maybe = value->ToString(context);
466
+
467
+ if (rstr_maybe.IsEmpty()) {
468
+ return Qnil;
469
+ } else {
470
+ Local<String> rstr = rstr_maybe.ToLocalChecked();
471
+ return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
472
+ }
449
473
  }
450
474
 
451
475
  static VALUE convert_v8_to_ruby(Isolate* isolate,
@@ -1086,8 +1110,10 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1086
1110
  Local<Context> context = context_info->context->Get(isolate);
1087
1111
  Context::Scope context_scope(context);
1088
1112
 
1089
- Local<String> v8_str = String::NewFromUtf8(isolate, RSTRING_PTR(name),
1090
- NewStringType::kNormal, (int)RSTRING_LEN(name)).ToLocalChecked();
1113
+ Local<String> v8_str =
1114
+ String::NewFromUtf8(isolate, RSTRING_PTR(name),
1115
+ NewStringType::kNormal, (int)RSTRING_LEN(name))
1116
+ .ToLocalChecked();
1091
1117
 
1092
1118
  // copy self so we can access from v8 external
1093
1119
  VALUE* self_copy;
@@ -1097,24 +1123,35 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1097
1123
  Local<Value> external = External::New(isolate, self_copy);
1098
1124
 
1099
1125
  if (parent_object == Qnil) {
1100
- context->Global()->Set(v8_str, FunctionTemplate::New(isolate, ruby_callback, external)->GetFunction());
1101
- } else {
1126
+ context->Global()->Set(
1127
+ v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
1128
+ ->GetFunction(context)
1129
+ .ToLocalChecked());
1102
1130
 
1103
- Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
1104
- NewStringType::kNormal, (int)RSTRING_LEN(parent_object_eval)).ToLocalChecked();
1131
+ } else {
1132
+ Local<String> eval =
1133
+ String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
1134
+ NewStringType::kNormal,
1135
+ (int)RSTRING_LEN(parent_object_eval))
1136
+ .ToLocalChecked();
1105
1137
 
1106
1138
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
1107
1139
  if (parsed_script.IsEmpty()) {
1108
- parse_error = true;
1140
+ parse_error = true;
1109
1141
  } else {
1110
- MaybeLocal<Value> maybe_value = parsed_script.ToLocalChecked()->Run(context);
1142
+ MaybeLocal<Value> maybe_value =
1143
+ parsed_script.ToLocalChecked()->Run(context);
1111
1144
  attach_error = true;
1112
1145
 
1113
1146
  if (!maybe_value.IsEmpty()) {
1114
1147
  Local<Value> value = maybe_value.ToLocalChecked();
1115
- if (value->IsObject()){
1116
- value.As<Object>()->Set(v8_str, FunctionTemplate::New(isolate, ruby_callback, external)->GetFunction());
1117
- attach_error = false;
1148
+ if (value->IsObject()) {
1149
+ value.As<Object>()->Set(
1150
+ v8_str, FunctionTemplate::New(
1151
+ isolate, ruby_callback, external)
1152
+ ->GetFunction(context)
1153
+ .ToLocalChecked());
1154
+ attach_error = false;
1118
1155
  }
1119
1156
  }
1120
1157
  }
@@ -1172,7 +1209,7 @@ void free_isolate(IsolateInfo* isolate_info) {
1172
1209
  delete isolate_info->allocator;
1173
1210
  }
1174
1211
 
1175
- static void *free_context_raw(void* arg) {
1212
+ static void free_context_raw(void *arg) {
1176
1213
  ContextInfo* context_info = (ContextInfo*)arg;
1177
1214
  IsolateInfo* isolate_info = context_info->isolate_info;
1178
1215
  Persistent<Context>* context = context_info->context;
@@ -1189,6 +1226,20 @@ static void *free_context_raw(void* arg) {
1189
1226
  }
1190
1227
 
1191
1228
  xfree(context_info);
1229
+ }
1230
+
1231
+ static void *free_context_thr(void* arg) {
1232
+ if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1233
+ return NULL;
1234
+ }
1235
+ if (ruby_exiting) {
1236
+ return NULL;
1237
+ }
1238
+
1239
+ free_context_raw(arg);
1240
+
1241
+ pthread_rwlock_unlock(&exit_lock);
1242
+
1192
1243
  return NULL;
1193
1244
  }
1194
1245
 
@@ -1202,22 +1253,17 @@ static void free_context(ContextInfo* context_info) {
1202
1253
  context_info_copy->context = context_info->context;
1203
1254
 
1204
1255
  if (isolate_info && isolate_info->refs() > 1) {
1205
- pthread_t free_context_thread;
1206
- if (pthread_create(&free_context_thread, NULL, free_context_raw, (void*)context_info_copy)) {
1207
- fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1208
- }
1209
-
1256
+ pthread_t free_context_thread;
1257
+ if (pthread_create(&free_context_thread, thread_attr_p,
1258
+ free_context_thr, (void*)context_info_copy)) {
1259
+ fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1260
+ }
1210
1261
  } else {
1211
1262
  free_context_raw(context_info_copy);
1212
1263
  }
1213
1264
 
1214
- if (context_info->context && isolate_info && isolate_info->isolate) {
1215
- context_info->context = NULL;
1216
- }
1217
-
1218
- if (isolate_info) {
1219
- context_info->isolate_info = NULL;
1220
- }
1265
+ context_info->context = NULL;
1266
+ context_info->isolate_info = NULL;
1221
1267
  }
1222
1268
 
1223
1269
  static void deallocate_isolate(void* data) {
@@ -1375,6 +1421,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1375
1421
  FileOutputStream stream(fp);
1376
1422
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1377
1423
 
1424
+ fflush(fp);
1425
+
1378
1426
  const_cast<HeapSnapshot*>(snap)->Delete();
1379
1427
 
1380
1428
  return Qtrue;
@@ -1422,6 +1470,12 @@ nogvl_context_call(void *args) {
1422
1470
  // terminate ASAP
1423
1471
  isolate->SetData(DO_TERMINATE, (void*)false);
1424
1472
 
1473
+ if (call->max_memory > 0) {
1474
+ isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1475
+ isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1476
+ isolate->AddGCEpilogueCallback(gc_callback);
1477
+ }
1478
+
1425
1479
  Isolate::Scope isolate_scope(isolate);
1426
1480
  EscapableHandleScope handle_scope(isolate);
1427
1481
  TryCatch trycatch(isolate);
@@ -1479,6 +1533,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1479
1533
  call_argv = argv + 1;
1480
1534
  }
1481
1535
 
1536
+ call.max_memory = 0;
1537
+ VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
1538
+ if (mem_softlimit != Qnil) {
1539
+ unsigned long sl_int = NUM2ULONG(mem_softlimit);
1540
+ call.max_memory = (size_t)sl_int;
1541
+ }
1542
+
1482
1543
  bool missingFunction = false;
1483
1544
  {
1484
1545
  Locker lock(isolate);
@@ -1536,6 +1597,16 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1536
1597
  return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1537
1598
  }
1538
1599
 
1600
+ static void set_ruby_exiting(VALUE value) {
1601
+ (void)value;
1602
+
1603
+ int res = pthread_rwlock_wrlock(&exit_lock);
1604
+ ruby_exiting = true;
1605
+ if (res == 0) {
1606
+ pthread_rwlock_unlock(&exit_lock);
1607
+ }
1608
+ }
1609
+
1539
1610
  extern "C" {
1540
1611
 
1541
1612
  void Init_mini_racer_extension ( void )
@@ -1589,5 +1660,14 @@ extern "C" {
1589
1660
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1590
1661
 
1591
1662
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
1663
+
1664
+ rb_set_end_proc(set_ruby_exiting, Qnil);
1665
+
1666
+ static pthread_attr_t attr;
1667
+ if (pthread_attr_init(&attr) == 0) {
1668
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
1669
+ thread_attr_p = &attr;
1670
+ }
1671
+ }
1592
1672
  }
1593
1673
  }
@@ -313,9 +313,17 @@ module MiniRacer
313
313
 
314
314
  # ensure we do not leak a thread in state
315
315
  t.join
316
+ t = nil
316
317
 
317
318
  rval
318
-
319
+ ensure
320
+ # exceptions need to be handled
321
+ if t && wp
322
+ wp.write("done")
323
+ t.join
324
+ end
325
+ wp.close if wp
326
+ rp.close if rp
319
327
  end
320
328
 
321
329
  def check_init_options!(options)
@@ -1,3 +1,3 @@
1
1
  module MiniRacer
2
- VERSION = "0.2.6"
2
+ VERSION = "0.2.11"
3
3
  end
@@ -14,18 +14,24 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/discourse/mini_racer"
15
15
  spec.license = "MIT"
16
16
 
17
+ spec.metadata = {
18
+ "bug_tracker_uri" => "https://github.com/discourse/mini_racer/issues",
19
+ "changelog_uri" => "https://github.com/discourse/mini_racer/blob/v#{spec.version}/CHANGELOG",
20
+ "documentation_uri" => "https://www.rubydoc.info/gems/mini_racer/#{spec.version}",
21
+ "source_code_uri" => "https://github.com/discourse/mini_racer/tree/v#{spec.version}",
22
+ }
17
23
 
18
24
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(benchmark|test|spec|features|examples)/}) }
19
25
  spec.bindir = "exe"
20
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
27
  spec.require_paths = ["lib"]
22
28
 
23
- spec.add_development_dependency "bundler", "~> 1.12"
29
+ spec.add_development_dependency "bundler"
24
30
  spec.add_development_dependency "rake", "~> 10.0"
25
31
  spec.add_development_dependency "minitest", "~> 5.0"
26
32
  spec.add_development_dependency "rake-compiler"
27
33
 
28
- spec.add_dependency 'libv8', '>= 6.9.411'
34
+ spec.add_dependency 'libv8', '> 7.3'
29
35
  spec.require_paths = ["lib", "ext"]
30
36
 
31
37
  spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_racer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-14 00:00:00.000000000 Z
11
+ date: 2020-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.12'
19
+ version: '0'
20
20
  type: :development
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: '1.12'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: libv8
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - ">"
74
74
  - !ruby/object:Gem::Version
75
- version: 6.9.411
75
+ version: '7.3'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - ">"
81
81
  - !ruby/object:Gem::Version
82
- version: 6.9.411
82
+ version: '7.3'
83
83
  description: Minimal embedded v8 engine for Ruby
84
84
  email:
85
85
  - sam.saffron@gmail.com
@@ -106,7 +106,11 @@ files:
106
106
  homepage: https://github.com/discourse/mini_racer
107
107
  licenses:
108
108
  - MIT
109
- metadata: {}
109
+ metadata:
110
+ bug_tracker_uri: https://github.com/discourse/mini_racer/issues
111
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.2.11/CHANGELOG
112
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.2.11
113
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.2.11
110
114
  post_install_message:
111
115
  rdoc_options: []
112
116
  require_paths: