mini_racer 0.2.9 → 0.5.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.
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'mini_racer_loader'
4
+ dir_config extension_name
5
+
6
+ $CPPFLAGS += " -fvisibility=hidden "
7
+
8
+ create_makefile extension_name
@@ -0,0 +1,123 @@
1
+ #include <ruby.h>
2
+ #include <dlfcn.h>
3
+ #include <string.h>
4
+ #include <stdint.h>
5
+ #include <stdlib.h>
6
+
7
+ // Load a Ruby extension like Ruby does, only with flags that:
8
+ // a) hide symbols from other extensions (RTLD_LOCAL)
9
+ // b) bind symbols tightly (RTLD_DEEPBIND, when available)
10
+
11
+ void Init_mini_racer_loader(void);
12
+
13
+ static void *_dln_load(const char *file);
14
+
15
+ static VALUE _load_shared_lib(VALUE self, volatile VALUE fname)
16
+ {
17
+ (void) self;
18
+
19
+ // check that path is not tainted
20
+ SafeStringValue(fname);
21
+
22
+ FilePathValue(fname);
23
+ VALUE path = rb_str_encode_ospath(fname);
24
+
25
+ char *loc = StringValueCStr(path);
26
+ void *handle = _dln_load(loc);
27
+
28
+ return handle ? Qtrue : Qfalse;
29
+ }
30
+
31
+ // adapted from Ruby's dln.c
32
+ #define INIT_FUNC_PREFIX ((char[]) {'I', 'n', 'i', 't', '_'})
33
+ #define INIT_FUNCNAME(buf, file) do { \
34
+ const char *base = (file); \
35
+ const size_t flen = _init_funcname(&base); \
36
+ const size_t plen = sizeof(INIT_FUNC_PREFIX); \
37
+ char *const tmp = ALLOCA_N(char, plen + flen + 1); \
38
+ memcpy(tmp, INIT_FUNC_PREFIX, plen); \
39
+ memcpy(tmp+plen, base, flen); \
40
+ tmp[plen+flen] = '\0'; \
41
+ *(buf) = tmp; \
42
+ } while(0)
43
+
44
+ // adapted from Ruby's dln.c
45
+ static size_t _init_funcname(const char **file)
46
+ {
47
+ const char *p = *file,
48
+ *base,
49
+ *dot = NULL;
50
+
51
+ for (base = p; *p; p++) { /* Find position of last '/' */
52
+ if (*p == '.' && !dot) {
53
+ dot = p;
54
+ }
55
+ if (*p == '/') {
56
+ base = p + 1;
57
+ dot = NULL;
58
+ }
59
+ }
60
+ *file = base;
61
+ return (uintptr_t) ((dot ? dot : p) - base);
62
+ }
63
+
64
+ // adapted from Ruby's dln.c
65
+ static void *_dln_load(const char *file)
66
+ {
67
+ char *buf;
68
+ const char *error;
69
+ #define DLN_ERROR() (error = dlerror(), strcpy(ALLOCA_N(char, strlen(error) + 1), error))
70
+
71
+ void *handle;
72
+ void (*init_fct)(void);
73
+
74
+ INIT_FUNCNAME(&buf, file);
75
+
76
+ #ifndef RTLD_DEEPBIND
77
+ # define RTLD_DEEPBIND 0
78
+ #endif
79
+ /* Load file */
80
+ if ((handle = dlopen(file, RTLD_LAZY|RTLD_LOCAL|RTLD_DEEPBIND)) == NULL) {
81
+ DLN_ERROR();
82
+ goto failed;
83
+ }
84
+ #if defined(RUBY_EXPORT)
85
+ {
86
+ static const char incompatible[] = "incompatible library version";
87
+ void *ex = dlsym(handle, "ruby_xmalloc");
88
+ if (ex && ex != (void *) &ruby_xmalloc) {
89
+
90
+ # if defined __APPLE__
91
+ /* dlclose() segfaults */
92
+ rb_fatal("%s - %s", incompatible, file);
93
+ # else
94
+ dlclose(handle);
95
+ error = incompatible;
96
+ goto failed;
97
+ #endif
98
+ }
99
+ }
100
+ # endif
101
+
102
+ init_fct = (void (*)(void)) dlsym(handle, buf);
103
+ if (init_fct == NULL) {
104
+ error = DLN_ERROR();
105
+ dlclose(handle);
106
+ goto failed;
107
+ }
108
+
109
+ /* Call the init code */
110
+ (*init_fct)();
111
+
112
+ return handle;
113
+
114
+ failed:
115
+ rb_raise(rb_eLoadError, "%s", error);
116
+ }
117
+
118
+ __attribute__((visibility("default"))) void Init_mini_racer_loader()
119
+ {
120
+ VALUE mMiniRacer = rb_define_module("MiniRacer");
121
+ VALUE mLoader = rb_define_module_under(mMiniRacer, "Loader");
122
+ rb_define_singleton_method(mLoader, "load", _load_shared_lib, 1);
123
+ }
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniRacer
2
- VERSION = "0.2.9"
4
+ VERSION = "0.5.0"
5
+ LIBV8_NODE_VERSION = "~> 16.10.0.0"
3
6
  end
