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.
- checksums.yaml +4 -4
- data/.dockerignore +12 -0
- data/.github/workflows/ci.yml +78 -0
- data/.gitignore +2 -0
- data/.travis.yml +14 -15
- data/CHANGELOG +65 -0
- data/Dockerfile +21 -0
- data/README.md +35 -7
- data/Rakefile +1 -0
- data/ext/mini_racer_extension/extconf.rb +11 -3
- data/ext/mini_racer_extension/mini_racer_extension.cc +421 -139
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer/version.rb +4 -1
- data/lib/mini_racer.rb +91 -15
- data/mini_racer.gemspec +6 -7
- metadata +40 -20
@@ -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/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
@@ -1,10 +1,23 @@
|
|
1
1
|
require "mini_racer/version"
|
2
|
-
require "
|
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(
|
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!(
|
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 =
|
143
|
-
|
144
|
-
|
145
|
-
|
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 =
|
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(
|
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!(
|
330
|
-
assert_option_is_nil_or_a('isolate',
|
331
|
-
assert_option_is_nil_or_a('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
|
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"
|
30
|
-
spec.add_development_dependency "rake", "
|
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',
|
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.
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
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: '
|
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: '
|
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:
|
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:
|
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:
|
70
|
+
name: m
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
76
|
-
type: :
|
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:
|
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.
|
112
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
113
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
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.
|
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.
|
150
|
+
rubygems_version: 3.1.6
|
131
151
|
signing_key:
|
132
152
|
specification_version: 4
|
133
153
|
summary: Minimal embedded v8 for Ruby
|