mini_racer 0.2.9 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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