data/lib/mini_racer.rb CHANGED
@@ -1,10 +1,23 @@
1
1
  require "mini_racer/version"
2
- require "mini_racer_extension"
2
+ require "mini_racer_loader"
3
+ require "pathname"
4
+
5
+ ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
6
+ ext_path = Gem.loaded_specs['mini_racer'].require_paths
7
+ .map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
8
+ ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
9
+
10
+ raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
11
+ MiniRacer::Loader.load(ext_found.to_s)
12
+
3
13
  require "thread"
4
14
  require "json"
5
15
 
6
16
  module MiniRacer
7
17
 
18
+ MARSHAL_STACKDEPTH_DEFAULT = 2**9-2
19
+ MARSHAL_STACKDEPTH_MAX_VALUE = 2**10-2
20
+
8
21
  class Error < ::StandardError; end
9
22
 
10
23
  class ContextDisposedError < Error; end
@@ -130,21 +143,30 @@ module MiniRacer
130
143
  end
131
144
  end
132
145
 
133
- def initialize(options = nil)
146
+ def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil, marshal_stack_depth: nil)
134
147
  options ||= {}
135
148
 
136
- check_init_options!(options)
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)
137
150
 
138
151
  @functions = {}
139
152
  @timeout = nil
140
153
  @max_memory = nil
141
154
  @current_exception = nil
142
- @timeout = options[:timeout]
143
- if options[:max_memory].is_a?(Numeric) && options[:max_memory] > 0
144
- @max_memory = options[:max_memory]
145
- end
155
+ @timeout = timeout
156
+ @max_memory = max_memory
157
+ @marshal_stack_depth = marshal_stack_depth
158
+
146
159
  # false signals it should be fetched if requested
147
- @isolate = options[:isolate] || false
160
+ @isolate = isolate || false
161
+
162
+ @ensure_gc_after_idle = ensure_gc_after_idle
163
+
164
+ if @ensure_gc_after_idle
165
+ @last_eval = nil
166
+ @ensure_gc_thread = nil
167
+ @ensure_gc_mutex = Mutex.new
168
+ end
169
+
148
170
  @disposed = false
149
171
 
150
172
  @callback_mutex = Mutex.new
@@ -153,7 +175,7 @@ module MiniRacer
153
175
  @eval_thread = nil
154
176
 
155
177
  # defined in the C class
156
- init_unsafe(options[:isolate], options[:snapshot])
178
+ init_unsafe(isolate, snapshot)
157
179
  end
158
180
 
159
181
  def isolate
@@ -203,6 +225,7 @@ module MiniRacer
203
225
  end
204
226
  ensure
205
227
  @eval_thread = nil
228
+ ensure_gc_thread if @ensure_gc_after_idle
206
229
  end
207
230
 
208
231
  def call(function_name, *arguments)
@@ -216,15 +239,17 @@ module MiniRacer
216
239
  end
217
240
  ensure
218
241
  @eval_thread = nil
242
+ ensure_gc_thread if @ensure_gc_after_idle
219
243
  end
220
244
 
221
245
  def dispose
222
246
  return if @disposed
223
247
  isolate_mutex.synchronize do
248
+ return if @disposed
224
249
  dispose_unsafe
250
+ @disposed = true
251
+ @isolate = nil # allow it to be garbage collected, if set
225
252
  end
226
- @disposed = true
227
- @isolate = nil # allow it to be garbage collected, if set
228
253
  end
229
254
 
230
255
 
@@ -273,6 +298,38 @@ module MiniRacer
273
298
 
274
299
  private
275
300
 
301
+ def ensure_gc_thread
302
+ @last_eval = Process.clock_gettime(Process::CLOCK_MONOTONIC)
303
+ @ensure_gc_mutex.synchronize do
304
+ @ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
305
+ @ensure_gc_thread ||= Thread.new do
306
+ ensure_gc_after_idle_seconds = @ensure_gc_after_idle / 1000.0
307
+ done = false
308
+ while !done
309
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
310
+
311
+ if @disposed
312
+ @ensure_gc_thread = nil
313
+ break
314
+ end
315
+
316
+ if !@eval_thread && ensure_gc_after_idle_seconds < now - @last_eval
317
+ @ensure_gc_mutex.synchronize do
318
+ isolate_mutex.synchronize do
319
+ if !@eval_thread
320
+ isolate.low_memory_notification if !@disposed
321
+ @ensure_gc_thread = nil
322
+ done = true
323
+ end
324
+ end
325
+ end
326
+ end
327
+ sleep ensure_gc_after_idle_seconds if !done
328
+ end
329
+ end
330
+ end
331
+ end
332
+
276
333
  def stop_attached
