mini_racer_rs 1.0.0 → 1.0.2
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/CHANGELOG.md +14 -0
- data/COMPATIBILITY.md +7 -1
- data/Cargo.lock +4 -4
- data/Cargo.toml +1 -1
- data/README.md +3 -5
- data/crates/mini_racer_engine/src/context.rs +11 -6
- data/crates/mini_racer_engine/src/control.rs +24 -3
- data/crates/mini_racer_engine/src/lib.rs +2 -1
- data/crates/mini_racer_engine/src/process/code_cache.rs +26 -15
- data/crates/mini_racer_engine/src/process/fork.rs +12 -2
- data/crates/mini_racer_engine/src/process/sources.rs +539 -0
- data/crates/mini_racer_engine/src/process.rs +1 -0
- data/crates/mini_racer_engine/src/request.rs +552 -0
- data/crates/mini_racer_engine/src/runner.rs +84 -28
- data/crates/mini_racer_engine/src/serialize.rs +65 -4
- data/crates/mini_racer_engine/src/sys.rs +91 -1
- data/crates/mini_racer_wire/src/lib.rs +1 -1
- data/crates/mini_racer_wire/src/write.rs +33 -1
- data/ext/mini_racer_rs/src/context.rs +7 -5
- data/ext/mini_racer_rs/src/decode.rs +11 -4
- data/ext/mini_racer_rs/src/encode.rs +221 -69
- data/lib/mini_racer_rs/version.rb +1 -1
- metadata +3 -1
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
use std::borrow::Cow;
|
|
2
|
+
use std::sync::atomic::{AtomicBool, Ordering};
|
|
3
|
+
|
|
4
|
+
use mini_racer_wire::{WRITE_VERSION, tag};
|
|
5
|
+
use v8::ValueDeserializerHelper;
|
|
6
|
+
|
|
7
|
+
// Deeper values are left to V8, which reads as deep as its stack allows.
|
|
8
|
+
const MAX_DEPTH: u32 = 64;
|
|
9
|
+
// Far below V8's own limit on an array's length, which it enforces itself.
|
|
10
|
+
const MAX_ELEMENTS: usize = 1 << 24;
|
|
11
|
+
// V8 throws for a BigInt past its limit, which this stays well short of.
|
|
12
|
+
const MAX_BIGINT_WORDS: usize = 1 << 20;
|
|
13
|
+
// V8's deserializer builds objects faster than the runner, which defines each
|
|
14
|
+
// property through the API, unless they hold two properties or fewer on
|
|
15
|
+
// average. A call with a few properties in all still gains, as it saves the
|
|
16
|
+
// deserializer's setup.
|
|
17
|
+
const FEW_PROPERTIES: usize = 4;
|
|
18
|
+
const PROPERTIES_PER_OBJECT: usize = 2;
|
|
19
|
+
// Each property grows the object's store a few slots at a time, so an object
|
|
20
|
+
// of many costs the square of their number; V8 reads the call instead.
|
|
21
|
+
const MAX_KEYS: usize = 64;
|
|
22
|
+
|
|
23
|
+
/// A call as `mini_racer`'s encoder sends it: the function's dotted path and
|
|
24
|
+
/// the arguments.
|
|
25
|
+
pub struct Call<'s, 'b> {
|
|
26
|
+
pub path: Cow<'b, str>,
|
|
27
|
+
pub args: Vec<v8::Local<'s, v8::Value>>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub enum Read<'s, 'b> {
|
|
31
|
+
Built(Call<'s, 'b>),
|
|
32
|
+
/// Left to V8's deserializer, which reads it as it always did.
|
|
33
|
+
Unbuilt,
|
|
34
|
+
/// V8's deserializer failed on a two-byte string in it, as it would have
|
|
35
|
+
/// on the whole, and its exception is pending.
|
|
36
|
+
Failed,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Whether the runner builds a call whose arguments hold `objects` objects
|
|
40
|
+
/// with `properties` properties in all, rather than V8's deserializer.
|
|
41
|
+
#[must_use]
|
|
42
|
+
pub const fn worth_building(objects: usize, properties: usize) -> bool {
|
|
43
|
+
properties <= FEW_PROPERTIES || properties <= objects.saturating_mul(PROPERTIES_PER_OBJECT)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Builds a call's arguments as V8's deserializer would, but with each array
|
|
47
|
+
/// packed, as an array built in JavaScript is: V8 serializes a holey array,
|
|
48
|
+
/// which its deserializer makes, element by element as a sparse array, so a
|
|
49
|
+
/// function returning an argument costs far more than it need.
|
|
50
|
+
///
|
|
51
|
+
/// A two-byte string is read by V8's own deserializer from its bytes alone.
|
|
52
|
+
/// The whole request is `Unbuilt` for anything else this would not build as V8
|
|
53
|
+
/// does or that V8 would refuse (a binary, a key that is not a string or an
|
|
54
|
+
/// int32, a key given twice, an array inside itself, an object of more than
|
|
55
|
+
/// `MAX_KEYS` keys, nesting past `MAX_DEPTH`), decided from the bytes before
|
|
56
|
+
/// any value is made. A termination requested as it reads leaves it `Unbuilt`
|
|
57
|
+
/// too: V8 acts on one before each value it reads, an array or object
|
|
58
|
+
/// beginning as a value does, so this looks before the request and before
|
|
59
|
+
/// each value. One requested before the dispatch refused it
|
|
60
|
+
/// (`TermCell::refuses`).
|
|
61
|
+
pub fn read<'s, 'b>(
|
|
62
|
+
scope: &v8::PinScope<'s, '_>,
|
|
63
|
+
bytes: &'b [u8],
|
|
64
|
+
termination_requested: &AtomicBool,
|
|
65
|
+
) -> Read<'s, 'b> {
|
|
66
|
+
if !buildable(bytes) {
|
|
67
|
+
return Read::Unbuilt;
|
|
68
|
+
}
|
|
69
|
+
let mut builder = Builder {
|
|
70
|
+
scope,
|
|
71
|
+
bytes: Cursor { rest: bytes },
|
|
72
|
+
termination_requested,
|
|
73
|
+
failed: false,
|
|
74
|
+
objects: Vec::new(),
|
|
75
|
+
elements: Vec::new(),
|
|
76
|
+
shapes: Vec::new(),
|
|
77
|
+
};
|
|
78
|
+
match builder.call() {
|
|
79
|
+
Some(call) => Read::Built(call),
|
|
80
|
+
None if builder.failed => Read::Failed,
|
|
81
|
+
None => Read::Unbuilt,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Every refusal but a termination is found here, before the builder makes
|
|
86
|
+
// anything: objects made for a call it then refused would leave V8's read of
|
|
87
|
+
// the whole call the shapes they generalized, and hold their memory while V8
|
|
88
|
+
// reads it all again.
|
|
89
|
+
fn buildable(bytes: &[u8]) -> bool {
|
|
90
|
+
Check {
|
|
91
|
+
bytes: Cursor { rest: bytes },
|
|
92
|
+
usable: Vec::new(),
|
|
93
|
+
keys: Vec::new(),
|
|
94
|
+
}
|
|
95
|
+
.call()
|
|
96
|
+
.is_some()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
struct Reader;
|
|
100
|
+
|
|
101
|
+
impl v8::ValueDeserializerImpl for Reader {}
|
|
102
|
+
|
|
103
|
+
#[derive(Clone, Copy)]
|
|
104
|
+
struct Cursor<'b> {
|
|
105
|
+
rest: &'b [u8],
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
impl<'b> Cursor<'b> {
|
|
109
|
+
fn byte(&mut self) -> Option<u8> {
|
|
110
|
+
let (&first, rest) = self.rest.split_first()?;
|
|
111
|
+
self.rest = rest;
|
|
112
|
+
Some(first)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const fn peek(&self) -> Option<u8> {
|
|
116
|
+
match self.rest.first() {
|
|
117
|
+
Some(&byte) => Some(byte),
|
|
118
|
+
None => None,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fn varint(&mut self) -> Option<u64> {
|
|
123
|
+
let mut value = 0u64;
|
|
124
|
+
for shift in (0..64).step_by(7) {
|
|
125
|
+
let byte = self.byte()?;
|
|
126
|
+
value |= u64::from(byte & 0x7f) << shift;
|
|
127
|
+
if byte & 0x80 == 0 {
|
|
128
|
+
return Some(value);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
None
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fn varint_u32(&mut self) -> Option<u32> {
|
|
135
|
+
u32::try_from(self.varint()?).ok()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fn take(&mut self, len: usize) -> Option<&'b [u8]> {
|
|
139
|
+
let (head, rest) = self.rest.split_at_checked(len)?;
|
|
140
|
+
self.rest = rest;
|
|
141
|
+
Some(head)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
fn text(&mut self) -> Option<&'b [u8]> {
|
|
145
|
+
let len = usize::try_from(self.varint_u32()?).ok()?;
|
|
146
|
+
self.take(len)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// V8's API refuses a string of more bytes than a string may have
|
|
150
|
+
// characters, even UTF-8 that decodes to fewer.
|
|
151
|
+
fn string(&mut self) -> Option<&'b [u8]> {
|
|
152
|
+
self.text()
|
|
153
|
+
.filter(|text| text.len() <= v8::String::MAX_LENGTH)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fn two_byte(&mut self) -> Option<()> {
|
|
157
|
+
let len = self.text()?.len();
|
|
158
|
+
(len.is_multiple_of(2) && len / 2 <= v8::String::MAX_LENGTH).then_some(())
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// The length of `[path, ...arguments]`, past V8's header.
|
|
162
|
+
fn request(&mut self) -> Option<u32> {
|
|
163
|
+
if self.byte()? != tag::HEADER
|
|
164
|
+
|| self.varint()? != u64::from(WRITE_VERSION)
|
|
165
|
+
|| self.byte()? != tag::BEGIN_DENSE_ARRAY
|
|
166
|
+
{
|
|
167
|
+
return None;
|
|
168
|
+
}
|
|
169
|
+
self.array_length().filter(|&length| length > 0)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// V8's path is the name converted to UTF-8, as a one-byte string's each
|
|
173
|
+
// byte becomes the character it is.
|
|
174
|
+
fn path(&mut self) -> Option<Cow<'b, str>> {
|
|
175
|
+
match self.byte()? {
|
|
176
|
+
tag::UTF8_STRING => std::str::from_utf8(self.string()?).ok().map(Cow::Borrowed),
|
|
177
|
+
tag::ONE_BYTE_STRING => {
|
|
178
|
+
let text = self.string()?;
|
|
179
|
+
Some(match std::str::from_utf8(text) {
|
|
180
|
+
Ok(ascii) if text.is_ascii() => Cow::Borrowed(ascii),
|
|
181
|
+
_ => Cow::Owned(text.iter().copied().map(char::from).collect()),
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
_ => None,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
fn key(&mut self) -> Option<Key<'b>> {
|
|
189
|
+
match self.byte()? {
|
|
190
|
+
tag::UTF8_STRING => {
|
|
191
|
+
let text = self.string()?;
|
|
192
|
+
std::str::from_utf8(text).ok()?;
|
|
193
|
+
Some(Key::Text(text))
|
|
194
|
+
}
|
|
195
|
+
tag::ONE_BYTE_STRING => {
|
|
196
|
+
let text = self.string()?;
|
|
197
|
+
text.is_ascii().then_some(Key::Text(text))
|
|
198
|
+
}
|
|
199
|
+
tag::INT32 => Some(Key::int(zigzag(self.varint_u32()?))),
|
|
200
|
+
_ => None,
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fn double(&mut self) -> Option<f64> {
|
|
205
|
+
let bytes: [u8; 8] = self.take(8)?.try_into().ok()?;
|
|
206
|
+
Some(f64::from_le_bytes(bytes))
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// V8 refuses an array longer than the bytes left, since every element
|
|
210
|
+
// takes at least one.
|
|
211
|
+
fn array_length(&mut self) -> Option<u32> {
|
|
212
|
+
let length = self.varint_u32()?;
|
|
213
|
+
usize::try_from(length)
|
|
214
|
+
.ok()
|
|
215
|
+
.filter(|&count| count <= self.rest.len() && count <= MAX_ELEMENTS)?;
|
|
216
|
+
Some(length)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fn array_end(&mut self, length: u32) -> Option<()> {
|
|
220
|
+
(self.byte()? == tag::END_DENSE_ARRAY
|
|
221
|
+
&& self.varint()? == 0
|
|
222
|
+
&& self.varint()? == u64::from(length))
|
|
223
|
+
.then_some(())
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
fn bigint_bytes(&mut self) -> Option<(bool, &'b [u8])> {
|
|
227
|
+
let bitfield = self.varint_u32()?;
|
|
228
|
+
let negative = bitfield & 1 == 1;
|
|
229
|
+
let len = usize::try_from(bitfield >> 1).ok()?;
|
|
230
|
+
// V8 has no negative zero, and the encoder writes whole words.
|
|
231
|
+
if !len.is_multiple_of(8) || len / 8 > MAX_BIGINT_WORDS || (negative && len == 0) {
|
|
232
|
+
return None;
|
|
233
|
+
}
|
|
234
|
+
Some((negative, self.take(len)?))
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// A key as V8 tells keys apart: an int32 by the digits of the property it names.
|
|
239
|
+
#[derive(Clone, Copy)]
|
|
240
|
+
enum Key<'b> {
|
|
241
|
+
Text(&'b [u8]),
|
|
242
|
+
Digits { buffer: [u8; 11], len: usize },
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
impl Key<'_> {
|
|
246
|
+
fn int(n: i32) -> Self {
|
|
247
|
+
let mut buffer = [0u8; 11];
|
|
248
|
+
let mut magnitude = n.unsigned_abs();
|
|
249
|
+
let mut len = 0;
|
|
250
|
+
loop {
|
|
251
|
+
let digit = u8::try_from(magnitude % 10).unwrap_or(0);
|
|
252
|
+
if let Some(slot) = buffer.get_mut(len) {
|
|
253
|
+
*slot = b'0' + digit;
|
|
254
|
+
}
|
|
255
|
+
len += 1;
|
|
256
|
+
magnitude /= 10;
|
|
257
|
+
if magnitude == 0 {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if n < 0 {
|
|
262
|
+
if let Some(slot) = buffer.get_mut(len) {
|
|
263
|
+
*slot = b'-';
|
|
264
|
+
}
|
|
265
|
+
len += 1;
|
|
266
|
+
}
|
|
267
|
+
if let Some(digits) = buffer.get_mut(..len) {
|
|
268
|
+
digits.reverse();
|
|
269
|
+
}
|
|
270
|
+
Self::Digits { buffer, len }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fn bytes(&self) -> &[u8] {
|
|
274
|
+
match self {
|
|
275
|
+
Self::Text(bytes) => bytes,
|
|
276
|
+
Self::Digits { buffer, len } => buffer.get(..*len).unwrap_or_default(),
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
impl PartialEq for Key<'_> {
|
|
282
|
+
fn eq(&self, other: &Self) -> bool {
|
|
283
|
+
self.bytes() == other.bytes()
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const fn zigzag(raw: u32) -> i32 {
|
|
288
|
+
let magnitude = (raw >> 1).cast_signed();
|
|
289
|
+
if raw & 1 == 0 { magnitude } else { !magnitude }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Reads a call as the builder does, making nothing.
|
|
293
|
+
struct Check<'b> {
|
|
294
|
+
bytes: Cursor<'b>,
|
|
295
|
+
// Whether a reference may name each id past the request array's, which
|
|
296
|
+
// none may: an array's only once it is complete, as the builder makes it
|
|
297
|
+
// only then.
|
|
298
|
+
usable: Vec<bool>,
|
|
299
|
+
// The keys of the objects being read, innermost last.
|
|
300
|
+
keys: Vec<Key<'b>>,
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
impl Check<'_> {
|
|
304
|
+
fn call(&mut self) -> Option<()> {
|
|
305
|
+
let length = self.bytes.request()?;
|
|
306
|
+
self.bytes.path()?;
|
|
307
|
+
for _ in 1..length {
|
|
308
|
+
self.value(MAX_DEPTH)?;
|
|
309
|
+
}
|
|
310
|
+
self.bytes.array_end(length)?;
|
|
311
|
+
self.bytes.rest.is_empty().then_some(())
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
fn value(&mut self, depth: u32) -> Option<()> {
|
|
315
|
+
match self.bytes.byte()? {
|
|
316
|
+
tag::INT32 => {
|
|
317
|
+
self.bytes.varint_u32()?;
|
|
318
|
+
}
|
|
319
|
+
tag::DOUBLE => {
|
|
320
|
+
self.bytes.double()?;
|
|
321
|
+
}
|
|
322
|
+
tag::NULL | tag::UNDEFINED | tag::TRUE | tag::FALSE => {}
|
|
323
|
+
tag::UTF8_STRING | tag::ONE_BYTE_STRING => {
|
|
324
|
+
self.bytes.string()?;
|
|
325
|
+
}
|
|
326
|
+
tag::TWO_BYTE_STRING => self.bytes.two_byte()?,
|
|
327
|
+
tag::BIGINT => {
|
|
328
|
+
self.bytes.bigint_bytes()?;
|
|
329
|
+
}
|
|
330
|
+
tag::DATE => {
|
|
331
|
+
self.bytes.double()?;
|
|
332
|
+
self.usable.push(true);
|
|
333
|
+
}
|
|
334
|
+
tag::OBJECT_REFERENCE => {
|
|
335
|
+
let id = usize::try_from(self.bytes.varint_u32()?).ok()?;
|
|
336
|
+
self.usable
|
|
337
|
+
.get(id.checked_sub(1)?)
|
|
338
|
+
.copied()
|
|
339
|
+
.filter(|&usable| usable)?;
|
|
340
|
+
}
|
|
341
|
+
tag::BEGIN_DENSE_ARRAY => self.array(depth.checked_sub(1)?)?,
|
|
342
|
+
tag::BEGIN_OBJECT => self.object(depth.checked_sub(1)?)?,
|
|
343
|
+
// A binary among them: V8's API aborts the process where its
|
|
344
|
+
// deserializer throws when it cannot allocate a buffer.
|
|
345
|
+
_ => return None,
|
|
346
|
+
}
|
|
347
|
+
Some(())
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
fn array(&mut self, depth: u32) -> Option<()> {
|
|
351
|
+
let length = self.bytes.array_length()?;
|
|
352
|
+
let id = self.usable.len();
|
|
353
|
+
self.usable.push(false);
|
|
354
|
+
for _ in 0..length {
|
|
355
|
+
self.value(depth)?;
|
|
356
|
+
}
|
|
357
|
+
self.bytes.array_end(length)?;
|
|
358
|
+
*self.usable.get_mut(id)? = true;
|
|
359
|
+
Some(())
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
fn object(&mut self, depth: u32) -> Option<()> {
|
|
363
|
+
self.usable.push(true);
|
|
364
|
+
let start = self.keys.len();
|
|
365
|
+
let mut count = 0usize;
|
|
366
|
+
while self.bytes.peek()? != tag::END_OBJECT {
|
|
367
|
+
let key = self.bytes.key()?;
|
|
368
|
+
// V8's deserializer fails on a key the object already has.
|
|
369
|
+
if self.keys.get(start..)?.contains(&key) {
|
|
370
|
+
return None;
|
|
371
|
+
}
|
|
372
|
+
self.keys.push(key);
|
|
373
|
+
self.value(depth)?;
|
|
374
|
+
count = count.checked_add(1).filter(|&count| count <= MAX_KEYS)?;
|
|
375
|
+
}
|
|
376
|
+
self.bytes.byte()?;
|
|
377
|
+
self.keys.truncate(start);
|
|
378
|
+
(self.bytes.varint()? == u64::try_from(count).ok()?).then_some(())
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
struct Builder<'s, 'b, 'p, 'i> {
|
|
383
|
+
scope: &'p v8::PinScope<'s, 'i>,
|
|
384
|
+
bytes: Cursor<'b>,
|
|
385
|
+
termination_requested: &'p AtomicBool,
|
|
386
|
+
failed: bool,
|
|
387
|
+
// The value of each id V8 gives an object as it reads past the request
|
|
388
|
+
// array's; an array has none until it is complete, where V8's would
|
|
389
|
+
// already have one.
|
|
390
|
+
objects: Vec<Option<v8::Local<'s, v8::Value>>>,
|
|
391
|
+
// The elements of the arrays being read, innermost last.
|
|
392
|
+
elements: Vec<v8::Local<'s, v8::Value>>,
|
|
393
|
+
// The names last made for each key position at each depth, so objects of
|
|
394
|
+
// one shape share them instead of looking each up again.
|
|
395
|
+
shapes: Vec<Vec<(Key<'b>, v8::Local<'s, v8::String>)>>,
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Reads again what `Check` accepted, so only a termination stops it, or V8's
|
|
399
|
+
// failed read of a two-byte string (`Read::Failed`), which would have failed
|
|
400
|
+
// V8's read of the whole call too.
|
|
401
|
+
impl<'s, 'b> Builder<'s, 'b, '_, '_> {
|
|
402
|
+
fn unterminated(&self) -> Option<()> {
|
|
403
|
+
(!self.termination_requested.load(Ordering::Acquire)).then_some(())
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
fn call(&mut self) -> Option<Call<'s, 'b>> {
|
|
407
|
+
self.unterminated()?;
|
|
408
|
+
let length = self.bytes.request()?;
|
|
409
|
+
let path = self.bytes.path()?;
|
|
410
|
+
let mut args = Vec::with_capacity(usize::try_from(length).ok()?.saturating_sub(1));
|
|
411
|
+
for _ in 1..length {
|
|
412
|
+
args.push(self.value(MAX_DEPTH)?);
|
|
413
|
+
}
|
|
414
|
+
self.bytes.array_end(length)?;
|
|
415
|
+
Some(Call { path, args })
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
fn value(&mut self, depth: u32) -> Option<v8::Local<'s, v8::Value>> {
|
|
419
|
+
self.unterminated()?;
|
|
420
|
+
let scope = self.scope;
|
|
421
|
+
let at = self.bytes;
|
|
422
|
+
Some(match self.bytes.byte()? {
|
|
423
|
+
tag::INT32 => v8::Integer::new(scope, zigzag(self.bytes.varint_u32()?)).into(),
|
|
424
|
+
tag::DOUBLE => v8::Number::new(scope, self.bytes.double()?).into(),
|
|
425
|
+
tag::NULL => v8::null(scope).into(),
|
|
426
|
+
tag::UNDEFINED => v8::undefined(scope).into(),
|
|
427
|
+
tag::TRUE => v8::Boolean::new(scope, true).into(),
|
|
428
|
+
tag::FALSE => v8::Boolean::new(scope, false).into(),
|
|
429
|
+
tag::UTF8_STRING => {
|
|
430
|
+
v8::String::new_from_utf8(scope, self.bytes.string()?, v8::NewStringType::Normal)?
|
|
431
|
+
.into()
|
|
432
|
+
}
|
|
433
|
+
tag::ONE_BYTE_STRING => v8::String::new_from_one_byte(
|
|
434
|
+
scope,
|
|
435
|
+
self.bytes.string()?,
|
|
436
|
+
v8::NewStringType::Normal,
|
|
437
|
+
)?
|
|
438
|
+
.into(),
|
|
439
|
+
tag::BIGINT => {
|
|
440
|
+
let (negative, digits) = self.bytes.bigint_bytes()?;
|
|
441
|
+
let words: Vec<u64> = digits
|
|
442
|
+
.as_chunks::<8>()
|
|
443
|
+
.0
|
|
444
|
+
.iter()
|
|
445
|
+
.map(|word| u64::from_le_bytes(*word))
|
|
446
|
+
.collect();
|
|
447
|
+
v8::BigInt::new_from_words(scope, negative, &words)?.into()
|
|
448
|
+
}
|
|
449
|
+
tag::DATE => {
|
|
450
|
+
let date: v8::Local<'s, v8::Value> =
|
|
451
|
+
v8::Date::new(scope, self.bytes.double()?)?.into();
|
|
452
|
+
self.objects.push(Some(date));
|
|
453
|
+
date
|
|
454
|
+
}
|
|
455
|
+
tag::OBJECT_REFERENCE => {
|
|
456
|
+
let id = usize::try_from(self.bytes.varint_u32()?).ok()?;
|
|
457
|
+
(*self.objects.get(id.checked_sub(1)?)?)?
|
|
458
|
+
}
|
|
459
|
+
tag::BEGIN_DENSE_ARRAY => self.array(depth.checked_sub(1)?)?,
|
|
460
|
+
tag::BEGIN_OBJECT => self.object(depth.checked_sub(1)?)?,
|
|
461
|
+
tag::TWO_BYTE_STRING => {
|
|
462
|
+
self.bytes.two_byte()?;
|
|
463
|
+
self.two_byte(at)?
|
|
464
|
+
}
|
|
465
|
+
_ => return None,
|
|
466
|
+
})
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Reads the string from `at` up to the cursor with V8's own deserializer,
|
|
470
|
+
// which keeps it two-byte, where V8's API makes a one-byte string of
|
|
471
|
+
// whatever fits.
|
|
472
|
+
fn two_byte(&mut self, at: Cursor<'b>) -> Option<v8::Local<'s, v8::Value>> {
|
|
473
|
+
let len = at.rest.len().checked_sub(self.bytes.rest.len())?;
|
|
474
|
+
let mut bytes = vec![tag::HEADER, u8::try_from(WRITE_VERSION).ok()?];
|
|
475
|
+
bytes.extend_from_slice(at.rest.get(..len)?);
|
|
476
|
+
let context = self.scope.get_current_context();
|
|
477
|
+
let deserializer = v8::ValueDeserializer::new(self.scope, Box::new(Reader), &bytes);
|
|
478
|
+
let value = (deserializer.read_header(context) == Some(true))
|
|
479
|
+
.then(|| deserializer.read_value(context))
|
|
480
|
+
.flatten();
|
|
481
|
+
if value.is_none() {
|
|
482
|
+
self.failed = true;
|
|
483
|
+
}
|
|
484
|
+
value
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
fn array(&mut self, depth: u32) -> Option<v8::Local<'s, v8::Value>> {
|
|
488
|
+
let length = self.bytes.array_length()?;
|
|
489
|
+
let id = self.objects.len();
|
|
490
|
+
self.objects.push(None);
|
|
491
|
+
let start = self.elements.len();
|
|
492
|
+
for _ in 0..length {
|
|
493
|
+
let element = self.value(depth)?;
|
|
494
|
+
self.elements.push(element);
|
|
495
|
+
}
|
|
496
|
+
self.bytes.array_end(length)?;
|
|
497
|
+
let array = v8::Array::new_with_elements(self.scope, self.elements.get(start..)?).into();
|
|
498
|
+
self.elements.truncate(start);
|
|
499
|
+
*self.objects.get_mut(id)? = Some(array);
|
|
500
|
+
Some(array)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Each property is defined as V8's deserializer defines it, never set, so
|
|
504
|
+
// no setter or prototype sees it.
|
|
505
|
+
fn object(&mut self, depth: u32) -> Option<v8::Local<'s, v8::Value>> {
|
|
506
|
+
let object = v8::Object::new(self.scope);
|
|
507
|
+
self.objects.push(Some(object.into()));
|
|
508
|
+
let mut position = 0usize;
|
|
509
|
+
while self.bytes.peek()? != tag::END_OBJECT {
|
|
510
|
+
let key = self.key(position, depth)?;
|
|
511
|
+
let value = self.value(depth)?;
|
|
512
|
+
object
|
|
513
|
+
.create_data_property(self.scope, key, value)
|
|
514
|
+
.filter(|&created| created)?;
|
|
515
|
+
position = position.checked_add(1)?;
|
|
516
|
+
}
|
|
517
|
+
self.bytes.byte()?;
|
|
518
|
+
self.bytes.varint()?;
|
|
519
|
+
Some(object.into())
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
fn key(&mut self, position: usize, depth: u32) -> Option<v8::Local<'s, v8::Name>> {
|
|
523
|
+
let key = self.bytes.key()?;
|
|
524
|
+
let level = usize::try_from(MAX_DEPTH.checked_sub(depth)?).ok()?;
|
|
525
|
+
if self.shapes.len() <= level {
|
|
526
|
+
self.shapes.resize_with(level.checked_add(1)?, Vec::new);
|
|
527
|
+
}
|
|
528
|
+
let shape = self.shapes.get_mut(level)?;
|
|
529
|
+
if let Some((known, name)) = shape.get(position)
|
|
530
|
+
&& *known == key
|
|
531
|
+
{
|
|
532
|
+
return Some((*name).into());
|
|
533
|
+
}
|
|
534
|
+
let name = match key {
|
|
535
|
+
Key::Text(text) => {
|
|
536
|
+
v8::String::new_from_utf8(self.scope, text, v8::NewStringType::Internalized)?
|
|
537
|
+
}
|
|
538
|
+
Key::Digits { .. } => v8::String::new_from_one_byte(
|
|
539
|
+
self.scope,
|
|
540
|
+
key.bytes(),
|
|
541
|
+
v8::NewStringType::Internalized,
|
|
542
|
+
)?,
|
|
543
|
+
};
|
|
544
|
+
let shape = self.shapes.get_mut(level)?;
|
|
545
|
+
if let Some(slot) = shape.get_mut(position) {
|
|
546
|
+
*slot = (key, name);
|
|
547
|
+
} else if shape.len() == position {
|
|
548
|
+
shape.push((key, name));
|
|
549
|
+
}
|
|
550
|
+
Some(name.into())
|
|
551
|
+
}
|
|
552
|
+
}
|