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.
@@ -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
+ }