277
334
  @callback_mutex.synchronize{
278
335
  if @callback_running
@@ -326,15 +383,34 @@ module MiniRacer
326
383
  rp.close if rp
327
384
  end
328
385
 
329
- def check_init_options!(options)
330
- assert_option_is_nil_or_a('isolate', options[:isolate], Isolate)
331
- assert_option_is_nil_or_a('snapshot', options[:snapshot], Snapshot)
386
+ def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
387
+ assert_option_is_nil_or_a('isolate', isolate, Isolate)
388
+ assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
389
+
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)
392
+ assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
393
+ assert_numeric_or_nil('timeout', timeout, min_value: 1)
332
394
 
333
- if options[:isolate] && options[:snapshot]
395
+ if isolate && snapshot
334
396
  raise ArgumentError, 'can only pass one of isolate and snapshot options'
335
397
  end
336
398
  end
337
399
 
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
+
405
+ if object.is_a?(Numeric) && object < min_value
406
+ raise ArgumentError, "#{option_name} must be larger than or equal to #{min_value}"
407
+ end
408
+
409
+ if !object.nil? && !object.is_a?(Numeric)
410
+ raise ArgumentError, "#{option_name} must be a number, passed a #{object.inspect}"
411
+ end
412
+ end
413
+
338
414
  def assert_option_is_nil_or_a(option_name, object, klass)
339
415
  unless object.nil? || object.is_a?(klass)
340
416
  raise ArgumentError, "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
data/mini_racer.gemspec CHANGED
@@ -22,19 +22,18 @@ 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
- spec.add_development_dependency "bundler", "~> 1.12"
30
- spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "bundler"
28
+ spec.add_development_dependency "rake", ">= 12.3.3"
31
29
  spec.add_development_dependency "minitest", "~> 5.0"
32
30
  spec.add_development_dependency "rake-compiler"
31
+ spec.add_development_dependency "m"
33
32
 
34
- spec.add_dependency 'libv8', '>= 6.9.411'
33
+ spec.add_dependency 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
35
34
  spec.require_paths = ["lib", "ext"]
36
35
 
37
- spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
36
+ spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
38
37
 
39
- spec.required_ruby_version = '>= 2.3'
38
+ spec.required_ruby_version = '>= 2.6'
40
39
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_racer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-09 00:00:00.000000000 Z
11
+ date: 2021-11-11 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
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: 12.3.3
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 12.3.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -67,31 +67,49 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: libv8
70
+ name: m
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 6.9.411
76
- type: :runtime
75
+ version: '0'
76
+ type: :development
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: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: libv8-node
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 16.10.0.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 16.10.0.0
83
97
  description: Minimal embedded v8 engine for Ruby
84
98
  email:
85
99
  - sam.saffron@gmail.com
86
100
  executables: []
87
101
  extensions:
102
+ - ext/mini_racer_loader/extconf.rb
88
103
  - ext/mini_racer_extension/extconf.rb
89
104
  extra_rdoc_files: []
90
105
  files:
106
+ - ".dockerignore"
107
+ - ".github/workflows/ci.yml"
91
108
  - ".gitignore"
92
109
  - ".travis.yml"
93
110
  - CHANGELOG
94
111
  - CODE_OF_CONDUCT.md
112
+ - Dockerfile
95
113
  - Gemfile
96
114
  - LICENSE.txt
97
115
  - README.md
@@ -100,6 +118,8 @@ files:
100
118
  - bin/setup
101
119
  - ext/mini_racer_extension/extconf.rb
102
120
  - ext/mini_racer_extension/mini_racer_extension.cc
121
+ - ext/mini_racer_loader/extconf.rb
122
+ - ext/mini_racer_loader/mini_racer_loader.c
103
123
  - lib/mini_racer.rb
104
124
  - lib/mini_racer/version.rb
105
125
  - mini_racer.gemspec
@@ -108,9 +128,9 @@ licenses:
108
128
  - MIT
109
129
  metadata:
110
130
  bug_tracker_uri: https://github.com/discourse/mini_racer/issues
111
- changelog_uri: https://github.com/discourse/mini_racer/blob/v0.2.9/CHANGELOG
112
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.2.9
113
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.2.9
131
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.5.0/CHANGELOG
132
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.5.0
133
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.5.0
114
134
  post_install_message:
115
135
  rdoc_options: []
116
136
  require_paths:
@@ -120,14 +140,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
140
  requirements:
121
141
  - - ">="
122
142
  - !ruby/object:Gem::Version
123
- version: '2.3'
143
+ version: '2.6'
124
144
  required_rubygems_version: !ruby/object:Gem::Requirement
125
145
  requirements:
126
146
  - - ">="
127
147
  - !ruby/object:Gem::Version
128
148
  version: '0'
129
149
  requirements: []
130
- rubygems_version: 3.0.3
150
+ rubygems_version: 3.1.6
131
151
  signing_key:
132
152
  specification_version: 4
133
153
  summary: Minimal embedded v8 for Ruby