Skip to content

Commit

Permalink
feat(vm): more abstract operations (#50)
Browse files Browse the repository at this point in the history
Co-authored-by: Carter Snook <[email protected]>
Co-authored-by: vimirage <[email protected]>
Co-authored-by: Aapo Alasuutari <[email protected]>
  • Loading branch information
4 people authored Dec 17, 2023
1 parent 2f0589e commit 7400d31
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 1 deletion.
89 changes: 88 additions & 1 deletion nova_vm/src/ecmascript/abstract_operations/type_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use crate::{
execution::{agent::ExceptionType, agent::JsError, Agent, JsResult},
types::{BigInt, Number, Object, PropertyKey, String, Value},
},
heap::WellKnownSymbolIndexes,
heap::{CreateHeapData, GetHeapData, WellKnownSymbolIndexes},
SmallInteger,
};

use super::{
Expand Down Expand Up @@ -490,3 +491,89 @@ pub(crate) fn to_object(_agent: &mut Agent, argument: Value) -> JsResult<Object>
_ => Ok(Object::try_from(argument).unwrap()),
}
}

/// ### [7.1.19 ToPropertyKey ( argument )](https://tc39.es/ecma262/#sec-topropertykey)
pub(crate) fn to_property_key(agent: &mut Agent, argument: Value) -> JsResult<PropertyKey> {
// 1. Let key be ? ToPrimitive(argument, hint String).
let key = to_primitive(agent, argument, Some(PreferredType::String))?;

// 2. If Type(key) is Symbol, then
// a. Return key.
// NOTE: This handles Symbols and other primitives because we use niche
// specializations for PropertyKey (e.g. integer indexes for arrays).
if let Ok(property_key) = PropertyKey::try_from(key) {
return Ok(property_key);
}

// 3. Return ! ToString(key).
Ok(to_string(agent, key).unwrap().into())
}

/// ### [7.1.20 ToLength ( argument )](https://tc39.es/ecma262/#sec-tolength)
pub(crate) fn to_length(agent: &mut Agent, argument: Value) -> JsResult<i64> {
// TODO: This can be heavily optimized by inlining `to_integer_or_infinity`.

// 1. Let len be ? ToIntegerOrInfinity(argument).
let len = to_integer_or_infinity(agent, argument)?;

// 2. If len ≤ 0, return +0𝔽.
if match len {
Number::Integer(n) => n.into_i64() <= 0,
Number::Float(n) => n <= 0.0,
Number::Number(n) => *agent.heap.get(n) <= 0.0,
} {
return Ok(0);
}

// 3. Return 𝔽(min(len, 2**53 - 1)).
Ok(match len {
Number::Integer(n) => n.into_i64().min(SmallInteger::MAX_NUMBER),
Number::Float(n) => n.min(SmallInteger::MAX_NUMBER as f32) as i64,
Number::Number(n) => agent.heap.get(n).min(SmallInteger::MAX_NUMBER as f64) as i64,
})
}

/// ### [7.1.21 CanonicalNumericIndexString ( argument )](https://tc39.es/ecma262/#sec-canonicalnumericindexstring)
pub(crate) fn canonical_numeric_index_string(
agent: &mut Agent,
argument: String,
) -> Option<Number> {
// 1. If argument is "-0", return -0𝔽.
if argument == String::from_small_string("-0") {
return Some((-0.0).into());
}

// 2. Let n be ! ToNumber(argument).
let n = to_number(agent, argument.into()).unwrap();

// 3. If ! ToString(n) is argument, return n.
if to_string(agent, n.into()).unwrap() == argument {
return Some(n);
}

// 4. Return undefined.
None
}

/// ### [7.1.22 ToIndex ( value )](https://tc39.es/ecma262/#sec-toindex)
pub(crate) fn to_index(agent: &mut Agent, argument: Value) -> JsResult<i64> {
// TODO: This can be heavily optimized by inlining `to_integer_or_infinity`.

// 1. Let integer be ? ToIntegerOrInfinity(value).
let integer = to_integer_or_infinity(agent, argument)?;

// 2. If integer is not in the inclusive interval from 0 to 2**53 - 1, throw a RangeError exception.
let integer = if let Number::Integer(n) = integer {
let integer = n.into_i64();
if !(0..=(SmallInteger::MAX_NUMBER)).contains(&integer) {
return Err(agent.throw_exception(ExceptionType::RangeError, "Result is out of range"));
}
integer
} else {
// to_integer_or_infinity returns +0, +Infinity, -Infinity, or an integer.
return Err(agent.throw_exception(ExceptionType::RangeError, "Result is out of range"));
};

// 3. Return integer.
Ok(integer)
}
17 changes: 17 additions & 0 deletions nova_vm/src/ecmascript/types/language/object/property_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,26 @@ impl TryFrom<Value> for PropertyKey {
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Integer(x) => Ok(PropertyKey::Integer(x)),
Value::Float(x) => {
if x == -0.0f32 {
Ok(PropertyKey::Integer(0.into()))
} else if x.fract() == 0.0
&& (SmallInteger::MIN_NUMBER..=SmallInteger::MAX_NUMBER).contains(&(x as i64))
{
unreachable!("Value::Float should not contain safe integers");
} else {
Err(())
}
}
Value::SmallString(x) => Ok(PropertyKey::SmallString(x)),
Value::String(x) => Ok(PropertyKey::String(x)),
Value::Symbol(x) => Ok(PropertyKey::Symbol(x)),
Value::SmallBigInt(x)
if (SmallInteger::MIN_NUMBER..=SmallInteger::MAX_NUMBER)
.contains(&x.into_i64()) =>
{
Ok(PropertyKey::Integer(x))
}
_ => Err(()),
}
}
Expand Down

0 comments on commit 7400d31

Please sign in to comment.