Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use NaN-boxing on value::InnerValue #4091

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6d3d9db
Start of nan boxing values
hansl Dec 19, 2024
c64417d
Continuing, but this endian thing is not helping
hansl Dec 20, 2024
e357908
Finish the boxing and add tests
hansl Dec 20, 2024
d16a546
Better tests and fixing a couple of bugs
hansl Dec 20, 2024
e2496c3
Rollback changes outside of core/engine/src/value/...
hansl Dec 20, 2024
683444e
Fix Drop::drop and clippies
hansl Dec 20, 2024
9d1ac9f
Move MSRV to 1.83
hansl Dec 20, 2024
167d634
Move MSRV back to 1.82 and impl missing const fn
hansl Dec 21, 2024
2a68855
Fix a few clippies and ignore the one thats wrong
hansl Dec 21, 2024
56d513a
Merge branch 'main' into nan_box
hansl Dec 23, 2024
c7cd8b0
WIP
hansl Dec 23, 2024
52fc103
Duh!
hansl Dec 26, 2024
084b25e
Merge remote-tracking branch 'upstream/main' into nan_box
hansl Dec 26, 2024
345cd69
Oops
hansl Dec 26, 2024
710d8f1
Clippies
hansl Dec 26, 2024
c6d0669
refactor bit-masks and ranges
hansl Dec 29, 2024
7ae9352
Merge remote-tracking branch 'upstream/main' into nan_box
hansl Dec 29, 2024
ad64fd9
NAN is not null_ptr and clippies
hansl Dec 29, 2024
ac171fe
Move all magic numbers and bits to the bits private module
hansl Dec 29, 2024
8789d55
Merge remote-tracking branch 'upstream/main' into nan_box
hansl Dec 30, 2024
ec32abd
Implement both enum-based and nan-boxed JsValue inner behind a flag
hansl Dec 30, 2024
40d5e5c
Clippies
hansl Dec 30, 2024
e647385
Remove debug logging
hansl Jan 1, 2025
69bbf82
Some simple attempts to improve performance with NonNull
hansl Jan 5, 2025
ca9f657
Use top 2 bits for pointer tagging, speeding up as_variant
hansl Jan 5, 2025
e29cedb
Remove the feature flag for NaN boxing now that were closer in perf
hansl Jan 5, 2025
c949461
Add a bits section to explain the scheme
hansl Jan 6, 2025
c21c886
Inline ALL THE THINGS - slight increase in perf
hansl Jan 7, 2025
5d406c7
Merge branch 'main' into nan_box
hansl Jan 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dhat = { workspace = true, optional = true }
[features]
default = ["boa_engine/annex-b", "boa_engine/experimental", "boa_engine/intl_bundled"]
dhat = ["dep:dhat"]
nan-box-jsvalue = ["boa_engine/nan-box-jsvalue"]

[target.x86_64-unknown-linux-gnu.dependencies]
jemallocator.workspace = true
Expand Down
1 change: 1 addition & 0 deletions core/engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ rust-version.workspace = true
profiler = ["boa_profiler/profiler"]
deser = ["boa_interner/serde", "boa_ast/serde"]
either = ["dep:either"]
nan-box-jsvalue = []

# Enables the `Intl` builtin object and bundles a default ICU4X data provider.
# Prefer this over `intl` if you just want to enable `Intl` without dealing with the
Expand Down
9 changes: 9 additions & 0 deletions core/engine/src/builtins/array/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,3 +953,12 @@ fn array_sort() {
"#}),
]);
}

#[test]
fn array_of_neg_zero() {
run_test_actions([
TestAction::run("let arr = [-0, -0, -0, -0];"),
// Assert the parity of all items of the list.
TestAction::assert("arr.every(x => (1/x) === -Infinity)"),
]);
}
24 changes: 12 additions & 12 deletions core/engine/src/value/conversions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Conversions from JavaScript values into Rust values, and the other way around.

use super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler};
use crate::value::inner::InnerValue;
use crate::{js_string, string::JsStr};

use super::{InnerValue, JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler};

mod either;
mod serde_json;
pub(super) mod try_from_js;
Expand All @@ -15,15 +15,15 @@ impl From<JsStr<'_>> for JsValue {
fn from(value: JsStr<'_>) -> Self {
let _timer = Profiler::global().start_event("From<JsStr<'_>>", "value");

Self::from_inner(InnerValue::String(value.into()))
Self::from_inner(InnerValue::string(value.into()))
}
}

