mini_racer 0.3.1 → 0.6.4
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 +124 -0
- data/.gitignore +2 -0
- data/CHANGELOG +61 -1
- data/Dockerfile +21 -0
- data/README.md +36 -19
- data/Rakefile +16 -3
- data/ext/mini_racer_extension/extconf.rb +41 -13
- data/ext/mini_racer_extension/mini_racer_extension.cc +416 -219
- data/ext/mini_racer_loader/extconf.rb +13 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer/truffleruby.rb +355 -0
- data/lib/mini_racer/version.rb +2 -1
- data/lib/mini_racer.rb +37 -16
- data/mini_racer.gemspec +3 -5
- metadata +18 -12
- data/.travis.yml +0 -17
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'mkmf'
|
|
2
|
+
|
|
3
|
+
if RUBY_ENGINE == "truffleruby"
|
|
4
|
+
File.write("Makefile", dummy_makefile($srcdir).join(""))
|
|
5
|
+
return
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
extension_name = 'mini_racer_loader'
|
|
9
|
+
dir_config extension_name
|
|
10
|
+
|
|
11
|
+
$CXXFLAGS += " -fvisibility=hidden "
|
|
12
|
+
|
|
13
|
+
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
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MiniRacer
|
|
4
|
+
|
|
5
|
+
class Context
|
|
6
|
+
|
|
7
|
+
class ExternalFunction
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def notify_v8
|
|
11
|
+
name = @name.encode(::Encoding::UTF_8)
|
|
12
|
+
wrapped = lambda do |*args|
|
|
13
|
+
converted = @parent.send(:convert_js_to_ruby, args)
|
|
14
|
+
begin
|
|
15
|
+
result = @callback.call(*converted)
|
|
16
|
+
rescue Polyglot::ForeignException => e
|
|
17
|
+
e = RuntimeError.new(e.message)
|
|
18
|
+
e.set_backtrace(e.backtrace)
|
|
19
|
+
@parent.instance_variable_set(:@current_exception, e)
|
|
20
|
+
raise e
|
|
21
|
+
rescue => e
|
|
22
|
+
@parent.instance_variable_set(:@current_exception, e)
|
|
23
|
+
raise e
|
|
24
|
+
end
|
|
25
|
+
@parent.send(:convert_ruby_to_js, result)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if @parent_object.nil?
|
|
29
|
+
# set global name to proc
|
|
30
|
+
result = @parent.eval_in_context('this')
|
|
31
|
+
result[name] = wrapped
|
|
32
|
+
else
|
|
33
|
+
parent_object_eval = @parent_object_eval.encode(::Encoding::UTF_8)
|
|
34
|
+
begin
|
|
35
|
+
result = @parent.eval_in_context(parent_object_eval)
|
|
36
|
+
rescue Polyglot::ForeignException, StandardError => e
|
|
37
|
+
raise ParseError, "Was expecting #{@parent_object} to be an object", e.backtrace
|
|
38
|
+
end
|
|
39
|
+
result[name] = wrapped
|
|
40
|
+
# set evaluated object results name to proc
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def heap_stats
|
|
46
|
+
{
|
|
47
|
+
total_physical_size: 0,
|
|
48
|
+
total_heap_size_executable: 0,
|
|
49
|
+
total_heap_size: 0,
|
|
50
|
+
used_heap_size: 0,
|
|
51
|
+
heap_size_limit: 0,
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def stop
|
|
56
|
+
if @entered
|
|
57
|
+
@context.stop
|
|
58
|
+
@stopped = true
|
|
59
|
+
stop_attached
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
@context_initialized = false
|
|
66
|
+
@use_strict = false
|
|
67
|
+
|
|
68
|
+
def init_unsafe(isolate, snapshot)
|
|
69
|
+
unless defined?(Polyglot::InnerContext)
|
|
70
|
+
raise "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
unless Polyglot.languages.include? "js"
|
|
74
|
+
raise "The language 'js' is not available, you likely need to `export TRUFFLERUBYOPT='--jvm --polyglot'`\n" \
|
|
75
|
+
"You also need to install the 'js' component with 'gu install js' on GraalVM 22.2+\n" \
|
|
76
|
+
"Note that you need TruffleRuby+GraalVM and not just the TruffleRuby standalone to use MiniRacer"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
@context = Polyglot::InnerContext.new(on_cancelled: -> {
|
|
80
|
+
raise ScriptTerminatedError, 'JavaScript was terminated (either by timeout or explicitly)'
|
|
81
|
+
})
|
|
82
|
+
Context.instance_variable_set(:@context_initialized, true)
|
|
83
|
+
@js_object = @context.eval('js', 'Object')
|
|
84
|
+
@isolate_mutex = Mutex.new
|
|
85
|
+
@stopped = false
|
|
86
|
+
@entered = false
|
|
87
|
+
@has_entered = false
|
|
88
|
+
@current_exception = nil
|
|
89
|
+
if isolate && snapshot
|
|
90
|
+
isolate.instance_variable_set(:@snapshot, snapshot)
|
|
91
|
+
end
|
|
92
|
+
if snapshot
|
|
93
|
+
@snapshot = snapshot
|
|
94
|
+
elsif isolate
|
|
95
|
+
@snapshot = isolate.instance_variable_get(:@snapshot)
|
|
96
|
+
else
|
|
97
|
+
@snapshot = nil
|
|
98
|
+
end
|
|
99
|
+
@is_object_or_array_func, @is_time_func, @js_date_to_time_func, @is_symbol_func, @js_symbol_to_symbol_func, @js_new_date_func, @js_new_array_func = eval_in_context <<-CODE
|
|
100
|
+
[
|
|
101
|
+
(x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) },
|
|
102
|
+
(x) => { return x instanceof Date },
|
|
103
|
+
(x) => { return x.getTime(x) },
|
|
104
|
+
(x) => { return typeof x === 'symbol' },
|
|
105
|
+
(x) => { var r = x.description; return r === undefined ? 'undefined' : r },
|
|
106
|
+
(x) => { return new Date(x) },
|
|
107
|
+
(x) => { return new Array(x) },
|
|
108
|
+
]
|
|
109
|
+
CODE
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def dispose_unsafe
|
|
113
|
+
@context.close
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def eval_unsafe(str, filename)
|
|
117
|
+
@entered = true
|
|
118
|
+
if !@has_entered && @snapshot
|
|
119
|
+
snapshot_src = encode(@snapshot.instance_variable_get(:@source))
|
|
120
|
+
begin
|
|
121
|
+
eval_in_context(snapshot_src, filename)
|
|
122
|
+
rescue Polyglot::ForeignException => e
|
|
123
|
+
raise RuntimeError, e.message, e.backtrace
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
@has_entered = true
|
|
127
|
+
raise RuntimeError, "TruffleRuby does not support eval after stop" if @stopped
|
|
128
|
+
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
|
129
|
+
raise TypeError, "wrong type argument #{filename.class} (should be a string)" unless filename.nil? || filename.is_a?(String)
|
|
130
|
+
|
|
131
|
+
str = encode(str)
|
|
132
|
+
begin
|
|
133
|
+
translate do
|
|
134
|
+
eval_in_context(str, filename)
|
|
135
|
+
end
|
|
136
|
+
rescue Polyglot::ForeignException => e
|
|
137
|
+
raise RuntimeError, e.message, e.backtrace
|
|
138
|
+
rescue ::RuntimeError => e
|
|
139
|
+
if @current_exception
|
|
140
|
+
e = @current_exception
|
|
141
|
+
@current_exception = nil
|
|
142
|
+
raise e
|
|
143
|
+
else
|
|
144
|
+
raise e, e.message
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
ensure
|
|
148
|
+
@entered = false
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def call_unsafe(function_name, *arguments)
|
|
152
|
+
@entered = true
|
|
153
|
+
if !@has_entered && @snapshot
|
|
154
|
+
src = encode(@snapshot.instance_variable_get(:source))
|
|
155
|
+
begin
|
|
156
|
+
eval_in_context(src)
|
|
157
|
+
rescue Polyglot::ForeignException => e
|
|
158
|
+
raise RuntimeError, e.message, e.backtrace
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
@has_entered = true
|
|
162
|
+
raise RuntimeError, "TruffleRuby does not support call after stop" if @stopped
|
|
163
|
+
begin
|
|
164
|
+
translate do
|
|
165
|
+
function = eval_in_context(function_name)
|
|
166
|
+
function.call(*convert_ruby_to_js(arguments))
|
|
167
|
+
end
|
|
168
|
+
rescue Polyglot::ForeignException => e
|
|
169
|
+
raise RuntimeError, e.message, e.backtrace
|
|
170
|
+
end
|
|
171
|
+
ensure
|
|
172
|
+
@entered = false
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def create_isolate_value
|
|
176
|
+
# Returning a dummy object since TruffleRuby does not have a 1-1 concept with isolate.
|
|
177
|
+
# However, code and ASTs are shared between contexts.
|
|
178
|
+
Isolate.new
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def isolate_mutex
|
|
182
|
+
@isolate_mutex
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def translate
|
|
186
|
+
convert_js_to_ruby yield
|
|
187
|
+
rescue Object => e
|
|
188
|
+
message = e.message
|
|
189
|
+
if @current_exception
|
|
190
|
+
raise @current_exception
|
|
191
|
+
elsif e.message && e.message.start_with?('SyntaxError:')
|
|
192
|
+
error_class = MiniRacer::ParseError
|
|
193
|
+
elsif e.is_a?(MiniRacer::ScriptTerminatedError)
|
|
194
|
+
error_class = MiniRacer::ScriptTerminatedError
|
|
195
|
+
else
|
|
196
|
+
error_class = MiniRacer::RuntimeError
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
if error_class == MiniRacer::RuntimeError
|
|
200
|
+
bls = e.backtrace_locations&.select { |bl| bl&.source_location&.language == 'js' }
|
|
201
|
+
if bls && !bls.empty?
|
|
202
|
+
if '(eval)' != bls[0].path
|
|
203
|
+
message = "#{e.message}\n at #{bls[0]}\n" + bls[1..].map(&:to_s).join("\n")
|
|
204
|
+
else
|
|
205
|
+
message = "#{e.message}\n" + bls.map(&:to_s).join("\n")
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
raise error_class, message
|
|
209
|
+
else
|
|
210
|
+
raise error_class, message, e.backtrace
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def convert_js_to_ruby(value)
|
|
215
|
+
case value
|
|
216
|
+
when true, false, Integer, Float
|
|
217
|
+
value
|
|
218
|
+
else
|
|
219
|
+
if value.nil?
|
|
220
|
+
nil
|
|
221
|
+
elsif value.respond_to?(:call)
|
|
222
|
+
MiniRacer::JavaScriptFunction.new
|
|
223
|
+
elsif value.respond_to?(:to_str)
|
|
224
|
+
value.to_str.dup
|
|
225
|
+
elsif value.respond_to?(:to_ary)
|
|
226
|
+
value.to_ary.map do |e|
|
|
227
|
+
if e.respond_to?(:call)
|
|
228
|
+
nil
|
|
229
|
+
else
|
|
230
|
+
convert_js_to_ruby(e)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
elsif time?(value)
|
|
234
|
+
js_date_to_time(value)
|
|
235
|
+
elsif symbol?(value)
|
|
236
|
+
js_symbol_to_symbol(value)
|
|
237
|
+
else
|
|
238
|
+
object = value
|
|
239
|
+
h = {}
|
|
240
|
+
object.instance_variables.each do |member|
|
|
241
|
+
v = object[member]
|
|
242
|
+
unless v.respond_to?(:call)
|
|
243
|
+
h[member.to_s] = convert_js_to_ruby(v)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
h
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def object_or_array?(val)
|
|
252
|
+
@is_object_or_array_func.call(val)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def time?(value)
|
|
256
|
+
@is_time_func.call(value)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def js_date_to_time(value)
|
|
260
|
+
millis = @js_date_to_time_func.call(value)
|
|
261
|
+
Time.at(Rational(millis, 1000))
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def symbol?(value)
|
|
265
|
+
@is_symbol_func.call(value)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def js_symbol_to_symbol(value)
|
|
269
|
+
@js_symbol_to_symbol_func.call(value).to_s.to_sym
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def js_new_date(value)
|
|
273
|
+
@js_new_date_func.call(value)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def js_new_array(size)
|
|
277
|
+
@js_new_array_func.call(size)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def convert_ruby_to_js(value)
|
|
281
|
+
case value
|
|
282
|
+
when nil, true, false, Integer, Float
|
|
283
|
+
value
|
|
284
|
+
when Array
|
|
285
|
+
ary = js_new_array(value.size)
|
|
286
|
+
value.each_with_index do |v, i|
|
|
287
|
+
ary[i] = convert_ruby_to_js(v)
|
|
288
|
+
end
|
|
289
|
+
ary
|
|
290
|
+
when Hash
|
|
291
|
+
h = @js_object.new
|
|
292
|
+
value.each_pair do |k, v|
|
|
293
|
+
h[convert_ruby_to_js(k.to_s)] = convert_ruby_to_js(v)
|
|
294
|
+
end
|
|
295
|
+
h
|
|
296
|
+
when String, Symbol
|
|
297
|
+
Truffle::Interop.as_truffle_string value
|
|
298
|
+
when Time
|
|
299
|
+
js_new_date(value.to_f * 1000)
|
|
300
|
+
when DateTime
|
|
301
|
+
js_new_date(value.to_time.to_f * 1000)
|
|
302
|
+
else
|
|
303
|
+
"Undefined Conversion"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def encode(string)
|
|
308
|
+
raise ArgumentError unless string
|
|
309
|
+
string.encode(::Encoding::UTF_8)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
class_eval <<-'RUBY', "(mini_racer)", 1
|
|
313
|
+
def eval_in_context(code, file = nil); code = ('"use strict";' + code) if Context.instance_variable_get(:@use_strict); @context.eval('js', code, file || '(mini_racer)'); end
|
|
314
|
+
RUBY
|
|
315
|
+
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
class Isolate
|
|
319
|
+
def init_with_snapshot(snapshot)
|
|
320
|
+
# TruffleRuby does not have a 1-1 concept with isolate.
|
|
321
|
+
# However, isolate can hold a snapshot, and code and ASTs are shared between contexts.
|
|
322
|
+
@snapshot = snapshot
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def low_memory_notification
|
|
326
|
+
GC.start
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def idle_notification(idle_time)
|
|
330
|
+
true
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
class Platform
|
|
335
|
+
def self.set_flag_as_str!(flag)
|
|
336
|
+
raise TypeError, "wrong type argument #{flag.class} (should be a string)" unless flag.is_a?(String)
|
|
337
|
+
raise MiniRacer::PlatformAlreadyInitialized, "The platform is already initialized." if Context.instance_variable_get(:@context_initialized)
|
|
338
|
+
Context.instance_variable_set(:@use_strict, true) if "--use_strict" == flag
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
class Snapshot
|
|
343
|
+
def load(str)
|
|
344
|
+
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
|
345
|
+
# Intentionally noop since TruffleRuby mocks the snapshot API
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def warmup_unsafe!(src)
|
|
349
|
+
raise TypeError, "wrong type argument #{src.class} (should be a string)" unless src.is_a?(String)
|
|
350
|
+
# Intentionally noop since TruffleRuby mocks the snapshot API
|
|
351
|
+
# by replaying snapshot source before the first eval/call
|
|
352
|
+
self
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
data/lib/mini_racer/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
require "mini_racer/version"
|
|
2
|
-
require "
|
|
2
|
+
require "pathname"
|
|
3
|
+
|
|
4
|
+
if RUBY_ENGINE == "truffleruby"
|
|
5
|
+
require "mini_racer/truffleruby"
|
|
6
|
+
else
|
|
7
|
+
require "mini_racer_loader"
|
|
8
|
+
ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
|
|
9
|
+
ext_path = Gem.loaded_specs['mini_racer'].require_paths
|
|
10
|
+
.map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
|
|
11
|
+
ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
|
|
12
|
+
|
|
13
|
+
raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
|
|
14
|
+
MiniRacer::Loader.load(ext_found.to_s)
|
|
15
|
+
end
|
|
16
|
+
|
|
3
17
|
require "thread"
|
|
4
18
|
require "json"
|
|
19
|
+
require "io/wait"
|
|
5
20
|
|
|
6
21
|
module MiniRacer
|
|
7
22
|
|
|
23
|
+
MARSHAL_STACKDEPTH_DEFAULT = 2**9-2
|
|
24
|
+
MARSHAL_STACKDEPTH_MAX_VALUE = 2**10-2
|
|
25
|
+
|
|
8
26
|
class Error < ::StandardError; end
|
|
9
27
|
|
|
10
28
|
class ContextDisposedError < Error; end
|
|
@@ -130,10 +148,10 @@ module MiniRacer
|
|
|
130
148
|
end
|
|
131
149
|
end
|
|
132
150
|
|
|
133
|
-
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
|
|
151
|
+
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil, marshal_stack_depth: nil)
|
|
134
152
|
options ||= {}
|
|
135
153
|
|
|
136
|
-
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
|
154
|
+
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
155
|
|
|
138
156
|
@functions = {}
|
|
139
157
|
@timeout = nil
|
|
@@ -141,6 +159,7 @@ module MiniRacer
|
|
|
141
159
|
@current_exception = nil
|
|
142
160
|
@timeout = timeout
|
|
143
161
|
@max_memory = max_memory
|
|
162
|
+
@marshal_stack_depth = marshal_stack_depth
|
|
144
163
|
|
|
145
164
|
# false signals it should be fetched if requested
|
|
146
165
|
@isolate = isolate || false
|
|
@@ -188,7 +207,7 @@ module MiniRacer
|
|
|
188
207
|
end
|
|
189
208
|
|
|
190
209
|
if !(File === f)
|
|
191
|
-
raise ArgumentError
|
|
210
|
+
raise ArgumentError, "file_or_io"
|
|
192
211
|
end
|
|
193
212
|
|
|
194
213
|
write_heap_snapshot_unsafe(f)
|
|
@@ -335,7 +354,7 @@ module MiniRacer
|
|
|
335
354
|
|
|
336
355
|
t = Thread.new do
|
|
337
356
|
begin
|
|
338
|
-
result =
|
|
357
|
+
result = rp.wait_readable(@timeout/1000.0)
|
|
339
358
|
if !result
|
|
340
359
|
mutex.synchronize do
|
|
341
360
|
stop unless done
|
|
@@ -352,7 +371,7 @@ module MiniRacer
|
|
|
352
371
|
done = true
|
|
353
372
|
end
|
|
354
373
|
|
|
355
|
-
wp.
|
|
374
|
+
wp.close
|
|
356
375
|
|
|
357
376
|
# ensure we do not leak a thread in state
|
|
358
377
|
t.join
|
|
@@ -361,19 +380,17 @@ module MiniRacer
|
|
|
361
380
|
rval
|
|
362
381
|
ensure
|
|
363
382
|
# exceptions need to be handled
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
end
|
|
368
|
-
wp.close if wp
|
|
369
|
-
rp.close if rp
|
|
383
|
+
wp&.close
|
|
384
|
+
t&.join
|
|
385
|
+
rp&.close
|
|
370
386
|
end
|
|
371
387
|
|
|
372
|
-
def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
|
|
388
|
+
def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
|
|
373
389
|
assert_option_is_nil_or_a('isolate', isolate, Isolate)
|
|
374
390
|
assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
|
|
375
391
|
|
|
376
|
-
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
|
|
392
|
+
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000, max_value: 2**32-1)
|
|
393
|
+
assert_numeric_or_nil('marshal_stack_depth', marshal_stack_depth, min_value: 1, max_value: MARSHAL_STACKDEPTH_MAX_VALUE)
|
|
377
394
|
assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
|
|
378
395
|
assert_numeric_or_nil('timeout', timeout, min_value: 1)
|
|
379
396
|
|
|
@@ -382,9 +399,13 @@ module MiniRacer
|
|
|
382
399
|
end
|
|
383
400
|
end
|
|
384
401
|
|
|
385
|
-
def assert_numeric_or_nil(option_name, object, min_value:)
|
|
402
|
+
def assert_numeric_or_nil(option_name, object, min_value:, max_value: nil)
|
|
403
|
+
if max_value && object.is_a?(Numeric) && object > max_value
|
|
404
|
+
raise ArgumentError, "#{option_name} must be less than or equal to #{max_value}"
|
|
405
|
+
end
|
|
406
|
+
|
|
386
407
|
if object.is_a?(Numeric) && object < min_value
|
|
387
|
-
raise ArgumentError, "#{option_name} must be larger than #{min_value}"
|
|
408
|
+
raise ArgumentError, "#{option_name} must be larger than or equal to #{min_value}"
|
|
388
409
|
end
|
|
389
410
|
|
|
390
411
|
if !object.nil? && !object.is_a?(Numeric)
|
data/mini_racer.gemspec
CHANGED
|
@@ -22,8 +22,6 @@ 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
27
|
spec.add_development_dependency "bundler"
|
|
@@ -32,10 +30,10 @@ Gem::Specification.new do |spec|
|
|
|
32
30
|
spec.add_development_dependency "rake-compiler"
|
|
33
31
|
spec.add_development_dependency "m"
|
|
34
32
|
|
|
35
|
-
spec.add_dependency 'libv8',
|
|
33
|
+
spec.add_dependency 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
|
|
36
34
|
spec.require_paths = ["lib", "ext"]
|
|
37
35
|
|
|
38
|
-
spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
|
|
36
|
+
spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
|
|
39
37
|
|
|
40
|
-
spec.required_ruby_version = '>= 2.
|
|
38
|
+
spec.required_ruby_version = '>= 2.6'
|
|
41
39
|
end
|