mini_racer 0.2.8 → 0.4.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
+ }
data/lib/mini_racer.rb CHANGED
@@ -1,5 +1,15 @@
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
 
@@ -130,21 +140,29 @@ module MiniRacer
130
140
  end
131
141
  end
132
142
 
133
- def initialize(options = nil)
143
+ def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
134
144
  options ||= {}
135
145
 
136
- check_init_options!(options)
146
+ check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
137
147
 
138
148
  @functions = {}
139
149
  @timeout = nil
140
150
  @max_memory = nil
141
151
  @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
152
+ @timeout = timeout
153
+ @max_memory = max_memory
154
+
146
155
  # false signals it should be fetched if requested
147
- @isolate = options[:isolate] || false
156
+ @isolate = isolate || false
157
+
158
+ @ensure_gc_after_idle = ensure_gc_after_idle
159
+
160
+ if @ensure_gc_after_idle
161
+ @last_eval = nil
162
+ @ensure_gc_thread = nil
163
+ @ensure_gc_mutex = Mutex.new
164
+ end
165
+
148
166
  @disposed = false
149
167
 
150
168
  @callback_mutex = Mutex.new
@@ -153,7 +171,7 @@ module MiniRacer
153
171
  @eval_thread = nil
154
172
 
155
173
  # defined in the C class
156
- init_unsafe(options[:isolate], options[:snapshot])
174
+ init_unsafe(isolate, snapshot)
157
175
  end
158
176
 
159
177
  def isolate
@@ -203,6 +221,7 @@ module MiniRacer
203
221
  end
204
222
  ensure
205
223
  @eval_thread = nil
224
+ ensure_gc_thread if @ensure_gc_after_idle
206
225
  end
207
226
 
208
227
  def call(function_name, *arguments)
@@ -216,15 +235,17 @@ module MiniRacer
216
235
  end
217
236
  ensure
218
237
  @eval_thread = nil
238
+ ensure_gc_thread if @ensure_gc_after_idle
219
239
  end
220
240
 
221
241
  def dispose
222
242
  return if @disposed
223
243
  isolate_mutex.synchronize do
244
+ return if @disposed
224
245
  dispose_unsafe
246
+ @disposed = true
247
+ @isolate = nil # allow it to be garbage collected, if set
225
248
  end
226
- @disposed = true
227
- @isolate = nil # allow it to be garbage collected, if set
228
249
  end
229
250
 
230
251
 
@@ -273,6 +294,38 @@ module MiniRacer
273
294
 
274
295
  private
275
296
 
297
+ def ensure_gc_thread
298
+ @last_eval = Process.clock_gettime(Process::CLOCK_MONOTONIC)
299
+ @ensure_gc_mutex.synchronize do
300
+ @ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
301
+ @ensure_gc_thread ||= Thread.new do
302
+ ensure_gc_after_idle_seconds = @ensure_gc_after_idle / 1000.0
303
+ done = false
304
+ while !done
305
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
306
+
307
+ if @disposed
308
+ @ensure_gc_thread = nil
309
+ break
310
+ end
311
+
312
+ if !@eval_thread && ensure_gc_after_idle_seconds < now - @last_eval
313
+ @ensure_gc_mutex.synchronize do
314
+ isolate_mutex.synchronize do
315
+ if !@eval_thread
316
+ isolate.low_memory_notification if !@disposed
317
+ @ensure_gc_thread = nil
318
+ done = true
319
+ end
320
+ end
321
+ end
322
+ end
323
+ sleep ensure_gc_after_idle_seconds if !done
324
+ end
325
+ end
326
+ end
327
+ end
328
+
276
329
  def stop_attached
277
330
  @callback_mutex.synchronize{
278
331
  if @callback_running
@@ -326,15 +379,29 @@ module MiniRacer
326
379
  rp.close if rp
327
380
  end
328
381
 
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)
382
+ def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
383
+ assert_option_is_nil_or_a('isolate', isolate, Isolate)
384
+ assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
385
+
386
+ assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
387
+ assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
388
+ assert_numeric_or_nil('timeout', timeout, min_value: 1)
332
389
 
333
- if options[:isolate] && options[:snapshot]
390
+ if isolate && snapshot
334
391
  raise ArgumentError, 'can only pass one of isolate and snapshot options'
335
392
  end
336
393
  end
337
394
 
395
+ def assert_numeric_or_nil(option_name, object, min_value:)
396
+ if object.is_a?(Numeric) && object < min_value
397
+ raise ArgumentError, "#{option_name} must be larger than #{min_value}"
398
+ end
399
+
400
+ if !object.nil? && !object.is_a?(Numeric)
401
+ raise ArgumentError, "#{option_name} must be a number, passed a #{object.inspect}"
402
+ end
403
+ end
404
+
338
405
  def assert_option_is_nil_or_a(option_name, object, klass)
339
406
  unless object.nil? || object.is_a?(klass)
340
407
  raise ArgumentError, "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniRacer
2
- VERSION = "0.2.8"
4
+ VERSION = "0.4.0"
5
+ LIBV8_NODE_VERSION = "~> 15.14.0.0"
3
6
  end
data/mini_racer.gemspec CHANGED
@@ -14,21 +14,28 @@ 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"
24
- spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "bundler"
30
+ spec.add_development_dependency "rake", ">= 12.3.3"
25
31
  spec.add_development_dependency "minitest", "~> 5.0"
26
32
  spec.add_development_dependency "rake-compiler"
33
+ spec.add_development_dependency "m"
27
34
 
28
- spec.add_dependency 'libv8', '>= 6.9.411'
35
+ spec.add_dependency 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
29
36
  spec.require_paths = ["lib", "ext"]
30
37
 
31
- spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
38
+ spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
32
39
 
33
40
  spec.required_ruby_version = '>= 2.3'
34
41
  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.8
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-11 00:00:00.000000000 Z
11
+ date: 2021-04-12 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: 15.14.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: 15.14.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,14 +118,20 @@ 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
106
126
  homepage: https://github.com/discourse/mini_racer
107
127
  licenses:
108
128
  - MIT
109
- metadata: {}
110
- post_install_message:
129
+ metadata:
130
+ bug_tracker_uri: https://github.com/discourse/mini_racer/issues
131
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.4.0/CHANGELOG
132
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.4.0
133
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.4.0
134
+ post_install_message:
111
135
  rdoc_options: []
112
136
  require_paths:
113
137
  - lib
@@ -123,8 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
147
  - !ruby/object:Gem::Version
124
148
  version: '0'
125
149
  requirements: []
126
- rubygems_version: 3.0.3
127
- signing_key:
150
+ rubygems_version: 3.2.2
151
+ signing_key:
128
152
  specification_version: 4
129
153
  summary: Minimal embedded v8 for Ruby
130
154
  test_files: []