mini_racer_rs 1.0.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 +7 -0
- data/AI-DISCLOSURE.md +13 -0
- data/CHANGELOG.md +45 -0
- data/COMPATIBILITY.md +235 -0
- data/Cargo.lock +1263 -0
- data/Cargo.toml +62 -0
- data/LICENSE +21 -0
- data/README.md +254 -0
- data/THIRD_PARTY_NOTICES.md +4501 -0
- data/crates/mini_racer_engine/Cargo.toml +25 -0
- data/crates/mini_racer_engine/build.rs +13 -0
- data/crates/mini_racer_engine/src/context.rs +802 -0
- data/crates/mini_racer_engine/src/control.rs +239 -0
- data/crates/mini_racer_engine/src/error.rs +88 -0
- data/crates/mini_racer_engine/src/fast_calls.rs +681 -0
- data/crates/mini_racer_engine/src/lib.rs +20 -0
- data/crates/mini_racer_engine/src/notify.rs +107 -0
- data/crates/mini_racer_engine/src/process/code_cache.rs +412 -0
- data/crates/mini_racer_engine/src/process/fork.rs +261 -0
- data/crates/mini_racer_engine/src/process/platform.rs +194 -0
- data/crates/mini_racer_engine/src/process/tasks.rs +96 -0
- data/crates/mini_racer_engine/src/process/watchdog.rs +185 -0
- data/crates/mini_racer_engine/src/process/workers.rs +165 -0
- data/crates/mini_racer_engine/src/process.rs +69 -0
- data/crates/mini_racer_engine/src/runner.rs +1560 -0
- data/crates/mini_racer_engine/src/serialize.rs +253 -0
- data/crates/mini_racer_engine/src/snapshot.rs +221 -0
- data/crates/mini_racer_engine/src/spin.rs +23 -0
- data/crates/mini_racer_engine/src/sys.rs +566 -0
- data/crates/mini_racer_sync/Cargo.toml +18 -0
- data/crates/mini_racer_sync/src/channel.rs +621 -0
- data/crates/mini_racer_sync/src/fork.rs +116 -0
- data/crates/mini_racer_sync/src/lib.rs +20 -0
- data/crates/mini_racer_sync/src/lock.rs +49 -0
- data/crates/mini_racer_sync/src/parker.rs +44 -0
- data/crates/mini_racer_sync/src/primitives.rs +10 -0
- data/crates/mini_racer_sync/src/tasks.rs +345 -0
- data/crates/mini_racer_sync/src/termination.rs +1234 -0
- data/crates/mini_racer_sync/src/workers.rs +283 -0
- data/crates/mini_racer_wire/Cargo.toml +18 -0
- data/crates/mini_racer_wire/src/decode.rs +926 -0
- data/crates/mini_racer_wire/src/lib.rs +67 -0
- data/crates/mini_racer_wire/src/marker.rs +43 -0
- data/crates/mini_racer_wire/src/text.rs +106 -0
- data/crates/mini_racer_wire/src/write.rs +187 -0
- data/ext/mini_racer_rs/Cargo.toml +26 -0
- data/ext/mini_racer_rs/build.rs +16 -0
- data/ext/mini_racer_rs/extconf.rb +78 -0
- data/ext/mini_racer_rs/src/context.rs +485 -0
- data/ext/mini_racer_rs/src/decode.rs +450 -0
- data/ext/mini_racer_rs/src/encode.rs +333 -0
- data/ext/mini_racer_rs/src/errors.rs +156 -0
- data/ext/mini_racer_rs/src/fork.rs +31 -0
- data/ext/mini_racer_rs/src/gc_root.rs +117 -0
- data/ext/mini_racer_rs/src/guard.rs +7 -0
- data/ext/mini_racer_rs/src/host.rs +339 -0
- data/ext/mini_racer_rs/src/lib.rs +44 -0
- data/ext/mini_racer_rs/src/options.rs +269 -0
- data/ext/mini_racer_rs/src/platform.rs +42 -0
- data/ext/mini_racer_rs/src/rb_raw.rs +368 -0
- data/ext/mini_racer_rs/src/snapshot.rs +149 -0
- data/ext/mini_racer_rs/v8-archives.sha256 +8 -0
- data/lib/mini_racer/shared.rb +420 -0
- data/lib/mini_racer/truffleruby.rb +439 -0
- data/lib/mini_racer/version.rb +6 -0
- data/lib/mini_racer.rb +3 -0
- data/lib/mini_racer_rs/internal_error.rb +7 -0
- data/lib/mini_racer_rs/version.rb +5 -0
- data/lib/mini_racer_rs/vocabulary.rb +105 -0
- data/lib/mini_racer_rs.rb +33 -0
- data/licenses/mini_racer.txt +21 -0
- data/licenses/rust/SOURCES.tsv +6 -0
- data/licenses/rust/std/COPYRIGHT +34 -0
- data/licenses/rust/std/COPYRIGHT-library.html +29357 -0
- data/licenses/rust/std/LICENSE-APACHE +176 -0
- data/licenses/rust/std/LICENSE-MIT +25 -0
- data/licenses/v8/SOURCES.tsv +20 -0
- data/licenses/v8/abseil-cpp/LICENSE +203 -0
- data/licenses/v8/chromium-build/LICENSE +27 -0
- data/licenses/v8/components.tsv +29 -0
- data/licenses/v8/dragonbox/LICENSE-Boost +23 -0
- data/licenses/v8/fast_float/LICENSE-MIT +27 -0
- data/licenses/v8/highway/LICENSE +371 -0
- data/licenses/v8/icu/LICENSE +568 -0
- data/licenses/v8/inspector_protocol/LICENSE +27 -0
- data/licenses/v8/libc++/LICENSE.TXT +311 -0
- data/licenses/v8/libc++abi/LICENSE.TXT +311 -0
- data/licenses/v8/llvm-libc/LICENSE.TXT +278 -0
- data/licenses/v8/rapidhash/LICENSE +34 -0
- data/licenses/v8/simdutf/LICENSE +18 -0
- data/licenses/v8/siphash/LICENSE +116 -0
- data/licenses/v8/utf8-decoder/LICENSE +19 -0
- data/licenses/v8/v8/LICENSE +61 -0
- data/licenses/v8/v8/LICENSE.fdlibm +6 -0
- data/licenses/v8/v8/LICENSE.strongtalk +29 -0
- data/licenses/v8/v8/LICENSE.v8 +26 -0
- metadata +156 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
use magnus::value::ReprValue;
|
|
2
|
+
use magnus::{
|
|
3
|
+
Class, Error, Fixnum, IntoValue, Module, Object, RArray, RClass, RHash, RModule, RObject,
|
|
4
|
+
RString, Ruby, Value,
|
|
5
|
+
};
|
|
6
|
+
use mini_racer_wire::{
|
|
7
|
+
Begun, Container, DecodeError, Limits, Scalar, Text, Visitor, decode_value, marker, text,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
use crate::gc_root::StaticRoot;
|
|
11
|
+
use crate::{errors, rb_raw};
|
|
12
|
+
|
|
13
|
+
#[expect(
|
|
14
|
+
clippy::expect_used,
|
|
15
|
+
reason = "init defines the class, and a root initializer cannot return an error"
|
|
16
|
+
)]
|
|
17
|
+
static JAVASCRIPT_FUNCTION: StaticRoot<RClass> = StaticRoot::new(|ruby| {
|
|
18
|
+
ruby.class_object()
|
|
19
|
+
.const_get::<_, RModule>("MiniRacer")
|
|
20
|
+
.and_then(|racer| racer.const_get("JavaScriptFunction"))
|
|
21
|
+
.expect("init defines MiniRacer::JavaScriptFunction")
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
25
|
+
enum Number {
|
|
26
|
+
Integer(i64),
|
|
27
|
+
BigInteger,
|
|
28
|
+
Float,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Upstream returns any finite integral double as an Integer (-0.0 included).
|
|
32
|
+
#[allow(
|
|
33
|
+
clippy::float_cmp,
|
|
34
|
+
reason = "fract() is exact; an integral double has a zero fraction"
|
|
35
|
+
)]
|
|
36
|
+
#[allow(
|
|
37
|
+
clippy::cast_possible_truncation,
|
|
38
|
+
reason = "range-checked against i64 first"
|
|
39
|
+
)]
|
|
40
|
+
fn classify(n: f64) -> Number {
|
|
41
|
+
const I64_END: f64 = 9_223_372_036_854_775_808.0;
|
|
42
|
+
if !n.is_finite() || n.fract() != 0.0 {
|
|
43
|
+
Number::Float
|
|
44
|
+
} else if (-I64_END..I64_END).contains(&n) {
|
|
45
|
+
Number::Integer(n as i64)
|
|
46
|
+
} else {
|
|
47
|
+
Number::BigInteger
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// mini_racer's Ruby shapes: an array drops its named properties and a sparse
|
|
52
|
+
// array its indexes, a set becomes an Array, a map keeps its keys as they are,
|
|
53
|
+
// an object's keys become Strings, and an error becomes a ScriptError.
|
|
54
|
+
enum Frame {
|
|
55
|
+
Array(RArray),
|
|
56
|
+
Sparse { array: RArray, named: bool },
|
|
57
|
+
Object(RHash),
|
|
58
|
+
Map(RHash),
|
|
59
|
+
Error(ErrorParts),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#[derive(Clone, Copy)]
|
|
63
|
+
struct ErrorParts {
|
|
64
|
+
error: Value,
|
|
65
|
+
message: Option<Value>,
|
|
66
|
+
stack: Option<Value>,
|
|
67
|
+
cause: Option<Value>,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const BATCH: usize = 64;
|
|
71
|
+
|
|
72
|
+
#[derive(Clone, Copy)]
|
|
73
|
+
enum Target {
|
|
74
|
+
Array(RArray),
|
|
75
|
+
Hash(RHash),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Decoded elements and object pairs wait here so a batch of them costs one
|
|
79
|
+
// rb_protect rather than one per value. Every container's begin and end
|
|
80
|
+
// empties it, so only the innermost open container ever has values waiting,
|
|
81
|
+
// and whatever a child does (a Map hashing an ancestor as its key) sees each
|
|
82
|
+
// container with every value decoded so far, as mini_racer builds them.
|
|
83
|
+
struct Pending {
|
|
84
|
+
target: Option<Target>,
|
|
85
|
+
values: [Value; BATCH],
|
|
86
|
+
filled: usize,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// The visitor and its frames live on the native stack for the whole decode,
|
|
90
|
+
// where Ruby's GC finds them (I3); `refs` holds every value V8 gave an id.
|
|
91
|
+
struct RubyVisitor<'r> {
|
|
92
|
+
ruby: &'r Ruby,
|
|
93
|
+
refs: RArray,
|
|
94
|
+
pending: Pending,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
impl RubyVisitor<'_> {
|
|
98
|
+
fn add(&mut self, target: Target, values: &[Value]) -> Result<(), Error> {
|
|
99
|
+
self.add_with(target, values.len(), |slots| slots.copy_from_slice(values))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// `fill` writes the `count` values straight into the batch, where the GC
|
|
103
|
+
// sees each one as soon as it is made.
|
|
104
|
+
fn add_with(
|
|
105
|
+
&mut self,
|
|
106
|
+
target: Target,
|
|
107
|
+
count: usize,
|
|
108
|
+
fill: impl FnOnce(&mut [Value]),
|
|
109
|
+
) -> Result<(), Error> {
|
|
110
|
+
if self.pending.filled.saturating_add(count) > BATCH {
|
|
111
|
+
self.flush()?;
|
|
112
|
+
}
|
|
113
|
+
if self.pending.filled == 0 {
|
|
114
|
+
self.pending.target = Some(target);
|
|
115
|
+
}
|
|
116
|
+
let start = self.pending.filled;
|
|
117
|
+
let end = start.saturating_add(count);
|
|
118
|
+
let slots = self
|
|
119
|
+
.pending
|
|
120
|
+
.values
|
|
121
|
+
.get_mut(start..end)
|
|
122
|
+
.ok_or_else(|| errors::internal(self.ruby, "decode batch overflow"))?;
|
|
123
|
+
fill(slots);
|
|
124
|
+
self.pending.filled = end;
|
|
125
|
+
Ok(())
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Converts a run straight into the batch, so a run as long as the batch
|
|
129
|
+
// is one rb_ary_cat and a short one shares its append with its neighbors.
|
|
130
|
+
fn extend<T: Copy>(
|
|
131
|
+
&mut self,
|
|
132
|
+
array: RArray,
|
|
133
|
+
values: &[T],
|
|
134
|
+
convert: impl Fn(&Ruby, T) -> Value,
|
|
135
|
+
) -> Result<(), Error> {
|
|
136
|
+
let ruby = self.ruby;
|
|
137
|
+
for chunk in values.chunks(BATCH) {
|
|
138
|
+
self.add_with(Target::Array(array), chunk.len(), |slots| {
|
|
139
|
+
for (slot, &n) in slots.iter_mut().zip(chunk) {
|
|
140
|
+
*slot = convert(ruby, n);
|
|
141
|
+
}
|
|
142
|
+
})?;
|
|
143
|
+
}
|
|
144
|
+
Ok(())
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fn insert_each<T: Copy>(
|
|
148
|
+
&mut self,
|
|
149
|
+
frame: &mut Frame,
|
|
150
|
+
entries: &[(i32, T)],
|
|
151
|
+
convert: impl Fn(&Ruby, T) -> Value,
|
|
152
|
+
) -> Result<(), Error> {
|
|
153
|
+
for &(key, n) in entries {
|
|
154
|
+
let key = int32(self.ruby, key);
|
|
155
|
+
let value = convert(self.ruby, n);
|
|
156
|
+
self.insert(frame, key, value)?;
|
|
157
|
+
}
|
|
158
|
+
Ok(())
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn flush(&mut self) -> Result<(), Error> {
|
|
162
|
+
let filled = std::mem::take(&mut self.pending.filled);
|
|
163
|
+
let values = self.pending.values.get(..filled).unwrap_or_default();
|
|
164
|
+
match self.pending.target {
|
|
165
|
+
_ if filled == 0 => Ok(()),
|
|
166
|
+
Some(Target::Array(array)) => array.cat(values),
|
|
167
|
+
Some(Target::Hash(hash)) => {
|
|
168
|
+
rb_raw::hash_aset_pairs(self.ruby, hash, values.as_chunks::<2>().0)
|
|
169
|
+
}
|
|
170
|
+
None => Err(errors::internal(self.ruby, "decode batch without a target")),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
fn bad_state<T>(&self) -> Result<T, Error> {
|
|
175
|
+
Err(errors::runtime(self.ruby, "bad state"))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
fn string(&self, text: Text<'_>) -> Result<Value, Error> {
|
|
179
|
+
let ruby = self.ruby;
|
|
180
|
+
Ok(match text {
|
|
181
|
+
Text::Utf8(bytes) => ruby.enc_str_new(bytes, ruby.utf8_encoding()).as_value(),
|
|
182
|
+
Text::Latin1(bytes) if bytes.is_ascii() => {
|
|
183
|
+
ruby.enc_str_new(bytes, ruby.utf8_encoding()).as_value()
|
|
184
|
+
}
|
|
185
|
+
Text::Latin1(bytes) => ruby.str_new(&text::latin1_to_utf8(bytes)).as_value(),
|
|
186
|
+
Text::Utf16Le(bytes) if marker::is_javascript_function(bytes) => {
|
|
187
|
+
JAVASCRIPT_FUNCTION.get(ruby).new_instance(())?
|
|
188
|
+
}
|
|
189
|
+
Text::Utf16Le(bytes) => {
|
|
190
|
+
if let Some(utf8) = text::utf16le_to_utf8(bytes) {
|
|
191
|
+
ruby.str_new(&utf8).as_value()
|
|
192
|
+
} else {
|
|
193
|
+
let utf16 = ruby
|
|
194
|
+
.find_encoding("UTF-16LE")
|
|
195
|
+
.ok_or_else(|| errors::internal(ruby, "no UTF-16LE encoding"))?;
|
|
196
|
+
ruby.enc_str_new(bytes, utf16).as_value()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// mini_racer splits a millisecond time into seconds and microseconds,
|
|
203
|
+
// truncating each, and lets Time normalize a negative remainder.
|
|
204
|
+
#[expect(
|
|
205
|
+
clippy::cast_possible_truncation,
|
|
206
|
+
reason = "a finite JavaScript time is within 8.64e15 ms"
|
|
207
|
+
)]
|
|
208
|
+
fn time(&self, millis: f64) -> Result<Value, Error> {
|
|
209
|
+
if !millis.is_finite() {
|
|
210
|
+
return Err(errors::raised(
|
|
211
|
+
self.ruby,
|
|
212
|
+
self.ruby.exception_range_error(),
|
|
213
|
+
"invalid Date",
|
|
214
|
+
));
|
|
215
|
+
}
|
|
216
|
+
let seconds = (millis / 1e3) as i64;
|
|
217
|
+
let micros = (1e3 * (millis % 1e3)) as i64;
|
|
218
|
+
Ok(self.ruby.time_new(seconds, micros)?.as_value())
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
fn script_error(&self, parts: ErrorParts) -> Result<Value, Error> {
|
|
222
|
+
let ruby = self.ruby;
|
|
223
|
+
// mini_racer builds both from C strings, so they are binary.
|
|
224
|
+
let mut message = parts
|
|
225
|
+
.message
|
|
226
|
+
.unwrap_or_else(|| ruby.str_from_slice(b"JS exception").as_value());
|
|
227
|
+
if let Some(stack) = parts.stack {
|
|
228
|
+
message = message.funcall("concat", (ruby.str_from_slice(b"\n"), stack))?;
|
|
229
|
+
}
|
|
230
|
+
let _: Value = parts.error.funcall("initialize", (message,))?;
|
|
231
|
+
if let Some(cause) = parts.cause.filter(|cause| !cause.is_nil()) {
|
|
232
|
+
RObject::from_value(parts.error)
|
|
233
|
+
.ok_or_else(|| errors::internal(ruby, "error is not an object"))?
|
|
234
|
+
.ivar_set("@cause", cause)?;
|
|
235
|
+
}
|
|
236
|
+
Ok(parts.error)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
fn is_index(key: Value) -> bool {
|
|
241
|
+
Fixnum::from_value(key).is_some_and(|key| i32::try_from(key.to_i64()).is_ok())
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
fn int32(ruby: &Ruby, n: i32) -> Value {
|
|
245
|
+
ruby.integer_from_i64(n.into()).as_value()
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
fn number(ruby: &Ruby, n: f64) -> Value {
|
|
249
|
+
match classify(n) {
|
|
250
|
+
Number::Integer(i) => ruby.integer_from_i64(i).as_value(),
|
|
251
|
+
Number::BigInteger => rb_raw::dbl2big(ruby, n),
|
|
252
|
+
Number::Float => ruby.float_from_f64(n).as_value(),
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
impl Visitor for RubyVisitor<'_> {
|
|
257
|
+
type Value = Value;
|
|
258
|
+
type Frame = Frame;
|
|
259
|
+
type Error = Error;
|
|
260
|
+
|
|
261
|
+
fn scalar(&mut self, scalar: Scalar<'_>) -> Result<Value, Error> {
|
|
262
|
+
let ruby = self.ruby;
|
|
263
|
+
Ok(match scalar {
|
|
264
|
+
Scalar::Undefined | Scalar::Null | Scalar::Hole => ruby.qnil().as_value(),
|
|
265
|
+
Scalar::Bool(b) => b.into_value_with(ruby),
|
|
266
|
+
Scalar::Int32(n) => int32(ruby, n),
|
|
267
|
+
Scalar::Uint32(n) => ruby.integer_from_u64(n.into()).as_value(),
|
|
268
|
+
Scalar::Number(n) => number(ruby, n),
|
|
269
|
+
Scalar::BigInt { negative, digits } => rb_raw::integer_unpack(ruby, digits, negative)?,
|
|
270
|
+
Scalar::Text(text) | Scalar::RegExp { pattern: text, .. } => self.string(text)?,
|
|
271
|
+
Scalar::Date(millis) => self.time(millis)?,
|
|
272
|
+
Scalar::ArrayBuffer(bytes) => ruby.str_from_slice(bytes).as_value(),
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
fn begin(&mut self, container: Container) -> Result<Begun<Frame, Value>, Error> {
|
|
277
|
+
self.flush()?;
|
|
278
|
+
let ruby = self.ruby;
|
|
279
|
+
let frame = match container {
|
|
280
|
+
Container::Values | Container::Set => Frame::Array(ruby.ary_new()),
|
|
281
|
+
Container::DenseArray { length } => {
|
|
282
|
+
Frame::Array(ruby.ary_new_capa(usize::try_from(length).unwrap_or(0)))
|
|
283
|
+
}
|
|
284
|
+
Container::SparseArray { .. } => Frame::Sparse {
|
|
285
|
+
array: ruby.ary_new(),
|
|
286
|
+
named: false,
|
|
287
|
+
},
|
|
288
|
+
Container::Object => Frame::Object(ruby.hash_new()),
|
|
289
|
+
Container::Map => Frame::Map(ruby.hash_new()),
|
|
290
|
+
// Allocated now so a cause can refer back to the error itself.
|
|
291
|
+
Container::Error => Frame::Error(ErrorParts {
|
|
292
|
+
error: errors::script_error(ruby).obj_alloc()?.as_value(),
|
|
293
|
+
message: None,
|
|
294
|
+
stack: None,
|
|
295
|
+
cause: None,
|
|
296
|
+
}),
|
|
297
|
+
};
|
|
298
|
+
let value = match &frame {
|
|
299
|
+
Frame::Array(array) | Frame::Sparse { array, .. } => array.as_value(),
|
|
300
|
+
Frame::Object(hash) | Frame::Map(hash) => hash.as_value(),
|
|
301
|
+
Frame::Error(parts) => parts.error,
|
|
302
|
+
};
|
|
303
|
+
Ok(Begun {
|
|
304
|
+
frame,
|
|
305
|
+
value: Some(value),
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fn push(&mut self, frame: &mut Frame, value: Value) -> Result<(), Error> {
|
|
310
|
+
match frame {
|
|
311
|
+
Frame::Array(array) | Frame::Sparse { array, .. } => {
|
|
312
|
+
self.add(Target::Array(*array), &[value])
|
|
313
|
+
}
|
|
314
|
+
Frame::Object(_) | Frame::Map(_) | Frame::Error(_) => self.bad_state(),
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
fn insert(&mut self, frame: &mut Frame, key: Value, value: Value) -> Result<(), Error> {
|
|
319
|
+
match frame {
|
|
320
|
+
Frame::Array(_) => Ok(()),
|
|
321
|
+
Frame::Sparse { array, named } => {
|
|
322
|
+
if !*named && is_index(key) {
|
|
323
|
+
self.add(Target::Array(*array), &[value])
|
|
324
|
+
} else {
|
|
325
|
+
*named = true;
|
|
326
|
+
Ok(())
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
Frame::Object(hash) => {
|
|
330
|
+
let key = match RString::from_value(key) {
|
|
331
|
+
Some(key) => key.as_value(),
|
|
332
|
+
None => key.funcall::<_, _, Value>("to_s", ())?,
|
|
333
|
+
};
|
|
334
|
+
self.add(Target::Hash(*hash), &[key, value])
|
|
335
|
+
}
|
|
336
|
+
Frame::Map(hash) => hash.aset(key, value),
|
|
337
|
+
Frame::Error(parts) => {
|
|
338
|
+
let field = RString::from_value(key)
|
|
339
|
+
.map(|key| rb_raw::string_bytes(self.ruby, key))
|
|
340
|
+
.unwrap_or_default();
|
|
341
|
+
match field.as_slice() {
|
|
342
|
+
b"message" => parts.message = Some(value),
|
|
343
|
+
b"stack" => parts.stack = Some(value),
|
|
344
|
+
b"cause" => parts.cause = Some(value),
|
|
345
|
+
_ => return self.bad_state(),
|
|
346
|
+
}
|
|
347
|
+
Ok(())
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
fn end(&mut self, frame: Frame) -> Result<Value, Error> {
|
|
353
|
+
self.flush()?;
|
|
354
|
+
match frame {
|
|
355
|
+
Frame::Array(array) | Frame::Sparse { array, .. } => Ok(array.as_value()),
|
|
356
|
+
Frame::Object(hash) | Frame::Map(hash) => Ok(hash.as_value()),
|
|
357
|
+
Frame::Error(parts) => self.script_error(parts),
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
fn register(&mut self, id: u32, value: Value) -> Result<(), Error> {
|
|
362
|
+
self.refs
|
|
363
|
+
.store(isize::try_from(id).unwrap_or(isize::MAX), value)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
fn back_ref(&mut self, id: u32) -> Result<Value, Error> {
|
|
367
|
+
self.refs.entry(isize::try_from(id).unwrap_or(isize::MAX))
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
fn push_int32s(&mut self, frame: &mut Frame, values: &[i32]) -> Result<(), Error> {
|
|
371
|
+
match frame {
|
|
372
|
+
Frame::Array(array) | Frame::Sparse { array, .. } => self.extend(*array, values, int32),
|
|
373
|
+
Frame::Object(_) | Frame::Map(_) | Frame::Error(_) => self.bad_state(),
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
fn push_numbers(&mut self, frame: &mut Frame, values: &[f64]) -> Result<(), Error> {
|
|
378
|
+
match frame {
|
|
379
|
+
Frame::Array(array) | Frame::Sparse { array, .. } => {
|
|
380
|
+
self.extend(*array, values, number)
|
|
381
|
+
}
|
|
382
|
+
Frame::Object(_) | Frame::Map(_) | Frame::Error(_) => self.bad_state(),
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// An int32 key is a Fixnum on every supported (64-bit) platform, so it
|
|
387
|
+
// passes `is_index` and `insert` would append each value.
|
|
388
|
+
fn insert_int32s(&mut self, frame: &mut Frame, entries: &[(i32, i32)]) -> Result<(), Error> {
|
|
389
|
+
match frame {
|
|
390
|
+
Frame::Sparse {
|
|
391
|
+
array,
|
|
392
|
+
named: false,
|
|
393
|
+
} => self.extend(*array, entries, |ruby, (_, n)| int32(ruby, n)),
|
|
394
|
+
_ => self.insert_each(frame, entries, int32),
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
fn insert_numbers(&mut self, frame: &mut Frame, entries: &[(i32, f64)]) -> Result<(), Error> {
|
|
399
|
+
match frame {
|
|
400
|
+
Frame::Sparse {
|
|
401
|
+
array,
|
|
402
|
+
named: false,
|
|
403
|
+
} => self.extend(*array, entries, |ruby, (_, n)| number(ruby, n)),
|
|
404
|
+
_ => self.insert_each(frame, entries, number),
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// mini_racer decodes a result inside its [result, error] reply, one level
|
|
410
|
+
// below its limit.
|
|
411
|
+
const RESULT_LIMITS: Limits = Limits {
|
|
412
|
+
max_depth: 95,
|
|
413
|
+
max_bigint_bytes: 16 << 20,
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
pub fn value(ruby: &Ruby, bytes: &[u8]) -> Result<Value, Error> {
|
|
417
|
+
let mut visitor = RubyVisitor {
|
|
418
|
+
ruby,
|
|
419
|
+
refs: ruby.ary_new(),
|
|
420
|
+
pending: Pending {
|
|
421
|
+
target: None,
|
|
422
|
+
values: [ruby.qnil().as_value(); BATCH],
|
|
423
|
+
filled: 0,
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
decode_value(bytes, RESULT_LIMITS, &mut visitor).map_err(|e| match e {
|
|
427
|
+
DecodeError::Visitor(error) => error,
|
|
428
|
+
other => errors::runtime(ruby, other.to_string()),
|
|
429
|
+
})
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
#[cfg(test)]
|
|
433
|
+
mod tests {
|
|
434
|
+
use super::*;
|
|
435
|
+
|
|
436
|
+
#[test]
|
|
437
|
+
fn classifies_numbers_like_upstream() {
|
|
438
|
+
assert_eq!(classify(2.0), Number::Integer(2));
|
|
439
|
+
assert_eq!(classify(-0.0), Number::Integer(0));
|
|
440
|
+
assert_eq!(classify(1.5), Number::Float);
|
|
441
|
+
assert_eq!(classify(f64::NAN), Number::Float);
|
|
442
|
+
assert_eq!(classify(f64::INFINITY), Number::Float);
|
|
443
|
+
assert_eq!(classify(1e300), Number::BigInteger);
|
|
444
|
+
assert_eq!(
|
|
445
|
+
classify(-9_223_372_036_854_775_808.0),
|
|
446
|
+
Number::Integer(i64::MIN)
|
|
447
|
+
);
|
|
448
|
+
assert_eq!(classify(9_223_372_036_854_775_808.0), Number::BigInteger);
|
|
449
|
+
}
|
|
450
|
+
}
|