impl From<JsString> for JsValue {
fn from(value: JsString) -> Self {
let _timer = Profiler::global().start_event("From<JsString>", "value");

Self::from_inner(InnerValue::String(value))
Self::from_inner(InnerValue::string(value))
}
}

Expand All @@ -45,7 +45,7 @@ impl From<JsSymbol> for JsValue {
fn from(value: JsSymbol) -> Self {
let _timer = Profiler::global().start_event("From<JsSymbol>", "value");

Self::from_inner(InnerValue::Symbol(value))
Self::from_inner(InnerValue::symbol(value))
}
}

Expand All @@ -54,7 +54,7 @@ impl From<f32> for JsValue {
fn from(value: f32) -> Self {
let _timer = Profiler::global().start_event("From<f32>", "value");

JsValue::from(f64::from(value))
Self::rational(f64::from(value))
}
}

Expand All @@ -63,7 +63,7 @@ impl From<f64> for JsValue {
fn from(value: f64) -> Self {
let _timer = Profiler::global().start_event("From<f64>", "value");

Self::from_inner(InnerValue::Float64(value))
Self::rational(value)
}
}

Expand All @@ -78,8 +78,8 @@ macro_rules! impl_from_integer {

i32::try_from(value)
.map_or_else(
|_| Self::from(value as f64),
|value| Self::from_inner(InnerValue::Integer32(value)),
|_| Self::rational(value as f64),
|value| Self::from_inner(InnerValue::integer32(value)),
)
}
}
Expand All @@ -94,7 +94,7 @@ impl From<JsBigInt> for JsValue {
fn from(value: JsBigInt) -> Self {
let _timer = Profiler::global().start_event("From<JsBigInt>", "value");

Self::from_inner(InnerValue::BigInt(value))
Self::from_inner(InnerValue::bigint(value))
}
}

Expand All @@ -103,7 +103,7 @@ impl From<bool> for JsValue {
fn from(value: bool) -> Self {
let _timer = Profiler::global().start_event("From<bool>", "value");

Self::from_inner(InnerValue::Boolean(value))
Self::from_inner(InnerValue::boolean(value))
}
}

Expand All @@ -112,7 +112,7 @@ impl From<JsObject> for JsValue {
fn from(object: JsObject) -> Self {
let _timer = Profiler::global().start_event("From<JsObject>", "value");

Self::from_inner(InnerValue::Object(object))
Self::from_inner(InnerValue::object(object))
}
}

Expand Down
24 changes: 12 additions & 12 deletions core/engine/src/value/conversions/serde_json.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! This module implements the conversions from and into [`serde_json::Value`].

use super::{InnerValue, JsValue};
use super::JsValue;
use crate::{
builtins::Array,
error::JsNativeError,
js_string,
object::JsObject,
property::{PropertyDescriptor, PropertyKey},
Context, JsResult,
Context, JsResult, JsVariant,
};
use serde_json::{Map, Value};

