Skip to content

Commit

Permalink
Merge 58ee11b into 607a534
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat authored Aug 11, 2020
2 parents 607a534 + 58ee11b commit 8c7e9fa
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 106 deletions.
4 changes: 2 additions & 2 deletions boa/src/builtins/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl BigInt {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => ctx.to_bigint(value)?,
Some(ref value) => value.to_bigint(ctx)?,
None => RcBigInt::from(Self::from(0)),
};
Ok(Value::from(data))
Expand Down Expand Up @@ -187,7 +187,7 @@ impl BigInt {
let bits = ctx.to_index(bits_arg)?;
let bits = u32::try_from(bits).unwrap_or(u32::MAX);

let bigint = ctx.to_bigint(bigint_arg)?;
let bigint = bigint_arg.to_bigint(ctx)?;

Ok((
bigint
Expand Down
6 changes: 3 additions & 3 deletions boa/src/builtins/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::{
builtins::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
value::PreferredType,
ResultValue, Value,
},
exec::PreferredType,
BoaProfiler, Interpreter,
};
use chrono::{prelude::*, Duration, LocalResult};
Expand Down Expand Up @@ -306,8 +306,8 @@ impl Date {
let value = &args[0];
let tv = match this_time_value(value, ctx) {
Ok(dt) => dt.0,
_ => match &ctx.to_primitive(value, PreferredType::Default)? {
Value::String(str) => match chrono::DateTime::parse_from_rfc3339(&str) {
_ => match value.to_primitive(ctx, PreferredType::Default)? {
Value::String(ref str) => match chrono::DateTime::parse_from_rfc3339(&str) {
Ok(dt) => Some(dt.naive_utc()),
_ => None,
},
Expand Down
22 changes: 10 additions & 12 deletions boa/src/builtins/date/tests.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![allow(clippy::zero_prefixed_literal)]

use crate::{
builtins::{object::ObjectData, Value},
forward, forward_val, Interpreter, Realm,
};
use chrono::prelude::*;

// NB: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of
// NOTE: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of
// this.

fn forward_dt_utc(engine: &mut Interpreter, src: &str) -> Option<NaiveDateTime> {
Expand All @@ -14,19 +16,15 @@ fn forward_dt_utc(engine: &mut Interpreter, src: &str) -> Option<NaiveDateTime>
panic!("expected success")
};

let date_time = if let Value::Object(date_time) = &date_time {
date_time
if let Value::Object(ref date_time) = date_time {
if let ObjectData::Date(ref date_time) = date_time.borrow().data {
date_time.0
} else {
panic!("expected date")
}
} else {
panic!("expected object")
};

let date_time = if let ObjectData::Date(date_time) = &date_time.borrow().data {
date_time.0
} else {
panic!("expected date")
};

date_time.clone()
}
}

fn forward_dt_local(engine: &mut Interpreter, src: &str) -> Option<NaiveDateTime> {
Expand Down
6 changes: 3 additions & 3 deletions boa/src/builtins/value/equality.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::{builtins::Number, exec::PreferredType, Interpreter};
use crate::{builtins::Number, Interpreter};

use std::borrow::Borrow;

Expand Down Expand Up @@ -94,14 +94,14 @@ impl Value {
// 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result
// of the comparison x == ? ToPrimitive(y).
(Self::Object(_), _) => {
let primitive = interpreter.to_primitive(self, PreferredType::Default)?;
let primitive = self.to_primitive(interpreter, PreferredType::Default)?;
return primitive.equals(other, interpreter);
}

// 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result
// of the comparison ? ToPrimitive(x) == y.
(_, Self::Object(_)) => {
let primitive = interpreter.to_primitive(other, PreferredType::Default)?;
let primitive = other.to_primitive(interpreter, PreferredType::Default)?;
return primitive.equals(self, interpreter);
}

Expand Down
64 changes: 64 additions & 0 deletions boa/src/builtins/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,10 +704,74 @@ impl Value {
new_func_val.set_field("length", Value::from(length));
new_func_val
}

/// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
///
/// <https://tc39.es/ecma262/#sec-toprimitive>
pub fn to_primitive(
&self,
ctx: &mut Interpreter,
preferred_type: PreferredType,
) -> ResultValue {
// 1. Assert: input is an ECMAScript language value. (always a value not need to check)
// 2. If Type(input) is Object, then
if let Value::Object(_) = self {
let mut hint = preferred_type;

// Skip d, e we don't support Symbols yet
// TODO: add when symbols are supported
// TODO: Add other steps.
if hint == PreferredType::Default {
hint = PreferredType::Number;
};

// g. Return ? OrdinaryToPrimitive(input, hint).
ctx.ordinary_to_primitive(self, hint)
} else {
// 3. Return input.
Ok(self.clone())
}
}

/// Helper function.
pub fn to_bigint(&self, ctx: &mut Interpreter) -> Result<RcBigInt, Value> {
match self {
Value::Null => Err(ctx.construct_type_error("cannot convert null to a BigInt")),
Value::Undefined => {
Err(ctx.construct_type_error("cannot convert undefined to a BigInt"))
}
Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, ctx)?)),
Value::Boolean(true) => Ok(RcBigInt::from(BigInt::from(1))),
Value::Boolean(false) => Ok(RcBigInt::from(BigInt::from(0))),
Value::Integer(num) => Ok(RcBigInt::from(BigInt::from(*num))),
Value::Rational(num) => {
if let Ok(bigint) = BigInt::try_from(*num) {
return Ok(RcBigInt::from(bigint));
}
Err(ctx.construct_type_error(format!(
"The number {} cannot be converted to a BigInt because it is not an integer",
num
)))
}
Value::BigInt(b) => Ok(b.clone()),
Value::Object(_) => {
let primitive = self.to_primitive(ctx, PreferredType::Number)?;
primitive.to_bigint(ctx)
}
Value::Symbol(_) => Err(ctx.construct_type_error("cannot convert Symbol to a BigInt")),
}
}
}

impl Default for Value {
fn default() -> Self {
Self::Undefined
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PreferredType {
String,
Number,
Default,
}
18 changes: 10 additions & 8 deletions boa/src/builtins/value/operations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use super::*;
use crate::builtins::number::{f64_to_int32, f64_to_uint32, Number};
use crate::exec::PreferredType;
use crate::builtins::{
number::{f64_to_int32, f64_to_uint32, Number},
value::PreferredType,
};

impl Value {
#[inline]
Expand All @@ -20,8 +22,8 @@ impl Value {

// Slow path:
(_, _) => match (
ctx.to_primitive(self, PreferredType::Default)?,
ctx.to_primitive(other, PreferredType::Default)?,
self.to_primitive(ctx, PreferredType::Default)?,
other.to_primitive(ctx, PreferredType::Default)?,
) {
(Self::String(ref x), ref y) => Self::string(format!("{}{}", x, ctx.to_string(y)?)),
(ref x, Self::String(ref y)) => Self::string(format!("{}{}", ctx.to_string(x)?, y)),
Expand Down Expand Up @@ -440,13 +442,13 @@ impl Value {
// Slow path:
(_, _) => {
let (px, py) = if left_first {
let px = ctx.to_primitive(self, PreferredType::Number)?;
let py = ctx.to_primitive(other, PreferredType::Number)?;
let px = self.to_primitive(ctx, PreferredType::Number)?;
let py = other.to_primitive(ctx, PreferredType::Number)?;
(px, py)
} else {
// NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.
let py = ctx.to_primitive(other, PreferredType::Number)?;
let px = ctx.to_primitive(self, PreferredType::Number)?;
let py = other.to_primitive(ctx, PreferredType::Number)?;
let px = self.to_primitive(ctx, PreferredType::Number)?;
(px, py)
};

Expand Down
1 change: 1 addition & 0 deletions boa/src/builtins/value/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ fn bitand_rational_and_rational() {
}

#[test]
#[allow(clippy::float_cmp)]
fn pow_number_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
Expand Down
80 changes: 7 additions & 73 deletions boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ use crate::{
number::{f64_to_int32, f64_to_uint32},
object::{Object, ObjectData, PROTOTYPE},
property::PropertyKey,
value::{RcBigInt, RcString, ResultValue, Type, Value},
BigInt, Console, Number,
value::{PreferredType, RcString, ResultValue, Type, Value},
Console, Number,
},
realm::Realm,
syntax::ast::{
Expand All @@ -40,7 +40,6 @@ use crate::{
BoaProfiler,
};
use std::borrow::Borrow;
use std::convert::TryFrom;
use std::ops::Deref;

pub trait Executable {
Expand All @@ -54,12 +53,6 @@ pub(crate) enum InterpreterState {
Return,
Break(Option<String>),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PreferredType {
String,
Number,
Default,
}

/// A Javascript intepreter
#[derive(Debug)]
Expand Down Expand Up @@ -210,42 +203,12 @@ impl Interpreter {
Value::Symbol(_) => Err(self.construct_type_error("can't convert symbol to string")),
Value::BigInt(ref bigint) => Ok(RcString::from(bigint.to_string())),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::String)?;
let primitive = value.to_primitive(self, PreferredType::String)?;
self.to_string(&primitive)
}
}
}

/// Helper function.
#[allow(clippy::wrong_self_convention)]
pub fn to_bigint(&mut self, value: &Value) -> Result<RcBigInt, Value> {
match value {
Value::Null => Err(self.construct_type_error("cannot convert null to a BigInt")),
Value::Undefined => {
Err(self.construct_type_error("cannot convert undefined to a BigInt"))
}
Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, self)?)),
Value::Boolean(true) => Ok(RcBigInt::from(BigInt::from(1))),
Value::Boolean(false) => Ok(RcBigInt::from(BigInt::from(0))),
Value::Integer(num) => Ok(RcBigInt::from(BigInt::from(*num))),
Value::Rational(num) => {
if let Ok(bigint) = BigInt::try_from(*num) {
return Ok(RcBigInt::from(bigint));
}
Err(self.construct_type_error(format!(
"The number {} cannot be converted to a BigInt because it is not an integer",
num
)))
}
Value::BigInt(b) => Ok(b.clone()),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::Number)?;
self.to_bigint(&primitive)
}
Value::Symbol(_) => Err(self.construct_type_error("cannot convert Symbol to a BigInt")),
}
}

/// Converts a value to a non-negative integer if it is a valid integer index value.
///
/// See: https://tc39.es/ecma262/#sec-toindex
Expand Down Expand Up @@ -352,7 +315,7 @@ impl Interpreter {
Value::Symbol(_) => Err(self.construct_type_error("argument must not be a symbol")),
Value::BigInt(_) => Err(self.construct_type_error("argument must not be a bigint")),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::Number)?;
let primitive = value.to_primitive(self, PreferredType::Number)?;
self.to_number(&primitive)
}
}
Expand All @@ -363,7 +326,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)]
pub fn to_numeric(&mut self, value: &Value) -> ResultValue {
let primitive = self.to_primitive(value, PreferredType::Number)?;
let primitive = value.to_primitive(self, PreferredType::Number)?;
if primitive.is_bigint() {
return Ok(primitive);
}
Expand All @@ -377,7 +340,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_numeric_number(&mut self, value: &Value) -> Result<f64, Value> {
let primitive = self.to_primitive(value, PreferredType::Number)?;
let primitive = value.to_primitive(self, PreferredType::Number)?;
if let Some(ref bigint) = primitive.as_bigint() {
return Ok(bigint.to_f64());
}
Expand Down Expand Up @@ -476,41 +439,12 @@ impl Interpreter {
self.throw_type_error("cannot convert object to primitive value")
}

/// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
///
/// <https://tc39.es/ecma262/#sec-toprimitive>
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_primitive(
&mut self,
input: &Value,
preferred_type: PreferredType,
) -> ResultValue {
// 1. Assert: input is an ECMAScript language value. (always a value not need to check)
// 2. If Type(input) is Object, then
if let Value::Object(_) = input {
let mut hint = preferred_type;

// Skip d, e we don't support Symbols yet
// TODO: add when symbols are supported
// TODO: Add other steps.
if hint == PreferredType::Default {
hint = PreferredType::Number;
};

// g. Return ? OrdinaryToPrimitive(input, hint).
self.ordinary_to_primitive(input, hint)
} else {
// 3. Return input.
Ok(input.clone())
}
}

/// The abstract operation ToPropertyKey takes argument argument. It converts argument to a value that can be used as a property key.
///
/// https://tc39.es/ecma262/#sec-topropertykey
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_property_key(&mut self, value: &Value) -> Result<PropertyKey, Value> {
let key = self.to_primitive(value, PreferredType::String)?;
let key = value.to_primitive(self, PreferredType::String)?;
if let Value::Symbol(ref symbol) = key {
Ok(PropertyKey::from(symbol.clone()))
} else {
Expand Down
10 changes: 5 additions & 5 deletions boa/src/exec/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,11 +879,11 @@ fn to_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);

assert!(engine.to_bigint(&Value::null()).is_err());
assert!(engine.to_bigint(&Value::undefined()).is_err());
assert!(engine.to_bigint(&Value::integer(55)).is_ok());
assert!(engine.to_bigint(&Value::rational(10.0)).is_ok());
assert!(engine.to_bigint(&Value::string("100")).is_ok());
assert!(Value::null().to_bigint(&mut engine).is_err());
assert!(Value::undefined().to_bigint(&mut engine).is_err());
assert!(Value::integer(55).to_bigint(&mut engine).is_ok());
assert!(Value::rational(10.0).to_bigint(&mut engine).is_ok());
assert!(Value::string("100").to_bigint(&mut engine).is_ok());
}

#[test]
Expand Down

0 comments on commit 8c7e9fa

Please sign in to comment.