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,67 @@
|
|
|
1
|
+
#![forbid(unsafe_code)]
|
|
2
|
+
#![deny(
|
|
3
|
+
clippy::arithmetic_side_effects,
|
|
4
|
+
clippy::cast_possible_truncation,
|
|
5
|
+
clippy::indexing_slicing
|
|
6
|
+
)]
|
|
7
|
+
|
|
8
|
+
mod decode;
|
|
9
|
+
pub mod marker;
|
|
10
|
+
pub mod text;
|
|
11
|
+
mod write;
|
|
12
|
+
|
|
13
|
+
use std::ops::RangeInclusive;
|
|
14
|
+
|
|
15
|
+
pub use decode::{
|
|
16
|
+
Begun, Container, DecodeError, Limits, Scalar, Text, Visitor, decode_value, decode_values,
|
|
17
|
+
};
|
|
18
|
+
pub use write::{WriteError, Writer};
|
|
19
|
+
|
|
20
|
+
pub const READ_VERSIONS: RangeInclusive<u32> = 13..=16;
|
|
21
|
+
pub const WRITE_VERSION: u32 = 15;
|
|
22
|
+
|
|
23
|
+
mod tag {
|
|
24
|
+
pub const HEADER: u8 = 0xFF;
|
|
25
|
+
pub const PADDING: u8 = 0x00;
|
|
26
|
+
pub const VERIFY_OBJECT_COUNT: u8 = b'?';
|
|
27
|
+
pub const THE_HOLE: u8 = b'-';
|
|
28
|
+
pub const UNDEFINED: u8 = b'_';
|
|
29
|
+
pub const NULL: u8 = b'0';
|
|
30
|
+
pub const TRUE: u8 = b'T';
|
|
31
|
+
pub const FALSE: u8 = b'F';
|
|
32
|
+
pub const INT32: u8 = b'I';
|
|
33
|
+
pub const UINT32: u8 = b'U';
|
|
34
|
+
pub const DOUBLE: u8 = b'N';
|
|
35
|
+
pub const BIGINT: u8 = b'Z';
|
|
36
|
+
pub const UTF8_STRING: u8 = b'S';
|
|
37
|
+
pub const ONE_BYTE_STRING: u8 = b'"';
|
|
38
|
+
pub const TWO_BYTE_STRING: u8 = b'c';
|
|
39
|
+
pub const OBJECT_REFERENCE: u8 = b'^';
|
|
40
|
+
pub const BEGIN_OBJECT: u8 = b'o';
|
|
41
|
+
pub const END_OBJECT: u8 = b'{';
|
|
42
|
+
pub const BEGIN_SPARSE_ARRAY: u8 = b'a';
|
|
43
|
+
pub const END_SPARSE_ARRAY: u8 = b'@';
|
|
44
|
+
pub const BEGIN_DENSE_ARRAY: u8 = b'A';
|
|
45
|
+
pub const END_DENSE_ARRAY: u8 = b'$';
|
|
46
|
+
pub const DATE: u8 = b'D';
|
|
47
|
+
pub const TRUE_OBJECT: u8 = b'y';
|
|
48
|
+
pub const FALSE_OBJECT: u8 = b'x';
|
|
49
|
+
pub const NUMBER_OBJECT: u8 = b'n';
|
|
50
|
+
pub const BIGINT_OBJECT: u8 = b'z';
|
|
51
|
+
pub const STRING_OBJECT: u8 = b's';
|
|
52
|
+
pub const REGEXP: u8 = b'R';
|
|
53
|
+
pub const BEGIN_MAP: u8 = b';';
|
|
54
|
+
pub const END_MAP: u8 = b':';
|
|
55
|
+
pub const BEGIN_SET: u8 = b'\'';
|
|
56
|
+
pub const END_SET: u8 = b',';
|
|
57
|
+
pub const ARRAY_BUFFER: u8 = b'B';
|
|
58
|
+
pub const IMMUTABLE_ARRAY_BUFFER: u8 = b'C';
|
|
59
|
+
pub const RESIZABLE_ARRAY_BUFFER: u8 = b'~';
|
|
60
|
+
pub const ARRAY_BUFFER_VIEW: u8 = b'V';
|
|
61
|
+
pub const ERROR: u8 = b'r';
|
|
62
|
+
pub const ERROR_MESSAGE: u8 = b'm';
|
|
63
|
+
pub const ERROR_STACK: u8 = b's';
|
|
64
|
+
pub const ERROR_CAUSE: u8 = b'c';
|
|
65
|
+
pub const ERROR_END: u8 = b'.';
|
|
66
|
+
pub const UINT8_ARRAY: u8 = b'B';
|
|
67
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//! `mini_racer` stands a two-byte string in for a function result, and its
|
|
2
|
+
//! Ruby side turns exactly that string into a `MiniRacer::JavaScriptFunction`.
|
|
3
|
+
|
|
4
|
+
/// U+0BFF, then `JavaScriptFunction`.
|
|
5
|
+
pub const JAVASCRIPT_FUNCTION: [u16; 19] = [
|
|
6
|
+
0x0BFF, 0x4A, 0x61, 0x76, 0x61, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x46, 0x75, 0x6E, 0x63,
|
|
7
|
+
0x74, 0x69, 0x6F, 0x6E,
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
/// Whether UTF-16LE `bytes` are the marker and nothing else.
|
|
11
|
+
#[must_use]
|
|
12
|
+
pub fn is_javascript_function(bytes: &[u8]) -> bool {
|
|
13
|
+
let (pairs, rest) = bytes.as_chunks::<2>();
|
|
14
|
+
rest.is_empty()
|
|
15
|
+
&& pairs.len() == JAVASCRIPT_FUNCTION.len()
|
|
16
|
+
&& pairs
|
|
17
|
+
.iter()
|
|
18
|
+
.zip(JAVASCRIPT_FUNCTION)
|
|
19
|
+
.all(|(pair, unit)| *pair == unit.to_le_bytes())
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[cfg(test)]
|
|
23
|
+
mod tests {
|
|
24
|
+
use super::*;
|
|
25
|
+
|
|
26
|
+
fn utf16le(text: &str) -> Vec<u8> {
|
|
27
|
+
text.encode_utf16().flat_map(u16::to_le_bytes).collect()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#[test]
|
|
31
|
+
fn only_the_whole_marker_matches() {
|
|
32
|
+
assert!(is_javascript_function(&utf16le(
|
|
33
|
+
"\u{bff}JavaScriptFunction"
|
|
34
|
+
)));
|
|
35
|
+
assert!(!is_javascript_function(&utf16le(
|
|
36
|
+
"\u{bff}JavaScriptFunctio"
|
|
37
|
+
)));
|
|
38
|
+
assert!(!is_javascript_function(&utf16le(
|
|
39
|
+
"\u{bff}JavaScriptFunctions"
|
|
40
|
+
)));
|
|
41
|
+
assert!(!is_javascript_function(&utf16le("JavaScriptFunction")));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Returned HTML is mostly ASCII, which is copied in spans; only blocks holding
|
|
2
|
+
// another byte are converted a byte at a time.
|
|
3
|
+
const LATIN1_BLOCK: usize = 32;
|
|
4
|
+
// Each block's count of non-ASCII bytes is summed in a u8.
|
|
5
|
+
const _: () = assert!(LATIN1_BLOCK <= u8::MAX as usize);
|
|
6
|
+
|
|
7
|
+
#[expect(clippy::expect_used, reason = "callers pass only spans of ASCII bytes")]
|
|
8
|
+
fn ascii(bytes: &[u8]) -> &str {
|
|
9
|
+
std::str::from_utf8(bytes).expect("ASCII is UTF-8")
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[must_use]
|
|
13
|
+
pub fn latin1_to_utf8(bytes: &[u8]) -> String {
|
|
14
|
+
if bytes.len() < LATIN1_BLOCK {
|
|
15
|
+
return bytes.iter().copied().map(char::from).collect();
|
|
16
|
+
}
|
|
17
|
+
let (wide, mixed) =
|
|
18
|
+
bytes
|
|
19
|
+
.chunks(LATIN1_BLOCK)
|
|
20
|
+
.fold((0_usize, 0_usize), |(wide, mixed), block| {
|
|
21
|
+
let count = block
|
|
22
|
+
.iter()
|
|
23
|
+
.map(|&byte| u8::from(!byte.is_ascii()))
|
|
24
|
+
.sum::<u8>();
|
|
25
|
+
(
|
|
26
|
+
wide.saturating_add(usize::from(count)),
|
|
27
|
+
mixed.saturating_add(usize::from(count > 0)),
|
|
28
|
+
)
|
|
29
|
+
});
|
|
30
|
+
let mut out = String::with_capacity(bytes.len().saturating_add(wide));
|
|
31
|
+
// Text where most blocks need converting anyway, like accented prose, is
|
|
32
|
+
// faster a byte at a time throughout.
|
|
33
|
+
if mixed.saturating_mul(LATIN1_BLOCK).saturating_mul(2) > bytes.len() {
|
|
34
|
+
out.extend(bytes.iter().copied().map(char::from));
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
let mut span = 0;
|
|
38
|
+
let mut at: usize = 0;
|
|
39
|
+
for block in bytes.chunks(LATIN1_BLOCK) {
|
|
40
|
+
let next = at.saturating_add(block.len());
|
|
41
|
+
if !block.is_ascii() {
|
|
42
|
+
if span < at {
|
|
43
|
+
out.push_str(ascii(bytes.get(span..at).unwrap_or_default()));
|
|
44
|
+
}
|
|
45
|
+
for &byte in block {
|
|
46
|
+
out.push(char::from(byte));
|
|
47
|
+
}
|
|
48
|
+
span = next;
|
|
49
|
+
}
|
|
50
|
+
at = next;
|
|
51
|
+
}
|
|
52
|
+
out.push_str(ascii(bytes.get(span..).unwrap_or_default()));
|
|
53
|
+
out
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[must_use]
|
|
57
|
+
pub fn utf16le_to_utf8(bytes: &[u8]) -> Option<String> {
|
|
58
|
+
let (pairs, rest) = bytes.as_chunks::<2>();
|
|
59
|
+
if !rest.is_empty() {
|
|
60
|
+
return None;
|
|
61
|
+
}
|
|
62
|
+
let units = pairs.iter().copied().map(u16::from_le_bytes);
|
|
63
|
+
char::decode_utf16(units)
|
|
64
|
+
.collect::<Result<String, _>>()
|
|
65
|
+
.ok()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#[cfg(test)]
|
|
69
|
+
mod tests {
|
|
70
|
+
use proptest::prelude::*;
|
|
71
|
+
|
|
72
|
+
use super::*;
|
|
73
|
+
|
|
74
|
+
#[test]
|
|
75
|
+
fn latin1_maps_bytes_to_code_points() {
|
|
76
|
+
assert_eq!(latin1_to_utf8(b"abc"), "abc");
|
|
77
|
+
assert_eq!(latin1_to_utf8(&[0x63, 0x61, 0x66, 0xE9]), "café");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#[test]
|
|
81
|
+
fn utf16_rejects_odd_lengths_and_lone_surrogates() {
|
|
82
|
+
assert_eq!(
|
|
83
|
+
utf16le_to_utf8(&[0x3D, 0xD8, 0x00, 0xDE]).as_deref(),
|
|
84
|
+
Some("😀")
|
|
85
|
+
);
|
|
86
|
+
assert_eq!(utf16le_to_utf8(&[0x41]), None);
|
|
87
|
+
assert_eq!(utf16le_to_utf8(&[0x3D, 0xD8]), None);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
proptest! {
|
|
91
|
+
// ASCII runs of every length with other bytes between them, so whole
|
|
92
|
+
// ASCII blocks, mixed blocks and spans across block edges all come up.
|
|
93
|
+
#[test]
|
|
94
|
+
fn latin1_matches_mapping_each_byte(segments in proptest::collection::vec(
|
|
95
|
+
(
|
|
96
|
+
proptest::collection::vec(0u8..0x80, 0..80),
|
|
97
|
+
proptest::collection::vec(0x80u8..=0xFF, 0..3),
|
|
98
|
+
),
|
|
99
|
+
0..24,
|
|
100
|
+
)) {
|
|
101
|
+
let bytes: Vec<u8> = segments.into_iter().flat_map(|(run, wide)| run.into_iter().chain(wide)).collect();
|
|
102
|
+
let by_byte: String = bytes.iter().copied().map(char::from).collect();
|
|
103
|
+
prop_assert_eq!(latin1_to_utf8(&bytes), by_byte);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
use crate::{WRITE_VERSION, tag};
|
|
2
|
+
|
|
3
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
|
4
|
+
pub enum WriteError {
|
|
5
|
+
#[error("bad bigint")]
|
|
6
|
+
BigInt,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// The bit pattern mini_racer writes for every NaN.
|
|
10
|
+
const CANONICAL_NAN: f64 = f64::from_bits(0x7FF8_0000_0000_0000);
|
|
11
|
+
|
|
12
|
+
/// Writes the V8 wire format the way `mini_racer`'s encoder does, into a buffer
|
|
13
|
+
/// the caller owns and can reuse.
|
|
14
|
+
///
|
|
15
|
+
/// Each method that writes an object returns the id V8 gives that object on
|
|
16
|
+
/// reading, for `object_ref`.
|
|
17
|
+
pub struct Writer<'a> {
|
|
18
|
+
out: &'a mut Vec<u8>,
|
|
19
|
+
next_id: u32,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl<'a> Writer<'a> {
|
|
23
|
+
/// Starts a frame: clears `out` and writes the header.
|
|
24
|
+
pub fn new(out: &'a mut Vec<u8>) -> Self {
|
|
25
|
+
out.clear();
|
|
26
|
+
out.push(tag::HEADER);
|
|
27
|
+
let mut writer = Self { out, next_id: 0 };
|
|
28
|
+
writer.varint(WRITE_VERSION.into());
|
|
29
|
+
writer
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const fn id(&mut self) -> u32 {
|
|
33
|
+
let id = self.next_id;
|
|
34
|
+
self.next_id = id.saturating_add(1);
|
|
35
|
+
id
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn varint(&mut self, mut value: u64) {
|
|
39
|
+
while value > 0x7F {
|
|
40
|
+
self.out
|
|
41
|
+
.push(0x80 | u8::try_from(value & 0x7F).unwrap_or(0));
|
|
42
|
+
value >>= 7;
|
|
43
|
+
}
|
|
44
|
+
self.out.push(u8::try_from(value).unwrap_or(u8::MAX));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn zigzag(&mut self, value: i32) {
|
|
48
|
+
self.varint(u64::from(((value << 1) ^ (value >> 31)).cast_unsigned()));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fn len(&mut self, len: usize) {
|
|
52
|
+
self.varint(u64::try_from(len).unwrap_or(u64::MAX));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub fn null(&mut self) {
|
|
56
|
+
self.out.push(tag::NULL);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn undefined(&mut self) {
|
|
60
|
+
self.out.push(tag::UNDEFINED);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pub fn bool(&mut self, value: bool) {
|
|
64
|
+
self.out.push(if value { tag::TRUE } else { tag::FALSE });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
pub fn number(&mut self, value: f64) {
|
|
68
|
+
self.out.push(tag::DOUBLE);
|
|
69
|
+
let value = if value.is_nan() { CANONICAL_NAN } else { value };
|
|
70
|
+
self.out.extend_from_slice(&value.to_le_bytes());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// An int32 when it fits; otherwise, as `mini_racer` does, a double up to
|
|
74
|
+
/// `2**63 / 1024` in magnitude, and a `BigInt` beyond.
|
|
75
|
+
pub fn integer(&mut self, value: i64) {
|
|
76
|
+
if let Ok(small) = i32::try_from(value) {
|
|
77
|
+
self.out.push(tag::INT32);
|
|
78
|
+
self.zigzag(small);
|
|
79
|
+
} else if value > i64::MIN / 1024 && value <= i64::MAX / 1024 {
|
|
80
|
+
#[expect(
|
|
81
|
+
clippy::cast_precision_loss,
|
|
82
|
+
reason = "mini_racer rounds these to a double too"
|
|
83
|
+
)]
|
|
84
|
+
self.number(value as f64);
|
|
85
|
+
} else {
|
|
86
|
+
let magnitude = value.unsigned_abs().to_le_bytes();
|
|
87
|
+
// Eight bytes always form one whole word.
|
|
88
|
+
let _ = self.bigint(value < 0, &magnitude);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// `digits` is the magnitude as little-endian 64-bit words.
|
|
93
|
+
pub fn bigint(&mut self, negative: bool, digits: &[u8]) -> Result<(), WriteError> {
|
|
94
|
+
if !digits.len().is_multiple_of(8) {
|
|
95
|
+
return Err(WriteError::BigInt);
|
|
96
|
+
}
|
|
97
|
+
let used = digits
|
|
98
|
+
.iter()
|
|
99
|
+
.rposition(|&byte| byte != 0)
|
|
100
|
+
.map_or(0, |last| last.saturating_add(1));
|
|
101
|
+
self.out.push(tag::BIGINT);
|
|
102
|
+
if used == 0 {
|
|
103
|
+
self.out.push(0);
|
|
104
|
+
return Ok(());
|
|
105
|
+
}
|
|
106
|
+
let words = used.div_ceil(8).saturating_mul(8);
|
|
107
|
+
let bitfield =
|
|
108
|
+
u64::try_from(words).unwrap_or(u64::MAX).saturating_mul(2) | u64::from(negative);
|
|
109
|
+
self.varint(bitfield);
|
|
110
|
+
self.out
|
|
111
|
+
.extend_from_slice(digits.get(..words).unwrap_or(digits));
|
|
112
|
+
Ok(())
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pub fn utf8(&mut self, bytes: &[u8]) {
|
|
116
|
+
self.out.push(tag::UTF8_STRING);
|
|
117
|
+
self.len(bytes.len());
|
|
118
|
+
self.out.extend_from_slice(bytes);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pub fn latin1(&mut self, bytes: &[u8]) {
|
|
122
|
+
self.out.push(tag::ONE_BYTE_STRING);
|
|
123
|
+
self.len(bytes.len());
|
|
124
|
+
self.out.extend_from_slice(bytes);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// `bytes` is UTF-16LE; its length is in bytes.
|
|
128
|
+
pub fn utf16le(&mut self, bytes: &[u8]) {
|
|
129
|
+
self.out.push(tag::TWO_BYTE_STRING);
|
|
130
|
+
self.len(bytes.len());
|
|
131
|
+
self.out.extend_from_slice(bytes);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Milliseconds since the Unix epoch; non-finite times become NaN.
|
|
135
|
+
pub fn date(&mut self, millis: f64) -> u32 {
|
|
136
|
+
self.out.push(tag::DATE);
|
|
137
|
+
let millis = if millis.is_finite() {
|
|
138
|
+
millis
|
|
139
|
+
} else {
|
|
140
|
+
CANONICAL_NAN
|
|
141
|
+
};
|
|
142
|
+
self.out.extend_from_slice(&millis.to_le_bytes());
|
|
143
|
+
self.id()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// A `Uint8Array` over a fresh buffer holding `bytes`. The id is the
|
|
147
|
+
/// view's; the buffer takes the one before it.
|
|
148
|
+
pub fn uint8_array(&mut self, bytes: &[u8]) -> u32 {
|
|
149
|
+
self.out.push(tag::ARRAY_BUFFER);
|
|
150
|
+
self.len(bytes.len());
|
|
151
|
+
self.out.extend_from_slice(bytes);
|
|
152
|
+
self.out.push(tag::ARRAY_BUFFER_VIEW);
|
|
153
|
+
self.out.push(tag::UINT8_ARRAY);
|
|
154
|
+
self.varint(0);
|
|
155
|
+
self.len(bytes.len());
|
|
156
|
+
self.varint(0);
|
|
157
|
+
self.id();
|
|
158
|
+
self.id()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
pub fn begin_object(&mut self) -> u32 {
|
|
162
|
+
self.out.push(tag::BEGIN_OBJECT);
|
|
163
|
+
self.id()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
pub fn end_object(&mut self, properties: u32) {
|
|
167
|
+
self.out.push(tag::END_OBJECT);
|
|
168
|
+
self.varint(properties.into());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
pub fn begin_array(&mut self, length: u32) -> u32 {
|
|
172
|
+
self.out.push(tag::BEGIN_DENSE_ARRAY);
|
|
173
|
+
self.varint(length.into());
|
|
174
|
+
self.id()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
pub fn end_array(&mut self, length: u32) {
|
|
178
|
+
self.out.push(tag::END_DENSE_ARRAY);
|
|
179
|
+
self.varint(0);
|
|
180
|
+
self.varint(length.into());
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
pub fn object_ref(&mut self, id: u32) {
|
|
184
|
+
self.out.push(tag::OBJECT_REFERENCE);
|
|
185
|
+
self.varint(id.into());
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
build = "build.rs"
|
|
3
|
+
description = "Ruby extension for mini_racer_rs"
|
|
4
|
+
edition.workspace = true
|
|
5
|
+
license.workspace = true
|
|
6
|
+
name = "mini_racer_rs"
|
|
7
|
+
publish.workspace = true
|
|
8
|
+
repository.workspace = true
|
|
9
|
+
rust-version.workspace = true
|
|
10
|
+
version.workspace = true
|
|
11
|
+
|
|
12
|
+
[lib]
|
|
13
|
+
crate-type = ["cdylib"]
|
|
14
|
+
|
|
15
|
+
[features]
|
|
16
|
+
no-madvise-wrap = ["mini_racer_engine/no-madvise-wrap"]
|
|
17
|
+
|
|
18
|
+
[dependencies]
|
|
19
|
+
magnus.workspace = true
|
|
20
|
+
mini_racer_engine.workspace = true
|
|
21
|
+
mini_racer_wire.workspace = true
|
|
22
|
+
rb-sys.workspace = true
|
|
23
|
+
thiserror.workspace = true
|
|
24
|
+
|
|
25
|
+
[lints]
|
|
26
|
+
workspace = true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#![allow(
|
|
2
|
+
clippy::print_stdout,
|
|
3
|
+
reason = "cargo reads build script directives from stdout"
|
|
4
|
+
)]
|
|
5
|
+
|
|
6
|
+
fn main() {
|
|
7
|
+
println!("cargo::rerun-if-env-changed=DEP_MINI_RACER_MADVISE_WRAP");
|
|
8
|
+
if std::env::var("DEP_MINI_RACER_MADVISE_WRAP").is_ok_and(|v| v == "madvise") {
|
|
9
|
+
println!("cargo::rustc-link-arg-cdylib=-Wl,--wrap=madvise");
|
|
10
|
+
}
|
|
11
|
+
// Ruby dlopens extensions RTLD_GLOBAL; -Bsymbolic keeps our V8 and rusty_v8
|
|
12
|
+
// symbols bound to our own copies if another extension exports the same names.
|
|
13
|
+
if std::env::var("CARGO_CFG_TARGET_OS").is_ok_and(|os| os == "linux") {
|
|
14
|
+
println!("cargo::rustc-link-arg-cdylib=-Wl,-Bsymbolic");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "mkmf"
|
|
4
|
+
|
|
5
|
+
if RUBY_ENGINE == "truffleruby"
|
|
6
|
+
File.write("Makefile", dummy_makefile(__dir__).join)
|
|
7
|
+
return
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
TARGETS = {
|
|
11
|
+
%w[x86_64 linux] => "x86_64-unknown-linux-gnu",
|
|
12
|
+
%w[aarch64 linux] => "aarch64-unknown-linux-gnu",
|
|
13
|
+
%w[arm64 darwin] => "aarch64-apple-darwin",
|
|
14
|
+
%w[aarch64 darwin] => "aarch64-apple-darwin",
|
|
15
|
+
%w[x86_64 darwin] => "x86_64-apple-darwin",
|
|
16
|
+
}.freeze
|
|
17
|
+
MINIMUM_RUST = Gem::Version.new("1.91.0")
|
|
18
|
+
|
|
19
|
+
host_os = RbConfig::CONFIG["host_os"]
|
|
20
|
+
os = case host_os
|
|
21
|
+
when /linux-musl/ then nil
|
|
22
|
+
when /linux/ then "linux"
|
|
23
|
+
when /darwin/ then "darwin"
|
|
24
|
+
end
|
|
25
|
+
target = os && TARGETS[[RbConfig::CONFIG["host_cpu"], os]]
|
|
26
|
+
unless target
|
|
27
|
+
abort "mini_racer_rs supports Linux (glibc) on x86_64 and aarch64 and macOS on arm64 and x86_64; " \
|
|
28
|
+
"#{RbConfig::CONFIG["host_cpu"]}-#{host_os} is not supported"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
rustc_version = `#{ENV.fetch("RUSTC", "rustc")} --version 2>/dev/null`[/\d+\.\d+\.\d+/]
|
|
32
|
+
if rustc_version.nil? || Gem::Version.new(rustc_version) < MINIMUM_RUST
|
|
33
|
+
abort "mini_racer_rs needs Rust #{MINIMUM_RUST} or newer to build from source " \
|
|
34
|
+
"(found #{rustc_version || "no rustc"}); install it from https://rustup.rs"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
build_env = {}
|
|
38
|
+
unless ENV.key?("LIBCLANG_PATH")
|
|
39
|
+
begin
|
|
40
|
+
require "libclang"
|
|
41
|
+
build_env["LIBCLANG_PATH"] = Libclang.libdir
|
|
42
|
+
# The libclang gem ships no compiler builtin headers (stdarg.h); borrow GCC's.
|
|
43
|
+
gcc_include = `gcc -print-file-name=include 2>/dev/null`.strip
|
|
44
|
+
if os == "linux" && !ENV.key?("BINDGEN_EXTRA_CLANG_ARGS") && File.directory?(gcc_include)
|
|
45
|
+
build_env["BINDGEN_EXTRA_CLANG_ARGS"] = "-I#{gcc_include}"
|
|
46
|
+
end
|
|
47
|
+
rescue LoadError
|
|
48
|
+
libclang = find_executable("llvm-config") || Dir[
|
|
49
|
+
"/usr/lib*/libclang*.so*",
|
|
50
|
+
"/usr/lib*/llvm*/lib/libclang*.so*",
|
|
51
|
+
"/usr/lib/*-linux-gnu/libclang*.so*",
|
|
52
|
+
"/usr/lib/llvm-*/lib/libclang*.so*",
|
|
53
|
+
"/Library/Developer/CommandLineTools/usr/lib/libclang.dylib",
|
|
54
|
+
"/Applications/Xcode*.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib",
|
|
55
|
+
"/opt/homebrew/opt/llvm/lib/libclang.dylib",
|
|
56
|
+
"/usr/local/opt/llvm/lib/libclang.dylib",
|
|
57
|
+
].first
|
|
58
|
+
unless libclang
|
|
59
|
+
abort "mini_racer_rs needs libclang to build from source: install clang " \
|
|
60
|
+
"(for example `apt install libclang-dev`), run `gem install libclang`, or set LIBCLANG_PATH"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
pinned = File.readlines(File.join(__dir__, "v8-archives.sha256"), chomp: true).to_h do |line|
|
|
66
|
+
sha, file = line.split(/\s+/, 2)
|
|
67
|
+
[file, sha]
|
|
68
|
+
end
|
|
69
|
+
unless %w[RUSTY_V8_ARCHIVE RUSTY_V8_MIRROR RUSTY_V8_ARCHIVE_SHA256].any? { ENV.key?(_1) }
|
|
70
|
+
build_env["RUSTY_V8_ARCHIVE_SHA256"] = pinned.fetch("librusty_v8_release_#{target}.a.gz")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
require "rb_sys/mkmf"
|
|
74
|
+
|
|
75
|
+
create_rust_makefile("mini_racer_rs/mini_racer_rs") do |r|
|
|
76
|
+
r.extra_cargo_args += ["--locked"]
|
|
77
|
+
r.env.merge!(build_env)
|
|
78
|
+
end
|