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.
- checksums.yaml +4 -4
- data/.dockerignore +12 -0
- data/.github/workflows/ci.yml +80 -0
- data/.gitignore +1 -0
- data/.travis.yml +16 -9
- data/CHANGELOG +71 -0
- data/Dockerfile +21 -0
- data/README.md +19 -4
- data/Rakefile +1 -0
- data/ext/mini_racer_extension/extconf.rb +10 -2
- data/ext/mini_racer_extension/mini_racer_extension.cc +223 -101
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer.rb +82 -15
- data/lib/mini_racer/version.rb +4 -1
- data/mini_racer.gemspec +11 -4
- metadata +43 -19
@@ -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 "
|
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(
|
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!(
|
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 =
|
143
|
-
|
144
|
-
|
145
|
-
end
|
152
|
+
@timeout = timeout
|
153
|
+
@max_memory = max_memory
|
154
|
+
|
146
155
|
# false signals it should be fetched if requested
|
147
|
-
@isolate =
|
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(
|
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!(
|
330
|
-
assert_option_is_nil_or_a('isolate',
|
331
|
-
assert_option_is_nil_or_a('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
|
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}"
|
data/lib/mini_racer/version.rb
CHANGED
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"
|
24
|
-
spec.add_development_dependency "rake", "
|
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',
|
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.
|
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:
|
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: '
|
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: 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
|
-
|
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.
|
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: []
|