Expand Down Expand Up @@ -113,17 +113,17 @@ impl JsValue {
///
/// Panics if the `JsValue` is `Undefined`.
pub fn to_json(&self, context: &mut Context) -> JsResult<Value> {
match &self.inner {
InnerValue::Null => Ok(Value::Null),
InnerValue::Undefined => todo!("undefined to JSON"),
InnerValue::Boolean(b) => Ok(Value::from(*b)),
InnerValue::String(string) => Ok(string.to_std_string_escaped().into()),
InnerValue::Float64(rat) => Ok(Value::from(*rat)),
InnerValue::Integer32(int) => Ok(Value::from(*int)),
InnerValue::BigInt(_bigint) => Err(JsNativeError::typ()
match self.variant() {
JsVariant::Null => Ok(Value::Null),
JsVariant::Undefined => todo!("undefined to JSON"),
JsVariant::Boolean(b) => Ok(Value::from(b)),
JsVariant::String(string) => Ok(string.to_std_string_escaped().into()),
JsVariant::Float64(rat) => Ok(Value::from(rat)),
JsVariant::Integer32(int) => Ok(Value::from(int)),
JsVariant::BigInt(_bigint) => Err(JsNativeError::typ()
.with_message("cannot convert bigint to JSON")
.into()),
InnerValue::Object(obj) => {
JsVariant::Object(obj) => {
let value_by_prop_key = |property_key, context: &mut Context| {
obj.borrow()
.properties()
Expand Down Expand Up @@ -168,7 +168,7 @@ impl JsValue {
Ok(Value::Object(map))
}
}
InnerValue::Symbol(_sym) => Err(JsNativeError::typ()
JsVariant::Symbol(_sym) => Err(JsNativeError::typ()
.with_message("cannot convert Symbol to JSON")
.into()),
}
Expand Down
69 changes: 38 additions & 31 deletions core/engine/src/value/conversions/try_from_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
use num_bigint::BigInt;
use num_traits::AsPrimitive;

use crate::value::InnerValue;
use crate::{js_string, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue};

mod collections;
Expand Down Expand Up @@ -62,11 +61,12 @@ impl TryFromJs for String {

impl TryFromJs for JsString {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match &value.inner {
InnerValue::String(s) => Ok(s.clone()),
_ => Err(JsNativeError::typ()
.with_message("cannot convert value to a String")
.into()),
if let Some(s) = value.as_string() {
Ok(s.clone())
} else {
Err(JsNativeError::typ()
.with_message("cannot convert value to a JsString")
.into())
}
}
}
Expand All @@ -90,7 +90,7 @@ where
T: TryFromJs,
{
fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {
let InnerValue::Object(object) = &value.inner else {
let Some(object) = &value.as_object() else {
return Err(JsNativeError::typ()
.with_message("cannot convert value to a Vec")
.into());
Expand Down Expand Up @@ -119,33 +119,36 @@ where

impl TryFromJs for JsObject {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match &value.inner {
InnerValue::Object(o) => Ok(o.clone()),
_ => Err(JsNativeError::typ()
if let Some(o) = value.as_object() {
Ok(o.clone())
} else {
Err(JsNativeError::typ()
.with_message("cannot convert value to a Object")
.into()),
.into())
}
}
}

impl TryFromJs for JsBigInt {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match &value.inner {
InnerValue::BigInt(b) => Ok(b.clone()),
_ => Err(JsNativeError::typ()
if let Some(b) = value.as_bigint() {
Ok(b.clone())
} else {
Err(JsNativeError::typ()
.with_message("cannot convert value to a BigInt")
.into()),
.into())
}
}
}

impl TryFromJs for BigInt {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match &value.inner {
InnerValue::BigInt(b) => Ok(b.as_inner().clone()),
_ => Err(JsNativeError::typ()
if let Some(b) = value.as_bigint() {
Ok(b.as_inner().clone())
} else {
Err(JsNativeError::typ()
.with_message("cannot convert value to a BigInt")
.into()),
.into())
}
}
}
Expand All @@ -158,12 +161,14 @@ impl TryFromJs for JsValue {

impl TryFromJs for f64 {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match &value.inner {
InnerValue::Integer32(i) => Ok((*i).into()),
InnerValue::Float64(r) => Ok(*r),
_ => Err(JsNativeError::typ()
if let Some(i) = value.0.as_integer32() {
Ok(f64::from(i))
} else if let Some(f) = value.0.as_float64() {
Ok(f)
} else {
Err(JsNativeError::typ()
.with_message("cannot convert value to a f64")
.into()),
.into())
}
}
}
Expand All @@ -184,23 +189,25 @@ macro_rules! impl_try_from_js_integer {
$(
impl TryFromJs for $type {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match &value.inner {
InnerValue::Integer32(i) => (*i).try_into().map_err(|e| {
if let Some(i) = value.as_i32() {
i.try_into().map_err(|e| {
JsNativeError::typ()
.with_message(format!(
concat!("cannot convert value to a ", stringify!($type), ": {}"),
e)
)
.into()
}),
InnerValue::Float64(f) => from_f64(*f).ok_or_else(|| {
})
} else if let Some(f) = value.as_number() {
from_f64(f).ok_or_else(|| {
JsNativeError::typ()
.with_message(concat!("cannot convert value to a ", stringify!($type)))
.into()
}),
_ => Err(JsNativeError::typ()
})
} else {
Err(JsNativeError::typ()
.with_message(concat!("cannot convert value to a ", stringify!($type)))
.into()),
.into())
}
}
}
Expand Down
Loading
Loading