Browse Source

Code cleanup and inline (#916)

* use gc module

* Added #[inline]

* Change context valiable names to context

* Fix test262

* Update test262 submodule

* Fix: Switch interpreter for context

Co-authored-by: João Borges <rageknify@gmail.com>
pull/921/head
Halid Odat 4 years ago committed by GitHub
parent
commit
6eac058406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 98
      boa/benches/exec.rs
  2. 14
      boa/examples/classes.rs
  3. 38
      boa/src/builtins/array/array_iterator.rs
  4. 102
      boa/src/builtins/array/mod.rs
  5. 657
      boa/src/builtins/array/tests.rs
  6. 35
      boa/src/builtins/bigint/mod.rs
  7. 266
      boa/src/builtins/bigint/tests.rs
  8. 12
      boa/src/builtins/boolean/mod.rs
  9. 28
      boa/src/builtins/boolean/tests.rs
  10. 136
      boa/src/builtins/console/mod.rs
  11. 28
      boa/src/builtins/console/tests.rs
  12. 94
      boa/src/builtins/date/mod.rs
  13. 372
      boa/src/builtins/date/tests.rs
  14. 8
      boa/src/builtins/error/eval.rs
  15. 8
      boa/src/builtins/error/mod.rs
  16. 8
      boa/src/builtins/error/range.rs
  17. 8
      boa/src/builtins/error/reference.rs
  18. 10
      boa/src/builtins/error/syntax.rs
  19. 52
      boa/src/builtins/error/tests.rs
  20. 8
      boa/src/builtins/error/type.rs
  21. 8
      boa/src/builtins/error/uri.rs
  22. 12
      boa/src/builtins/function/mod.rs
  23. 78
      boa/src/builtins/function/tests.rs
  24. 2
      boa/src/builtins/infinity/mod.rs
  25. 39
      boa/src/builtins/iterable/mod.rs
  26. 32
      boa/src/builtins/json/mod.rs
  27. 142
      boa/src/builtins/json/tests.rs
  28. 38
      boa/src/builtins/map/map_iterator.rs
  29. 62
      boa/src/builtins/map/mod.rs
  30. 10
      boa/src/builtins/map/ordered_map.rs
  31. 202
      boa/src/builtins/map/tests.rs
  32. 142
      boa/src/builtins/math/mod.rs
  33. 546
      boa/src/builtins/math/tests.rs
  34. 2
      boa/src/builtins/nan/mod.rs
  35. 90
      boa/src/builtins/number/mod.rs
  36. 575
      boa/src/builtins/number/tests.rs
  37. 54
      boa/src/builtins/object/mod.rs
  38. 168
      boa/src/builtins/object/tests.rs
  39. 16
      boa/src/builtins/regexp/mod.rs
  40. 80
      boa/src/builtins/regexp/tests.rs
  41. 232
      boa/src/builtins/string/mod.rs
  42. 37
      boa/src/builtins/string/string_iterator.rs
  43. 662
      boa/src/builtins/string/tests.rs
  44. 14
      boa/src/builtins/symbol/mod.rs
  45. 22
      boa/src/builtins/symbol/tests.rs
  46. 2
      boa/src/builtins/undefined/mod.rs
  47. 12
      boa/src/class.rs
  48. 24
      boa/src/context.rs
  49. 2
      boa/src/environment/declarative_environment_record.rs
  50. 2
      boa/src/environment/environment_record_trait.rs
  51. 4
      boa/src/environment/function_environment_record.rs
  52. 2
      boa/src/environment/global_environment_record.rs
  53. 2
      boa/src/environment/object_environment_record.rs
  54. 2
      boa/src/exec/mod.rs
  55. 202
      boa/src/exec/tests.rs
  56. 12
      boa/src/lib.rs
  57. 46
      boa/src/object/gcobject.rs
  58. 6
      boa/src/object/internal_methods.rs
  59. 4
      boa/src/property/attribute/mod.rs
  60. 4
      boa/src/realm.rs
  61. 6
      boa/src/syntax/ast/constant.rs
  62. 14
      boa/src/syntax/ast/node/array/mod.rs
  63. 18
      boa/src/syntax/ast/node/block/mod.rs
  64. 12
      boa/src/syntax/ast/node/break_node/mod.rs
  65. 6
      boa/src/syntax/ast/node/break_node/tests.rs
  66. 32
      boa/src/syntax/ast/node/call/mod.rs
  67. 16
      boa/src/syntax/ast/node/conditional/conditional_op/mod.rs
  68. 16
      boa/src/syntax/ast/node/conditional/if_node/mod.rs
  69. 6
      boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs
  70. 19
      boa/src/syntax/ast/node/declaration/const_decl_list/mod.rs
  71. 10
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  72. 6
      boa/src/syntax/ast/node/declaration/function_expr/mod.rs
  73. 10
      boa/src/syntax/ast/node/declaration/let_decl_list/mod.rs
  74. 8
      boa/src/syntax/ast/node/declaration/var_decl_list/mod.rs
  75. 8
      boa/src/syntax/ast/node/field/get_const_field/mod.rs
  76. 12
      boa/src/syntax/ast/node/field/get_field/mod.rs
  77. 14
      boa/src/syntax/ast/node/identifier/mod.rs
  78. 6
      boa/src/syntax/ast/node/iteration/continue_node/mod.rs
  79. 22
      boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs
  80. 22
      boa/src/syntax/ast/node/iteration/for_loop/mod.rs
  81. 46
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  82. 54
      boa/src/syntax/ast/node/iteration/tests.rs
  83. 14
      boa/src/syntax/ast/node/iteration/while_loop/mod.rs
  84. 79
      boa/src/syntax/ast/node/mod.rs
  85. 12
      boa/src/syntax/ast/node/new/mod.rs
  86. 10
      boa/src/syntax/ast/node/object/mod.rs
  87. 20
      boa/src/syntax/ast/node/operator/assign/mod.rs
  88. 126
      boa/src/syntax/ast/node/operator/bin_op/mod.rs
  89. 38
      boa/src/syntax/ast/node/operator/unary_op/mod.rs
  90. 8
      boa/src/syntax/ast/node/return_smt/mod.rs
  91. 12
      boa/src/syntax/ast/node/spread/mod.rs
  92. 16
      boa/src/syntax/ast/node/statement_list/mod.rs
  93. 22
      boa/src/syntax/ast/node/switch/mod.rs
  94. 12
      boa/src/syntax/ast/node/throw/mod.rs
  95. 14
      boa/src/syntax/ast/node/try_node/mod.rs
  96. 16
      boa/src/syntax/ast/op.rs
  97. 18
      boa/src/value/equality.rs
  98. 118
      boa/src/value/mod.rs
  99. 140
      boa/src/value/operations.rs
  100. 17
      boa/src/value/rcbigint.rs
  101. Some files were not shown because too many files have changed in this diff Show More

98
boa/benches/exec.rs

@ -17,8 +17,7 @@ fn create_realm(c: &mut Criterion) {
static SYMBOL_CREATION: &str = include_str!("bench_scripts/symbol_creation.js");
fn symbol_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(SYMBOL_CREATION.as_bytes(), false)
@ -27,30 +26,28 @@ fn symbol_creation(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("Symbols (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static FOR_LOOP: &str = include_str!("bench_scripts/for_loop.js");
fn for_loop_execution(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(FOR_LOOP.as_bytes(), false).parse_all().unwrap();
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("For loop (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static FIBONACCI: &str = include_str!("bench_scripts/fibonacci.js");
fn fibonacci(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(FIBONACCI.as_bytes(), false)
@ -59,15 +56,14 @@ fn fibonacci(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("Fibonacci (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static OBJECT_CREATION: &str = include_str!("bench_scripts/object_creation.js");
fn object_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(OBJECT_CREATION.as_bytes(), false)
@ -76,15 +72,14 @@ fn object_creation(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("Object Creation (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static OBJECT_PROP_ACCESS_CONST: &str = include_str!("bench_scripts/object_prop_access_const.js");
fn object_prop_access_const(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(OBJECT_PROP_ACCESS_CONST.as_bytes(), false)
@ -93,15 +88,14 @@ fn object_prop_access_const(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("Static Object Property Access (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static OBJECT_PROP_ACCESS_DYN: &str = include_str!("bench_scripts/object_prop_access_dyn.js");
fn object_prop_access_dyn(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(OBJECT_PROP_ACCESS_DYN.as_bytes(), false)
@ -110,15 +104,14 @@ fn object_prop_access_dyn(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("Dynamic Object Property Access (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static REGEXP_LITERAL_CREATION: &str = include_str!("bench_scripts/regexp_literal_creation.js");
fn regexp_literal_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP_LITERAL_CREATION.as_bytes(), false)
@ -127,15 +120,14 @@ fn regexp_literal_creation(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("RegExp Literal Creation (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static REGEXP_CREATION: &str = include_str!("bench_scripts/regexp_creation.js");
fn regexp_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP_CREATION.as_bytes(), false)
@ -144,15 +136,14 @@ fn regexp_creation(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("RegExp (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static REGEXP_LITERAL: &str = include_str!("bench_scripts/regexp_literal.js");
fn regexp_literal(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP_LITERAL.as_bytes(), false)
@ -161,182 +152,181 @@ fn regexp_literal(c: &mut Criterion) {
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("RegExp Literal (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static REGEXP: &str = include_str!("bench_scripts/regexp.js");
fn regexp(c: &mut Criterion) {
// Create new Realm and interpreter.
let mut engine = Context::new();
let mut context = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP.as_bytes(), false).parse_all().unwrap();
// Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler
c.bench_function("RegExp (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static ARRAY_ACCESS: &str = include_str!("bench_scripts/array_access.js");
fn array_access(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(ARRAY_ACCESS.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("Array access (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static ARRAY_CREATE: &str = include_str!("bench_scripts/array_create.js");
fn array_creation(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(ARRAY_CREATE.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("Array creation (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static ARRAY_POP: &str = include_str!("bench_scripts/array_pop.js");
fn array_pop(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(ARRAY_POP.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("Array pop (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static STRING_CONCAT: &str = include_str!("bench_scripts/string_concat.js");
fn string_concat(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(STRING_CONCAT.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("String concatenation (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static STRING_COMPARE: &str = include_str!("bench_scripts/string_compare.js");
fn string_compare(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(STRING_COMPARE.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("String comparison (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static STRING_COPY: &str = include_str!("bench_scripts/string_copy.js");
fn string_copy(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(STRING_COPY.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("String copy (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static NUMBER_OBJECT_ACCESS: &str = include_str!("bench_scripts/number_object_access.js");
fn number_object_access(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(NUMBER_OBJECT_ACCESS.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("Number Object Access (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static BOOLEAN_OBJECT_ACCESS: &str = include_str!("bench_scripts/boolean_object_access.js");
fn boolean_object_access(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(BOOLEAN_OBJECT_ACCESS.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("Boolean Object Access (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static STRING_OBJECT_ACCESS: &str = include_str!("bench_scripts/string_object_access.js");
fn string_object_access(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(STRING_OBJECT_ACCESS.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("String Object Access (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static ARITHMETIC_OPERATIONS: &str = include_str!("bench_scripts/arithmetic_operations.js");
fn arithmetic_operations(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(ARITHMETIC_OPERATIONS.as_bytes(), false)
.parse_all()
.unwrap();
c.bench_function("Arithmetic operations (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js");
fn clean_js(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(CLEAN_JS.as_bytes(), false).parse_all().unwrap();
c.bench_function("Clean js (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}
static MINI_JS: &str = include_str!("bench_scripts/mini_js.js");
fn mini_js(c: &mut Criterion) {
let mut engine = Context::new();
let mut context = Context::new();
let nodes = Parser::new(MINI_JS.as_bytes(), false).parse_all().unwrap();
c.bench_function("Mini js (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
b.iter(|| black_box(&nodes).run(&mut context).unwrap())
});
}

14
boa/examples/classes.rs

@ -26,7 +26,7 @@ struct Person {
// or any function that matches that signature.
impl Person {
/// This function says hello
fn say_hello(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
fn say_hello(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
// We check if this is an object.
if let Some(object) = this.as_object() {
// If it is we downcast the type to type `Person`.
@ -42,7 +42,7 @@ impl Person {
}
// If `this` was not an object or the type was not an native object `Person`,
// we throw a `TypeError`.
ctx.throw_type_error("'this' is not a Person object")
context.throw_type_error("'this' is not a Person object")
}
}
@ -57,15 +57,19 @@ impl Class for Person {
const LENGTH: usize = 2;
// This is what is called when we do `new Person()`
fn constructor(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Self> {
fn constructor(_this: &Value, args: &[Value], context: &mut Context) -> Result<Self> {
// we get the first arguemnt of undefined if the first one is unavalable and call `to_string`.
//
// This is equivalent to `String(arg)`.
let name = args.get(0).cloned().unwrap_or_default().to_string(ctx)?;
let name = args
.get(0)
.cloned()
.unwrap_or_default()
.to_string(context)?;
// we get the second arguemnt of undefined if the first one is unavalable and call `to_u32`.
//
// This is equivalent to `arg | 0`.
let age = args.get(1).cloned().unwrap_or_default().to_u32(ctx)?;
let age = args.get(1).cloned().unwrap_or_default().to_u32(context)?;
// we construct the the native struct `Person`
let person = Person {

38
boa/src/builtins/array/array_iterator.rs

@ -1,10 +1,10 @@
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, Value},
gc::{Finalize, Trace},
object::ObjectData,
property::{Attribute, DataDescriptor},
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
#[derive(Debug, Clone, Finalize, Trace)]
pub enum ArrayIterationKind {
@ -46,16 +46,16 @@ impl ArrayIterator {
///
/// [spec]: https://tc39.es/ecma262/#sec-createarrayiterator
pub(crate) fn create_array_iterator(
ctx: &Context,
context: &Context,
array: Value,
kind: ArrayIterationKind,
) -> Result<Value> {
let array_iterator = Value::new_object(Some(ctx.global_object()));
let array_iterator = Value::new_object(Some(context.global_object()));
array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind)));
array_iterator
.as_object()
.expect("array iterator object")
.set_prototype_instance(ctx.iterator_prototypes().array_iterator().into());
.set_prototype_instance(context.iterator_prototypes().array_iterator().into());
Ok(array_iterator)
}
@ -67,48 +67,48 @@ impl ArrayIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
pub(crate) fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn next(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
if let Value::Object(ref object) = this {
let mut object = object.borrow_mut();
if let Some(array_iterator) = object.as_array_iterator_mut() {
let index = array_iterator.next_index;
if array_iterator.array.is_undefined() {
return Ok(create_iter_result_object(ctx, Value::undefined(), true));
return Ok(create_iter_result_object(context, Value::undefined(), true));
}
let len = array_iterator
.array
.get_field("length")
.as_number()
.ok_or_else(|| ctx.construct_type_error("Not an array"))?
.ok_or_else(|| context.construct_type_error("Not an array"))?
as u32;
if array_iterator.next_index >= len {
array_iterator.array = Value::undefined();
return Ok(create_iter_result_object(ctx, Value::undefined(), true));
return Ok(create_iter_result_object(context, Value::undefined(), true));
}
array_iterator.next_index = index + 1;
match array_iterator.kind {
ArrayIterationKind::Key => {
Ok(create_iter_result_object(ctx, index.into(), false))
Ok(create_iter_result_object(context, index.into(), false))
}
ArrayIterationKind::Value => {
let element_value = array_iterator.array.get_field(index);
Ok(create_iter_result_object(ctx, element_value, false))
Ok(create_iter_result_object(context, element_value, false))
}
ArrayIterationKind::KeyAndValue => {
let element_value = array_iterator.array.get_field(index);
let result = Array::constructor(
&Value::new_object(Some(ctx.global_object())),
&Value::new_object(Some(context.global_object())),
&[index.into(), element_value],
ctx,
context,
)?;
Ok(create_iter_result_object(ctx, result, false))
Ok(create_iter_result_object(context, result, false))
}
}
} else {
ctx.throw_type_error("`this` is not an ArrayIterator")
context.throw_type_error("`this` is not an ArrayIterator")
}
} else {
ctx.throw_type_error("`this` is not an ArrayIterator")
context.throw_type_error("`this` is not an ArrayIterator")
}
}
@ -118,19 +118,19 @@ impl ArrayIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value {
let global = ctx.global_object();
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: Value) -> Value {
let global = context.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let array_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx);
make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
array_iterator
.as_object()
.expect("array iterator prototype object")
.set_prototype_instance(iterator_prototype);
let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol();
let to_string_tag = context.well_known_symbols().to_string_tag_symbol();
let to_string_tag_property = DataDescriptor::new("Array Iterator", Attribute::CONFIGURABLE);
array_iterator.set_property(to_string_tag, to_string_tag_property);
array_iterator

102
boa/src/builtins/array/mod.rs

@ -275,11 +275,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.isarray
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
pub(crate) fn is_array(
_this: &Value,
args: &[Value],
_interpreter: &mut Context,
) -> Result<Value> {
pub(crate) fn is_array(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
match args.get(0).and_then(|x| x.as_object()) {
Some(object) => Ok(Value::from(object.borrow().is_array())),
None => Ok(Value::from(false)),
@ -373,7 +369,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
pub(crate) fn for_each(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn for_each(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from("Missing argument for Array.prototype.forEach"));
}
@ -387,7 +383,7 @@ impl Array {
let element = this.get_field(i);
let arguments = [element, Value::from(i), this.clone()];
ctx.call(callback_arg, &this_arg, &arguments)?;
context.call(callback_arg, &this_arg, &arguments)?;
}
Ok(Value::undefined())
@ -405,20 +401,20 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
pub(crate) fn join(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn join(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let separator = if args.is_empty() {
String::from(",")
} else {
args.get(0)
.expect("Could not get argument")
.to_string(ctx)?
.to_string(context)?
.to_string()
};
let mut elem_strs = Vec::new();
let length = this.get_field("length").as_number().unwrap() as i32;
for n in 0..length {
let elem_str = this.get_field(n).to_string(ctx)?.to_string();
let elem_str = this.get_field(n).to_string(context)?.to_string();
elem_strs.push(elem_str);
}
@ -438,14 +434,14 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let method_name = "join";
let mut arguments = vec![Value::from(",")];
// 2.
let mut method = this.get_field(method_name);
// 3.
if !method.is_function() {
method = ctx
method = context
.global_object()
.get_field("Object")
.get_field(PROTOTYPE)
@ -454,7 +450,7 @@ impl Array {
arguments = Vec::new();
}
// 4.
let join = ctx.call(&method, this, &arguments)?;
let join = context.call(&method, this, &arguments)?;
let string = if let Value::String(ref s) = join {
Value::from(s.as_str())
@ -602,7 +598,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
pub(crate) fn every(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
pub(crate) fn every(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing callback when calling function Array.prototype.every",
@ -620,7 +616,7 @@ impl Array {
while i < len {
let element = this.get_field(i);
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter.call(callback, &this_arg, &arguments)?;
let result = context.call(callback, &this_arg, &arguments)?;
if !result.to_boolean() {
return Ok(Value::from(false));
}
@ -798,7 +794,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
pub(crate) fn find(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
pub(crate) fn find(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing callback when calling function Array.prototype.find",
@ -810,7 +806,7 @@ impl Array {
for i in 0..len {
let element = this.get_field(i);
let arguments = [element.clone(), Value::from(i), this.clone()];
let result = interpreter.call(callback, &this_arg, &arguments)?;
let result = context.call(callback, &this_arg, &arguments)?;
if result.to_boolean() {
return Ok(element);
}
@ -830,11 +826,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.findindex
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
pub(crate) fn find_index(
this: &Value,
args: &[Value],
interpreter: &mut Context,
) -> Result<Value> {
pub(crate) fn find_index(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"Missing argument for Array.prototype.findIndex",
@ -851,7 +843,7 @@ impl Array {
let element = this.get_field(i);
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter.call(predicate_arg, &this_arg, &arguments)?;
let result = context.call(predicate_arg, &this_arg, &arguments)?;
if result.to_boolean() {
return Ok(Value::rational(f64::from(i)));
@ -872,17 +864,17 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
pub(crate) fn fill(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn fill(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let len: i32 = this.get_field("length").as_number().unwrap() as i32;
let default_value = Value::undefined();
let value = args.get(0).unwrap_or(&default_value);
let relative_start = args.get(1).unwrap_or(&default_value).to_number(ctx)? as i32;
let relative_start = args.get(1).unwrap_or(&default_value).to_number(context)? as i32;
let relative_end_val = args.get(2).unwrap_or(&default_value);
let relative_end = if relative_end_val.is_undefined() {
len
} else {
relative_end_val.to_number(ctx)? as i32
relative_end_val.to_number(context)? as i32
};
let start = if relative_start < 0 {
max(len + relative_start, 0)
@ -942,8 +934,8 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
pub(crate) fn slice(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
let new_array = Self::new_array(interpreter)?;
pub(crate) fn slice(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let new_array = Self::new_array(context)?;
let len = this.get_field("length").as_number().unwrap() as i32;
let start = match args.get(0) {
@ -987,7 +979,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
pub(crate) fn filter(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
pub(crate) fn filter(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing argument 0 when calling function Array.prototype.filter",
@ -999,7 +991,7 @@ impl Array {
let length = this.get_field("length").as_number().unwrap() as i32;
let new = Self::new_array(interpreter)?;
let new = Self::new_array(context)?;
let values = (0..length)
.filter_map(|idx| {
@ -1007,7 +999,7 @@ impl Array {
let args = [element.clone(), Value::from(idx), new.clone()];
let callback_result = interpreter
let callback_result = context
.call(&callback, &this_val, &args)
.unwrap_or_else(|_| Value::undefined());
@ -1037,7 +1029,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
pub(crate) fn some(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
pub(crate) fn some(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing callback when calling function Array.prototype.some",
@ -1055,7 +1047,7 @@ impl Array {
while i < len {
let element = this.get_field(i);
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter.call(callback, &this_arg, &arguments)?;
let result = context.call(callback, &this_arg, &arguments)?;
if result.to_boolean() {
return Ok(Value::from(true));
}
@ -1080,16 +1072,16 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reduce
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
pub(crate) fn reduce(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
let this: Value = this.to_object(interpreter)?.into();
pub(crate) fn reduce(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let this: Value = this.to_object(context)?.into();
let callback = match args.get(0) {
Some(value) if value.is_function() => value,
_ => return interpreter.throw_type_error("Reduce was called without a callback"),
_ => return context.throw_type_error("Reduce was called without a callback"),
};
let initial_value = args.get(1).cloned().unwrap_or_else(Value::undefined);
let mut length = this.get_field("length").to_length(interpreter)?;
let mut length = this.get_field("length").to_length(context)?;
if length == 0 && initial_value.is_undefined() {
return interpreter
return context
.throw_type_error("Reduce was called on an empty array and with no initial value");
}
let mut k = 0;
@ -1103,7 +1095,7 @@ impl Array {
k += 1;
}
if !k_present {
return interpreter.throw_type_error(
return context.throw_type_error(
"Reduce was called on an empty array and with no initial value",
);
}
@ -1116,11 +1108,11 @@ impl Array {
while k < length {
if this.has_field(k) {
let arguments = [accumulator, this.get_field(k), Value::from(k), this.clone()];
accumulator = interpreter.call(&callback, &Value::undefined(), &arguments)?;
accumulator = context.call(&callback, &Value::undefined(), &arguments)?;
/* We keep track of possibly shortened length in order to prevent unnecessary iteration.
It may also be necessary to do this since shortening the array length does not
delete array elements. See: https://github.com/boa-dev/boa/issues/557 */
length = min(length, this.get_field("length").to_length(interpreter)?);
length = min(length, this.get_field("length").to_length(context)?);
}
k += 1;
}
@ -1141,18 +1133,18 @@ impl Array {
pub(crate) fn reduce_right(
this: &Value,
args: &[Value],
interpreter: &mut Context,
context: &mut Context,
) -> Result<Value> {
let this: Value = this.to_object(interpreter)?.into();
let this: Value = this.to_object(context)?.into();
let callback = match args.get(0) {
Some(value) if value.is_function() => value,
_ => return interpreter.throw_type_error("reduceRight was called without a callback"),
_ => return context.throw_type_error("reduceRight was called without a callback"),
};
let initial_value = args.get(1).cloned().unwrap_or_else(Value::undefined);
let mut length = this.get_field("length").to_length(interpreter)?;
let mut length = this.get_field("length").to_length(context)?;
if length == 0 {
return if initial_value.is_undefined() {
interpreter.throw_type_error(
context.throw_type_error(
"reduceRight was called on an empty array and with no initial value",
)
} else {
@ -1175,7 +1167,7 @@ impl Array {
k -= 1;
}
if !k_present {
return interpreter.throw_type_error(
return context.throw_type_error(
"reduceRight was called on an empty array and with no initial value",
);
}
@ -1189,11 +1181,11 @@ impl Array {
while k != usize::MAX {
if this.has_field(k) {
let arguments = [accumulator, this.get_field(k), Value::from(k), this.clone()];
accumulator = interpreter.call(&callback, &Value::undefined(), &arguments)?;
accumulator = context.call(&callback, &Value::undefined(), &arguments)?;
/* We keep track of possibly shortened length in order to prevent unnecessary iteration.
It may also be necessary to do this since shortening the array length does not
delete array elements. See: https://github.com/boa-dev/boa/issues/557 */
length = min(length, this.get_field("length").to_length(interpreter)?);
length = min(length, this.get_field("length").to_length(context)?);
// move k to the last defined element if necessary or return if the length was set to 0
if k >= length {
@ -1223,8 +1215,8 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn values(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::Value)
pub(crate) fn values(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(context, this.clone(), ArrayIterationKind::Value)
}
/// `Array.prototype.keys( )`
@ -1237,8 +1229,8 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn keys(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::Key)
pub(crate) fn keys(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(context, this.clone(), ArrayIterationKind::Key)
}
/// `Array.prototype.entries( )`
@ -1251,7 +1243,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn entries(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::KeyAndValue)
pub(crate) fn entries(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(context, this.clone(), ArrayIterationKind::KeyAndValue)
}
}

657
boa/src/builtins/array/tests.rs

File diff suppressed because it is too large Load Diff

35
boa/src/builtins/bigint/mod.rs

@ -14,14 +14,13 @@
use crate::{
builtins::BuiltIn,
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{RcBigInt, Value},
BoaProfiler, Context, Result,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -103,7 +102,7 @@ impl BigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue
#[inline]
fn this_bigint_value(value: &Value, ctx: &mut Context) -> Result<RcBigInt> {
fn this_bigint_value(value: &Value, context: &mut Context) -> Result<RcBigInt> {
match value {
// 1. If Type(value) is BigInt, return value.
Value::BigInt(ref bigint) => return Ok(bigint.clone()),
@ -120,7 +119,7 @@ impl BigInt {
}
// 3. Throw a TypeError exception.
Err(ctx.construct_type_error("'this' is not a BigInt"))
Err(context.construct_type_error("'this' is not a BigInt"))
}
/// `BigInt.prototype.toString( [radix] )`
@ -134,18 +133,18 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn to_string(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let radix = if !args.is_empty() {
args[0].to_integer(ctx)? as i32
args[0].to_integer(context)? as i32
} else {
10
};
if radix < 2 || radix > 36 {
return ctx
return context
.throw_range_error("radix must be an integer at least 2 and no greater than 36");
}
Ok(Value::from(
Self::this_bigint_value(this, ctx)?.to_string_radix(radix as u32),
Self::this_bigint_value(this, context)?.to_string_radix(radix as u32),
))
}
@ -159,8 +158,8 @@ impl BigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf
pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_bigint_value(this, ctx)?))
pub(crate) fn value_of(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_bigint_value(this, context)?))
}
/// `BigInt.asIntN()`
@ -170,8 +169,8 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_int_n(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let (modulo, bits) = Self::calculate_as_uint_n(args, ctx)?;
pub(crate) fn as_int_n(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let (modulo, bits) = Self::calculate_as_uint_n(args, context)?;
if bits > 0
&& modulo
@ -197,8 +196,8 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.asuintn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_uint_n(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let (modulo, _) = Self::calculate_as_uint_n(args, ctx)?;
pub(crate) fn as_uint_n(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let (modulo, _) = Self::calculate_as_uint_n(args, context)?;
Ok(Value::from(modulo))
}
@ -208,7 +207,7 @@ impl BigInt {
/// This function expects the same arguments as `as_uint_n` and wraps the value of a `BigInt`.
/// Additionally to the wrapped unsigned value it returns the converted `bits` argument, so it
/// can be reused from the `as_int_n` method.
fn calculate_as_uint_n(args: &[Value], ctx: &mut Context) -> Result<(BigInt, u32)> {
fn calculate_as_uint_n(args: &[Value], context: &mut Context) -> Result<(BigInt, u32)> {
use std::convert::TryFrom;
let undefined_value = Value::undefined();
@ -216,10 +215,10 @@ impl BigInt {
let bits_arg = args.get(0).unwrap_or(&undefined_value);
let bigint_arg = args.get(1).unwrap_or(&undefined_value);
let bits = bits_arg.to_index(ctx)?;
let bits = bits_arg.to_index(context)?;
let bits = u32::try_from(bits).unwrap_or(u32::MAX);
let bigint = bigint_arg.to_bigint(ctx)?;
let bigint = bigint_arg.to_bigint(context)?;
Ok((
bigint.as_inner().clone().mod_floor(
@ -237,5 +236,5 @@ unsafe impl Trace for BigInt {
// BigInt type implements an empty trace becasue the inner structure
// `num_bigint::BigInt` does not implement `Trace` trait.
// If it did this would be unsound.
unsafe_empty_trace!();
empty_trace!();
}

266
boa/src/builtins/bigint/tests.rs

@ -2,84 +2,84 @@ use crate::{forward, Context};
#[test]
fn equality() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "0n == 0n"), "true");
assert_eq!(forward(&mut engine, "1n == 0n"), "false");
assert_eq!(forward(&mut context, "0n == 0n"), "true");
assert_eq!(forward(&mut context, "1n == 0n"), "false");
assert_eq!(
forward(
&mut engine,
&mut context,
"1000000000000000000000000000000000n == 1000000000000000000000000000000000n"
),
"true"
);
assert_eq!(forward(&mut engine, "0n == ''"), "true");
assert_eq!(forward(&mut engine, "100n == '100'"), "true");
assert_eq!(forward(&mut engine, "100n == '100.5'"), "false");
assert_eq!(forward(&mut context, "0n == ''"), "true");
assert_eq!(forward(&mut context, "100n == '100'"), "true");
assert_eq!(forward(&mut context, "100n == '100.5'"), "false");
assert_eq!(
forward(&mut engine, "10000000000000000n == '10000000000000000'"),
forward(&mut context, "10000000000000000n == '10000000000000000'"),
"true"
);
assert_eq!(forward(&mut engine, "'' == 0n"), "true");
assert_eq!(forward(&mut engine, "'100' == 100n"), "true");
assert_eq!(forward(&mut engine, "'100.5' == 100n"), "false");
assert_eq!(forward(&mut context, "'' == 0n"), "true");
assert_eq!(forward(&mut context, "'100' == 100n"), "true");
assert_eq!(forward(&mut context, "'100.5' == 100n"), "false");
assert_eq!(
forward(&mut engine, "'10000000000000000' == 10000000000000000n"),
forward(&mut context, "'10000000000000000' == 10000000000000000n"),
"true"
);
assert_eq!(forward(&mut engine, "0n == 0"), "true");
assert_eq!(forward(&mut engine, "0n == 0.0"), "true");
assert_eq!(forward(&mut engine, "100n == 100"), "true");
assert_eq!(forward(&mut engine, "100n == 100.0"), "true");
assert_eq!(forward(&mut engine, "100n == '100.5'"), "false");
assert_eq!(forward(&mut engine, "100n == '1005'"), "false");
assert_eq!(forward(&mut context, "0n == 0"), "true");
assert_eq!(forward(&mut context, "0n == 0.0"), "true");
assert_eq!(forward(&mut context, "100n == 100"), "true");
assert_eq!(forward(&mut context, "100n == 100.0"), "true");
assert_eq!(forward(&mut context, "100n == '100.5'"), "false");
assert_eq!(forward(&mut context, "100n == '1005'"), "false");
assert_eq!(
forward(&mut engine, "10000000000000000n == 10000000000000000"),
forward(&mut context, "10000000000000000n == 10000000000000000"),
"true"
);
assert_eq!(forward(&mut engine, "0 == 0n"), "true");
assert_eq!(forward(&mut engine, "0.0 == 0n"), "true");
assert_eq!(forward(&mut engine, "100 == 100n"), "true");
assert_eq!(forward(&mut engine, "100.0 == 100n"), "true");
assert_eq!(forward(&mut engine, "100.5 == 100n"), "false");
assert_eq!(forward(&mut engine, "1005 == 100n"), "false");
assert_eq!(forward(&mut context, "0 == 0n"), "true");
assert_eq!(forward(&mut context, "0.0 == 0n"), "true");
assert_eq!(forward(&mut context, "100 == 100n"), "true");
assert_eq!(forward(&mut context, "100.0 == 100n"), "true");
assert_eq!(forward(&mut context, "100.5 == 100n"), "false");
assert_eq!(forward(&mut context, "1005 == 100n"), "false");
assert_eq!(
forward(&mut engine, "10000000000000000 == 10000000000000000n"),
forward(&mut context, "10000000000000000 == 10000000000000000n"),
"true"
);
}
#[test]
fn bigint_function_conversion_from_integer() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "BigInt(1000)"), "1000n");
assert_eq!(forward(&mut context, "BigInt(1000)"), "1000n");
assert_eq!(
forward(&mut engine, "BigInt(20000000000000000)"),
forward(&mut context, "BigInt(20000000000000000)"),
"20000000000000000n"
);
assert_eq!(
forward(&mut engine, "BigInt(1000000000000000000000000000000000)"),
forward(&mut context, "BigInt(1000000000000000000000000000000000)"),
"999999999999999945575230987042816n"
);
}
#[test]
fn bigint_function_conversion_from_rational() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "BigInt(0.0)"), "0n");
assert_eq!(forward(&mut engine, "BigInt(1.0)"), "1n");
assert_eq!(forward(&mut engine, "BigInt(10000.0)"), "10000n");
assert_eq!(forward(&mut context, "BigInt(0.0)"), "0n");
assert_eq!(forward(&mut context, "BigInt(1.0)"), "1n");
assert_eq!(forward(&mut context, "BigInt(10000.0)"), "10000n");
}
#[test]
fn bigint_function_conversion_from_rational_with_fractional_part() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
try {
@ -89,14 +89,14 @@ fn bigint_function_conversion_from_rational_with_fractional_part() {
}
"#;
assert_eq!(
forward(&mut engine, scenario),
forward(&mut context, scenario),
"\"TypeError: The number 0.1 cannot be converted to a BigInt because it is not an integer\""
);
}
#[test]
fn bigint_function_conversion_from_null() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
try {
@ -106,14 +106,14 @@ fn bigint_function_conversion_from_null() {
}
"#;
assert_eq!(
forward(&mut engine, scenario),
forward(&mut context, scenario),
"\"TypeError: cannot convert null to a BigInt\""
);
}
#[test]
fn bigint_function_conversion_from_undefined() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
try {
@ -123,178 +123,178 @@ fn bigint_function_conversion_from_undefined() {
}
"#;
assert_eq!(
forward(&mut engine, scenario),
forward(&mut context, scenario),
"\"TypeError: cannot convert undefined to a BigInt\""
);
}
#[test]
fn bigint_function_conversion_from_string() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "BigInt('')"), "0n");
assert_eq!(forward(&mut engine, "BigInt(' ')"), "0n");
assert_eq!(forward(&mut context, "BigInt('')"), "0n");
assert_eq!(forward(&mut context, "BigInt(' ')"), "0n");
assert_eq!(
forward(&mut engine, "BigInt('200000000000000000')"),
forward(&mut context, "BigInt('200000000000000000')"),
"200000000000000000n"
);
assert_eq!(
forward(&mut engine, "BigInt('1000000000000000000000000000000000')"),
forward(&mut context, "BigInt('1000000000000000000000000000000000')"),
"1000000000000000000000000000000000n"
);
assert_eq!(forward(&mut engine, "BigInt('0b1111')"), "15n");
assert_eq!(forward(&mut engine, "BigInt('0o70')"), "56n");
assert_eq!(forward(&mut engine, "BigInt('0xFF')"), "255n");
assert_eq!(forward(&mut context, "BigInt('0b1111')"), "15n");
assert_eq!(forward(&mut context, "BigInt('0o70')"), "56n");
assert_eq!(forward(&mut context, "BigInt('0xFF')"), "255n");
}
#[test]
fn add() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "10000n + 1000n"), "11000n");
assert_eq!(forward(&mut context, "10000n + 1000n"), "11000n");
}
#[test]
fn sub() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "10000n - 1000n"), "9000n");
assert_eq!(forward(&mut context, "10000n - 1000n"), "9000n");
}
#[test]
fn mul() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(
forward(&mut engine, "123456789n * 102030n"),
forward(&mut context, "123456789n * 102030n"),
"12596296181670n"
);
}
#[test]
fn div() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "15000n / 50n"), "300n");
assert_eq!(forward(&mut context, "15000n / 50n"), "300n");
}
#[test]
fn div_with_truncation() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "15001n / 50n"), "300n");
assert_eq!(forward(&mut context, "15001n / 50n"), "300n");
}
#[test]
fn r#mod() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "15007n % 10n"), "7n");
assert_eq!(forward(&mut context, "15007n % 10n"), "7n");
}
#[test]
fn pow() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(
forward(&mut engine, "100n ** 10n"),
forward(&mut context, "100n ** 10n"),
"100000000000000000000n"
);
}
#[test]
fn pow_negative_exponent() {
let mut engine = Context::new();
let mut context = Context::new();
assert_throws(&mut engine, "10n ** (-10n)", "RangeError");
assert_throws(&mut context, "10n ** (-10n)", "RangeError");
}
#[test]
fn shl() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "8n << 2n"), "32n");
assert_eq!(forward(&mut context, "8n << 2n"), "32n");
}
#[test]
fn shl_out_of_range() {
let mut engine = Context::new();
let mut context = Context::new();
assert_throws(&mut engine, "1000n << 1000000000000000n", "RangeError");
assert_throws(&mut context, "1000n << 1000000000000000n", "RangeError");
}
#[test]
fn shr() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "8n >> 2n"), "2n");
assert_eq!(forward(&mut context, "8n >> 2n"), "2n");
}
#[test]
fn shr_out_of_range() {
let mut engine = Context::new();
let mut context = Context::new();
assert_throws(&mut engine, "1000n >> 1000000000000000n", "RangeError");
assert_throws(&mut context, "1000n >> 1000000000000000n", "RangeError");
}
#[test]
fn to_string() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "1000n.toString()"), "\"1000\"");
assert_eq!(forward(&mut engine, "1000n.toString(2)"), "\"1111101000\"");
assert_eq!(forward(&mut engine, "255n.toString(16)"), "\"ff\"");
assert_eq!(forward(&mut engine, "1000n.toString(36)"), "\"rs\"");
assert_eq!(forward(&mut context, "1000n.toString()"), "\"1000\"");
assert_eq!(forward(&mut context, "1000n.toString(2)"), "\"1111101000\"");
assert_eq!(forward(&mut context, "255n.toString(16)"), "\"ff\"");
assert_eq!(forward(&mut context, "1000n.toString(36)"), "\"rs\"");
}
#[test]
fn to_string_invalid_radix() {
let mut engine = Context::new();
let mut context = Context::new();
assert_throws(&mut engine, "10n.toString(null)", "RangeError");
assert_throws(&mut engine, "10n.toString(-1)", "RangeError");
assert_throws(&mut engine, "10n.toString(37)", "RangeError");
assert_throws(&mut context, "10n.toString(null)", "RangeError");
assert_throws(&mut context, "10n.toString(-1)", "RangeError");
assert_throws(&mut context, "10n.toString(37)", "RangeError");
}
#[test]
fn as_int_n() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(forward(&mut engine, "BigInt.asIntN(0, 1n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asIntN(1, 1n)"), "-1n");
assert_eq!(forward(&mut engine, "BigInt.asIntN(3, 10n)"), "2n");
assert_eq!(forward(&mut engine, "BigInt.asIntN({}, 1n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asIntN(2, 0n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asIntN(2, -0n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asIntN(0, 1n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asIntN(1, 1n)"), "-1n");
assert_eq!(forward(&mut context, "BigInt.asIntN(3, 10n)"), "2n");
assert_eq!(forward(&mut context, "BigInt.asIntN({}, 1n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asIntN(2, 0n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asIntN(2, -0n)"), "0n");
assert_eq!(
forward(&mut engine, "BigInt.asIntN(2, -123456789012345678901n)"),
forward(&mut context, "BigInt.asIntN(2, -123456789012345678901n)"),
"-1n"
);
assert_eq!(
forward(&mut engine, "BigInt.asIntN(2, -123456789012345678900n)"),
forward(&mut context, "BigInt.asIntN(2, -123456789012345678900n)"),
"0n"
);
assert_eq!(
forward(&mut engine, "BigInt.asIntN(2, 123456789012345678900n)"),
forward(&mut context, "BigInt.asIntN(2, 123456789012345678900n)"),
"0n"
);
assert_eq!(
forward(&mut engine, "BigInt.asIntN(2, 123456789012345678901n)"),
forward(&mut context, "BigInt.asIntN(2, 123456789012345678901n)"),
"1n"
);
assert_eq!(
forward(
&mut engine,
&mut context,
"BigInt.asIntN(200, 0xcffffffffffffffffffffffffffffffffffffffffffffffffffn)"
),
"-1n"
);
assert_eq!(
forward(
&mut engine,
&mut context,
"BigInt.asIntN(201, 0xcffffffffffffffffffffffffffffffffffffffffffffffffffn)"
),
"1606938044258990275541962092341162602522202993782792835301375n"
@ -302,14 +302,14 @@ fn as_int_n() {
assert_eq!(
forward(
&mut engine,
&mut context,
"BigInt.asIntN(200, 0xc89e081df68b65fedb32cffea660e55df9605650a603ad5fc54n)"
),
"-741470203160010616172516490008037905920749803227695190508460n"
);
assert_eq!(
forward(
&mut engine,
&mut context,
"BigInt.asIntN(201, 0xc89e081df68b65fedb32cffea660e55df9605650a603ad5fc54n)"
),
"865467841098979659369445602333124696601453190555097644792916n"
@ -318,63 +318,63 @@ fn as_int_n() {
#[test]
fn as_int_n_errors() {
let mut engine = Context::new();
let mut context = Context::new();
assert_throws(&mut engine, "BigInt.asIntN(-1, 0n)", "RangeError");
assert_throws(&mut engine, "BigInt.asIntN(-2.5, 0n)", "RangeError");
assert_throws(&mut context, "BigInt.asIntN(-1, 0n)", "RangeError");
assert_throws(&mut context, "BigInt.asIntN(-2.5, 0n)", "RangeError");
assert_throws(
&mut engine,
&mut context,
"BigInt.asIntN(9007199254740992, 0n)",
"RangeError",
);
assert_throws(&mut engine, "BigInt.asIntN(0n, 0n)", "TypeError");
assert_throws(&mut context, "BigInt.asIntN(0n, 0n)", "TypeError");
}
#[test]
fn as_uint_n() {
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "BigInt.asUintN(0, -2n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(0, -1n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(0, 0n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(0, 1n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(0, 2n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(1, -3n)"), "1n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(1, -2n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(1, -1n)"), "1n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(1, 0n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(1, 1n)"), "1n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(1, 2n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(1, 3n)"), "1n");
let mut context = Context::new();
assert_eq!(forward(&mut context, "BigInt.asUintN(0, -2n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(0, -1n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(0, 0n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(0, 1n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(0, 2n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(1, -3n)"), "1n");
assert_eq!(forward(&mut context, "BigInt.asUintN(1, -2n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(1, -1n)"), "1n");
assert_eq!(forward(&mut context, "BigInt.asUintN(1, 0n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(1, 1n)"), "1n");
assert_eq!(forward(&mut context, "BigInt.asUintN(1, 2n)"), "0n");
assert_eq!(forward(&mut context, "BigInt.asUintN(1, 3n)"), "1n");
assert_eq!(
forward(&mut engine, "BigInt.asUintN(1, -123456789012345678901n)"),
forward(&mut context, "BigInt.asUintN(1, -123456789012345678901n)"),
"1n"
);
assert_eq!(
forward(&mut engine, "BigInt.asUintN(1, -123456789012345678900n)"),
forward(&mut context, "BigInt.asUintN(1, -123456789012345678900n)"),
"0n"
);
assert_eq!(
forward(&mut engine, "BigInt.asUintN(1, 123456789012345678900n)"),
forward(&mut context, "BigInt.asUintN(1, 123456789012345678900n)"),
"0n"
);
assert_eq!(
forward(&mut engine, "BigInt.asUintN(1, 123456789012345678901n)"),
forward(&mut context, "BigInt.asUintN(1, 123456789012345678901n)"),
"1n"
);
assert_eq!(
forward(
&mut engine,
&mut context,
"BigInt.asUintN(200, 0xbffffffffffffffffffffffffffffffffffffffffffffffffffn)"
),
"1606938044258990275541962092341162602522202993782792835301375n"
);
assert_eq!(
forward(
&mut engine,
&mut context,
"BigInt.asUintN(201, 0xbffffffffffffffffffffffffffffffffffffffffffffffffffn)"
),
"3213876088517980551083924184682325205044405987565585670602751n"
@ -383,31 +383,31 @@ fn as_uint_n() {
#[test]
fn as_uint_n_errors() {
let mut engine = Context::new();
let mut context = Context::new();
assert_throws(&mut engine, "BigInt.asUintN(-1, 0n)", "RangeError");
assert_throws(&mut engine, "BigInt.asUintN(-2.5, 0n)", "RangeError");
assert_throws(&mut context, "BigInt.asUintN(-1, 0n)", "RangeError");
assert_throws(&mut context, "BigInt.asUintN(-2.5, 0n)", "RangeError");
assert_throws(
&mut engine,
&mut context,
"BigInt.asUintN(9007199254740992, 0n)",
"RangeError",
);
assert_throws(&mut engine, "BigInt.asUintN(0n, 0n)", "TypeError");
assert_throws(&mut context, "BigInt.asUintN(0n, 0n)", "TypeError");
}
fn assert_throws(engine: &mut Context, src: &str, error_type: &str) {
let result = forward(engine, src);
fn assert_throws(context: &mut Context, src: &str, error_type: &str) {
let result = forward(context, src);
assert!(result.contains(error_type));
}
#[test]
fn division_by_zero() {
let mut engine = Context::new();
assert_throws(&mut engine, "1n/0n", "RangeError");
let mut context = Context::new();
assert_throws(&mut context, "1n/0n", "RangeError");
}
#[test]
fn remainder_by_zero() {
let mut engine = Context::new();
assert_throws(&mut engine, "1n % 0n", "RangeError");
let mut context = Context::new();
assert_throws(&mut context, "1n % 0n", "RangeError");
}

12
boa/src/builtins/boolean/mod.rs

@ -70,7 +70,7 @@ impl Boolean {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
fn this_boolean_value(value: &Value, ctx: &mut Context) -> Result<bool> {
fn this_boolean_value(value: &Value, context: &mut Context) -> Result<bool> {
match value {
Value::Boolean(boolean) => return Ok(*boolean),
Value::Object(ref object) => {
@ -82,7 +82,7 @@ impl Boolean {
_ => {}
}
Err(ctx.construct_type_error("'this' is not a boolean"))
Err(context.construct_type_error("'this' is not a boolean"))
}
/// The `toString()` method returns a string representing the specified `Boolean` object.
@ -94,8 +94,8 @@ impl Boolean {
/// [spec]: https://tc39.es/ecma262/#sec-boolean-object
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let boolean = Self::this_boolean_value(this, ctx)?;
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let boolean = Self::this_boolean_value(this, context)?;
Ok(Value::from(boolean.to_string()))
}
@ -108,7 +108,7 @@ impl Boolean {
/// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf
#[inline]
pub(crate) fn value_of(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_boolean_value(this, ctx)?))
pub(crate) fn value_of(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_boolean_value(this, context)?))
}
}

28
boa/src/builtins/boolean/tests.rs

@ -4,14 +4,14 @@ use crate::{forward, forward_val, value::same_value, Context};
#[allow(clippy::unwrap_used)]
#[test]
fn construct_and_call() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var one = new Boolean(1);
var zero = Boolean(0);
"#;
eprintln!("{}", forward(&mut engine, init));
let one = forward_val(&mut engine, "one").unwrap();
let zero = forward_val(&mut engine, "zero").unwrap();
eprintln!("{}", forward(&mut context, init));
let one = forward_val(&mut context, "one").unwrap();
let zero = forward_val(&mut context, "zero").unwrap();
assert_eq!(one.is_object(), true);
assert_eq!(zero.is_boolean(), true);
@ -19,7 +19,7 @@ fn construct_and_call() {
#[test]
fn constructor_gives_true_instance() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var trueVal = new Boolean(true);
var trueNum = new Boolean(1);
@ -27,11 +27,11 @@ fn constructor_gives_true_instance() {
var trueBool = new Boolean(trueVal);
"#;
eprintln!("{}", forward(&mut engine, init));
let true_val = forward_val(&mut engine, "trueVal").expect("value expected");
let true_num = forward_val(&mut engine, "trueNum").expect("value expected");
let true_string = forward_val(&mut engine, "trueString").expect("value expected");
let true_bool = forward_val(&mut engine, "trueBool").expect("value expected");
eprintln!("{}", forward(&mut context, init));
let true_val = forward_val(&mut context, "trueVal").expect("value expected");
let true_num = forward_val(&mut context, "trueNum").expect("value expected");
let true_string = forward_val(&mut context, "trueString").expect("value expected");
let true_bool = forward_val(&mut context, "trueBool").expect("value expected");
// Values should all be objects
assert_eq!(true_val.is_object(), true);
@ -48,15 +48,15 @@ fn constructor_gives_true_instance() {
#[test]
fn instances_have_correct_proto_set() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var boolInstance = new Boolean(true);
var boolProto = Boolean.prototype;
"#;
eprintln!("{}", forward(&mut engine, init));
let bool_instance = forward_val(&mut engine, "boolInstance").expect("value expected");
let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected");
eprintln!("{}", forward(&mut context, init));
let bool_instance = forward_val(&mut context, "boolInstance").expect("value expected");
let bool_prototype = forward_val(&mut context, "boolProto").expect("value expected");
assert!(same_value(
&bool_instance.as_object().unwrap().prototype_instance(),

136
boa/src/builtins/console/mod.rs

@ -58,8 +58,12 @@ pub(crate) fn logger(msg: LogMessage, console_state: &Console) {
}
/// This represents the `console` formatter.
pub fn formatter(data: &[Value], ctx: &mut Context) -> Result<String> {
let target = data.get(0).cloned().unwrap_or_default().to_string(ctx)?;
pub fn formatter(data: &[Value], context: &mut Context) -> Result<String> {
let target = data
.get(0)
.cloned()
.unwrap_or_default()
.to_string(context)?;
match data.len() {
0 => Ok(String::new()),
@ -78,7 +82,7 @@ pub fn formatter(data: &[Value], ctx: &mut Context) -> Result<String> {
.get(arg_index)
.cloned()
.unwrap_or_default()
.to_integer(ctx)?;
.to_integer(context)?;
formatted.push_str(&format!("{}", arg));
arg_index += 1;
}
@ -88,7 +92,7 @@ pub fn formatter(data: &[Value], ctx: &mut Context) -> Result<String> {
.get(arg_index)
.cloned()
.unwrap_or_default()
.to_number(ctx)?;
.to_number(context)?;
formatted.push_str(&format!("{number:.prec$}", number = arg, prec = 6));
arg_index += 1
}
@ -104,7 +108,7 @@ pub fn formatter(data: &[Value], ctx: &mut Context) -> Result<String> {
.get(arg_index)
.cloned()
.unwrap_or_default()
.to_string(ctx)?;
.to_string(context)?;
formatted.push_str(&arg);
arg_index += 1
}
@ -122,7 +126,7 @@ pub fn formatter(data: &[Value], ctx: &mut Context) -> Result<String> {
/* unformatted data */
for rest in data.iter().skip(arg_index) {
formatted.push_str(&format!(" {}", rest.to_string(ctx)?))
formatted.push_str(&format!(" {}", rest.to_string(context)?))
}
Ok(formatted)
@ -188,7 +192,7 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#assert
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert
pub(crate) fn assert(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn assert(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default();
if !assertion {
@ -203,7 +207,10 @@ impl Console {
args[0] = Value::from(concat);
}
logger(LogMessage::Error(formatter(&args, ctx)?), ctx.console());
logger(
LogMessage::Error(formatter(&args, context)?),
context.console(),
);
}
Ok(Value::undefined())
@ -219,8 +226,8 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#clear
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear
pub(crate) fn clear(_: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ctx.console_mut().groups.clear();
pub(crate) fn clear(_: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
context.console_mut().groups.clear();
Ok(Value::undefined())
}
@ -234,8 +241,11 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#debug
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug
pub(crate) fn debug(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
pub(crate) fn debug(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
logger(
LogMessage::Log(formatter(args, context)?),
context.console(),
);
Ok(Value::undefined())
}
@ -249,8 +259,11 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#error
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error
pub(crate) fn error(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Error(formatter(args, ctx)?), ctx.console());
pub(crate) fn error(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
logger(
LogMessage::Error(formatter(args, context)?),
context.console(),
);
Ok(Value::undefined())
}
@ -264,8 +277,11 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#info
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info
pub(crate) fn info(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Info(formatter(args, ctx)?), ctx.console());
pub(crate) fn info(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
logger(
LogMessage::Info(formatter(args, context)?),
context.console(),
);
Ok(Value::undefined())
}
@ -279,8 +295,11 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log
pub(crate) fn log(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
pub(crate) fn log(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
logger(
LogMessage::Log(formatter(args, context)?),
context.console(),
);
Ok(Value::undefined())
}
@ -294,14 +313,17 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#trace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace
pub(crate) fn trace(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn trace(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if !args.is_empty() {
logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
logger(
LogMessage::Log(formatter(args, context)?),
context.console(),
);
/* TODO: get and print stack trace */
logger(
LogMessage::Log("Not implemented: <stack trace>".to_string()),
ctx.console(),
context.console(),
)
}
@ -318,8 +340,11 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#warn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn
pub(crate) fn warn(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Warn(formatter(args, ctx)?), ctx.console());
pub(crate) fn warn(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
logger(
LogMessage::Warn(formatter(args, context)?),
context.console(),
);
Ok(Value::undefined())
}
@ -333,17 +358,20 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#count
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count
pub(crate) fn count(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn count(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
Some(value) => value.to_string(context)?,
None => "default".into(),
};
let msg = format!("count {}:", &label);
let c = ctx.console_mut().count_map.entry(label).or_insert(0);
let c = context.console_mut().count_map.entry(label).or_insert(0);
*c += 1;
logger(LogMessage::Info(format!("{} {}", msg, c)), ctx.console());
logger(
LogMessage::Info(format!("{} {}", msg, c)),
context.console(),
);
Ok(Value::undefined())
}
@ -357,17 +385,17 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#countreset
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset
pub(crate) fn count_reset(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn count_reset(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
Some(value) => value.to_string(context)?,
None => "default".into(),
};
ctx.console_mut().count_map.remove(&label);
context.console_mut().count_map.remove(&label);
logger(
LogMessage::Warn(format!("countReset {}", label)),
ctx.console(),
context.console(),
);
Ok(Value::undefined())
@ -391,20 +419,20 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#time
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time
pub(crate) fn time(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn time(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
Some(value) => value.to_string(context)?,
None => "default".into(),
};
if ctx.console().timer_map.get(&label).is_some() {
if context.console().timer_map.get(&label).is_some() {
logger(
LogMessage::Warn(format!("Timer '{}' already exist", label)),
ctx.console(),
context.console(),
);
} else {
let time = Self::system_time_in_ms();
ctx.console_mut().timer_map.insert(label, time);
context.console_mut().timer_map.insert(label, time);
}
Ok(Value::undefined())
@ -420,23 +448,23 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#timelog
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog
pub(crate) fn time_log(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn time_log(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
Some(value) => value.to_string(context)?,
None => "default".into(),
};
if let Some(t) = ctx.console().timer_map.get(&label) {
if let Some(t) = context.console().timer_map.get(&label) {
let time = Self::system_time_in_ms();
let mut concat = format!("{}: {} ms", label, time - t);
for msg in args.iter().skip(1) {
concat = concat + " " + &msg.display().to_string();
}
logger(LogMessage::Log(concat), ctx.console());
logger(LogMessage::Log(concat), context.console());
} else {
logger(
LogMessage::Warn(format!("Timer '{}' doesn't exist", label)),
ctx.console(),
context.console(),
);
}
@ -453,22 +481,22 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#timeend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd
pub(crate) fn time_end(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn time_end(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
Some(value) => value.to_string(context)?,
None => "default".into(),
};
if let Some(t) = ctx.console_mut().timer_map.remove(label.as_str()) {
if let Some(t) = context.console_mut().timer_map.remove(label.as_str()) {
let time = Self::system_time_in_ms();
logger(
LogMessage::Info(format!("{}: {} ms - timer removed", label, time - t)),
ctx.console(),
context.console(),
);
} else {
logger(
LogMessage::Warn(format!("Timer '{}' doesn't exist", label)),
ctx.console(),
context.console(),
);
}
@ -485,14 +513,14 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#group
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group
pub(crate) fn group(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let group_label = formatter(args, ctx)?;
pub(crate) fn group(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let group_label = formatter(args, context)?;
logger(
LogMessage::Info(format!("group: {}", &group_label)),
ctx.console(),
context.console(),
);
ctx.console_mut().groups.push(group_label);
context.console_mut().groups.push(group_label);
Ok(Value::undefined())
}
@ -507,8 +535,8 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#groupend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd
pub(crate) fn group_end(_: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ctx.console_mut().groups.pop();
pub(crate) fn group_end(_: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
context.console_mut().groups.pop();
Ok(Value::undefined())
}
@ -523,11 +551,11 @@ impl Console {
///
/// [spec]: https://console.spec.whatwg.org/#dir
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir
pub(crate) fn dir(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn dir(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let undefined = Value::undefined();
logger(
LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined), true)),
ctx.console(),
context.console(),
);
Ok(Value::undefined())

28
boa/src/builtins/console/tests.rs

@ -2,41 +2,41 @@ use crate::{builtins::console::formatter, Context, Value};
#[test]
fn formatter_no_args_is_empty_string() {
let mut engine = Context::new();
assert_eq!(formatter(&[], &mut engine).unwrap(), "");
let mut context = Context::new();
assert_eq!(formatter(&[], &mut context).unwrap(), "");
}
#[test]
fn formatter_empty_format_string_is_empty_string() {
let mut engine = Context::new();
let mut context = Context::new();
let val = Value::string("".to_string());
assert_eq!(formatter(&[val], &mut engine).unwrap(), "");
assert_eq!(formatter(&[val], &mut context).unwrap(), "");
}
#[test]
fn formatter_format_without_args_renders_verbatim() {
let mut engine = Context::new();
let mut context = Context::new();
let val = [Value::string("%d %s %% %f")];
let res = formatter(&val, &mut engine).unwrap();
let res = formatter(&val, &mut context).unwrap();
assert_eq!(res, "%d %s %% %f");
}
#[test]
fn formatter_empty_format_string_concatenates_rest_of_args() {
let mut engine = Context::new();
let mut context = Context::new();
let val = [
Value::string(""),
Value::string("to powinno zostać"),
Value::string("połączone"),
];
let res = formatter(&val, &mut engine).unwrap();
let res = formatter(&val, &mut context).unwrap();
assert_eq!(res, " to powinno zostać połączone");
}
#[test]
fn formatter_utf_8_checks() {
let mut engine = Context::new();
let mut context = Context::new();
let val = [
Value::string("Są takie chwile %dą %są tu%sów %привет%ź".to_string()),
@ -44,28 +44,28 @@ fn formatter_utf_8_checks() {
Value::rational(1.23),
Value::string("ł".to_string()),
];
let res = formatter(&val, &mut engine).unwrap();
let res = formatter(&val, &mut context).unwrap();
assert_eq!(res, "Są takie chwile 123ą 1.23ą tułów %привет%ź");
}
#[test]
fn formatter_trailing_format_leader_renders() {
let mut engine = Context::new();
let mut context = Context::new();
let val = [
Value::string("%%%%%".to_string()),
Value::string("|".to_string()),
];
let res = formatter(&val, &mut engine).unwrap();
let res = formatter(&val, &mut context).unwrap();
assert_eq!(res, "%%% |");
}
#[test]
#[allow(clippy::approx_constant)]
fn formatter_float_format_works() {
let mut engine = Context::new();
let mut context = Context::new();
let val = [Value::string("%f".to_string()), Value::rational(3.1415)];
let res = formatter(&val, &mut engine).unwrap();
let res = formatter(&val, &mut context).unwrap();
assert_eq!(res, "3.141500");
}

94
boa/src/builtins/date/mod.rs

@ -3,13 +3,13 @@ mod tests;
use crate::{
builtins::BuiltIn,
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{PreferredType, Value},
BoaProfiler, Context, Result,
};
use chrono::{prelude::*, Duration, LocalResult};
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt::Display;
const NANOS_IN_MS: f64 = 1_000_000f64;
@ -38,8 +38,8 @@ fn ignore_ambiguity<T>(result: LocalResult<T>) -> Option<T> {
macro_rules! getter_method {
($name:ident) => {{
fn get_value(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(this_time_value(this, ctx)?.$name()))
fn get_value(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
Ok(Value::from(this_time_value(this, context)?.$name()))
}
get_value
}};
@ -53,14 +53,14 @@ macro_rules! getter_method {
macro_rules! setter_method {
($name:ident($($e:expr),* $(,)?)) => {{
fn set_value(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let mut result = this_time_value(this, ctx)?;
fn set_value(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let mut result = this_time_value(this, context)?;
result.$name(
$(
args
.get($e)
.and_then(|value| {
value.to_numeric_number(ctx).map_or_else(
value.to_numeric_number(context).map_or_else(
|_| None,
|value| {
if value == 0f64 || value.is_normal() {
@ -96,7 +96,7 @@ impl Display for Date {
unsafe impl Trace for Date {
// Date is a stack value, it doesn't require tracing.
// only safe if `chrono` never implements `Trace` for `NaiveDateTime`
unsafe_empty_trace!();
empty_trace!();
}
impl Default for Date {
@ -323,15 +323,19 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if this.is_global() {
Self::make_date_string()
} else if args.is_empty() {
Self::make_date_now(this)
} else if args.len() == 1 {
Self::make_date_single(this, args, ctx)
Self::make_date_single(this, args, context)
} else {
Self::make_date_multiple(this, args, ctx)
Self::make_date_multiple(this, args, context)
}
}
@ -378,18 +382,18 @@ impl Date {
pub(crate) fn make_date_single(
this: &Value,
args: &[Value],
ctx: &mut Context,
context: &mut Context,
) -> Result<Value> {
let value = &args[0];
let tv = match this_time_value(value, ctx) {
let tv = match this_time_value(value, context) {
Ok(dt) => dt.0,
_ => match value.to_primitive(ctx, PreferredType::Default)? {
_ => match value.to_primitive(context, PreferredType::Default)? {
Value::String(ref str) => match chrono::DateTime::parse_from_rfc3339(&str) {
Ok(dt) => Some(dt.naive_utc()),
_ => None,
},
tv => {
let tv = tv.to_number(ctx)?;
let tv = tv.to_number(context)?;
let secs = (tv / 1_000f64) as i64;
let nsecs = ((tv % 1_000f64) * 1_000_000f64) as u32;
NaiveDateTime::from_timestamp_opt(secs, nsecs)
@ -415,15 +419,25 @@ impl Date {
pub(crate) fn make_date_multiple(
this: &Value,
args: &[Value],
ctx: &mut Context,
context: &mut Context,
) -> Result<Value> {
let year = args[0].to_number(ctx)?;
let month = args[1].to_number(ctx)?;
let day = args.get(2).map_or(Ok(1f64), |value| value.to_number(ctx))?;
let hour = args.get(3).map_or(Ok(0f64), |value| value.to_number(ctx))?;
let min = args.get(4).map_or(Ok(0f64), |value| value.to_number(ctx))?;
let sec = args.get(5).map_or(Ok(0f64), |value| value.to_number(ctx))?;
let milli = args.get(6).map_or(Ok(0f64), |value| value.to_number(ctx))?;
let year = args[0].to_number(context)?;
let month = args[1].to_number(context)?;
let day = args
.get(2)
.map_or(Ok(1f64), |value| value.to_number(context))?;
let hour = args
.get(3)
.map_or(Ok(0f64), |value| value.to_number(context))?;
let min = args
.get(4)
.map_or(Ok(0f64), |value| value.to_number(context))?;
let sec = args
.get(5)
.map_or(Ok(0f64), |value| value.to_number(context))?;
let milli = args
.get(6)
.map_or(Ok(0f64), |value| value.to_number(context))?;
// If any of the args are infinity or NaN, return an invalid date.
if !check_normal_opt!(year, month, day, hour, min, sec, milli) {
@ -1257,7 +1271,7 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-date.parse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
pub(crate) fn parse(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn parse(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// This method is implementation-defined and discouraged, so we just require the same format as the string
// constructor.
@ -1265,7 +1279,7 @@ impl Date {
return Ok(Value::number(f64::NAN));
}
match DateTime::parse_from_rfc3339(&args[0].to_string(ctx)?) {
match DateTime::parse_from_rfc3339(&args[0].to_string(context)?) {
Ok(v) => Ok(Value::number(v.naive_utc().timestamp_millis() as f64)),
_ => Ok(Value::number(f64::NAN)),
}
@ -1281,16 +1295,28 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-date.utc
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC
pub(crate) fn utc(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn utc(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let year = args
.get(0)
.map_or(Ok(f64::NAN), |value| value.to_number(ctx))?;
let month = args.get(1).map_or(Ok(1f64), |value| value.to_number(ctx))?;
let day = args.get(2).map_or(Ok(1f64), |value| value.to_number(ctx))?;
let hour = args.get(3).map_or(Ok(0f64), |value| value.to_number(ctx))?;
let min = args.get(4).map_or(Ok(0f64), |value| value.to_number(ctx))?;
let sec = args.get(5).map_or(Ok(0f64), |value| value.to_number(ctx))?;
let milli = args.get(6).map_or(Ok(0f64), |value| value.to_number(ctx))?;
.map_or(Ok(f64::NAN), |value| value.to_number(context))?;
let month = args
.get(1)
.map_or(Ok(1f64), |value| value.to_number(context))?;
let day = args
.get(2)
.map_or(Ok(1f64), |value| value.to_number(context))?;
let hour = args
.get(3)
.map_or(Ok(0f64), |value| value.to_number(context))?;
let min = args
.get(4)
.map_or(Ok(0f64), |value| value.to_number(context))?;
let sec = args
.get(5)
.map_or(Ok(0f64), |value| value.to_number(context))?;
let milli = args
.get(6)
.map_or(Ok(0f64), |value| value.to_number(context))?;
if !check_normal_opt!(year, month, day, hour, min, sec, milli) {
return Ok(Value::number(f64::NAN));
@ -1331,11 +1357,11 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-thistimevalue
#[inline]
pub fn this_time_value(value: &Value, ctx: &mut Context) -> Result<Date> {
pub fn this_time_value(value: &Value, context: &mut Context) -> Result<Date> {
if let Value::Object(ref object) = value {
if let ObjectData::Date(ref date) = object.borrow().data {
return Ok(*date);
}
}
Err(ctx.construct_type_error("'this' is not a Date"))
Err(context.construct_type_error("'this' is not a Date"))
}

372
boa/src/builtins/date/tests.rs

File diff suppressed because it is too large Load Diff

8
boa/src/builtins/error/eval.rs

@ -56,9 +56,13 @@ impl EvalError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
this.set_field("message", message.to_string(context)?);
}
// This value is used by console.log and other routines to match Object type

8
boa/src/builtins/error/mod.rs

@ -73,9 +73,13 @@ impl Error {
/// `Error( message )`
///
/// Create a new error object.
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
this.set_field("message", message.to_string(context)?);
}
// This value is used by console.log and other routines to match Object type

8
boa/src/builtins/error/range.rs

@ -54,9 +54,13 @@ impl RangeError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
this.set_field("message", message.to_string(context)?);
}
// This value is used by console.log and other routines to match Object type

8
boa/src/builtins/error/reference.rs

@ -53,9 +53,13 @@ impl ReferenceError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
this.set_field("message", message.to_string(context)?);
}
// This value is used by console.log and other routines to match Object type

10
boa/src/builtins/error/syntax.rs

@ -1,7 +1,7 @@
//! This module implements the global `SyntaxError` object.
//!
//! The SyntaxError object represents an error when trying to interpret syntactically invalid code.
//! It is thrown when the JavaScript engine encounters tokens or token order that does not conform
//! It is thrown when the JavaScript context encounters tokens or token order that does not conform
//! to the syntax of the language when parsing code.
//!
//! More information:
@ -56,9 +56,13 @@ impl SyntaxError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
this.set_field("message", message.to_string(context)?);
}
// This value is used by console.log and other routines to match Object type

52
boa/src/builtins/error/tests.rs

@ -2,7 +2,7 @@ use crate::{forward, Context};
#[test]
fn error_to_string() {
let mut ctx = Context::new();
let mut context = Context::new();
let init = r#"
let e = new Error('1');
let name = new Error();
@ -13,68 +13,74 @@ fn error_to_string() {
let syntax_e = new SyntaxError('4');
let type_e = new TypeError('5');
"#;
forward(&mut ctx, init);
assert_eq!(forward(&mut ctx, "e.toString()"), "\"Error: 1\"");
assert_eq!(forward(&mut ctx, "name.toString()"), "\"Error\"");
assert_eq!(forward(&mut ctx, "message.toString()"), "\"message\"");
assert_eq!(forward(&mut ctx, "range_e.toString()"), "\"RangeError: 2\"");
forward(&mut context, init);
assert_eq!(forward(&mut context, "e.toString()"), "\"Error: 1\"");
assert_eq!(forward(&mut context, "name.toString()"), "\"Error\"");
assert_eq!(forward(&mut context, "message.toString()"), "\"message\"");
assert_eq!(
forward(&mut ctx, "ref_e.toString()"),
forward(&mut context, "range_e.toString()"),
"\"RangeError: 2\""
);
assert_eq!(
forward(&mut context, "ref_e.toString()"),
"\"ReferenceError: 3\""
);
assert_eq!(
forward(&mut ctx, "syntax_e.toString()"),
forward(&mut context, "syntax_e.toString()"),
"\"SyntaxError: 4\""
);
assert_eq!(forward(&mut ctx, "type_e.toString()"), "\"TypeError: 5\"");
assert_eq!(
forward(&mut context, "type_e.toString()"),
"\"TypeError: 5\""
);
}
#[test]
fn eval_error_name() {
let mut ctx = Context::new();
assert_eq!(forward(&mut ctx, "EvalError.name"), "\"EvalError\"");
let mut context = Context::new();
assert_eq!(forward(&mut context, "EvalError.name"), "\"EvalError\"");
}
#[test]
fn eval_error_length() {
let mut ctx = Context::new();
assert_eq!(forward(&mut ctx, "EvalError.length"), "1");
let mut context = Context::new();
assert_eq!(forward(&mut context, "EvalError.length"), "1");
}
#[test]
fn eval_error_to_string() {
let mut ctx = Context::new();
let mut context = Context::new();
assert_eq!(
forward(&mut ctx, "new EvalError('hello').toString()"),
forward(&mut context, "new EvalError('hello').toString()"),
"\"EvalError: hello\""
);
assert_eq!(
forward(&mut ctx, "new EvalError().toString()"),
forward(&mut context, "new EvalError().toString()"),
"\"EvalError\""
);
}
#[test]
fn uri_error_name() {
let mut ctx = Context::new();
assert_eq!(forward(&mut ctx, "URIError.name"), "\"URIError\"");
let mut context = Context::new();
assert_eq!(forward(&mut context, "URIError.name"), "\"URIError\"");
}
#[test]
fn uri_error_length() {
let mut ctx = Context::new();
assert_eq!(forward(&mut ctx, "URIError.length"), "1");
let mut context = Context::new();
assert_eq!(forward(&mut context, "URIError.length"), "1");
}
#[test]
fn uri_error_to_string() {
let mut ctx = Context::new();
let mut context = Context::new();
assert_eq!(
forward(&mut ctx, "new URIError('hello').toString()"),
forward(&mut context, "new URIError('hello').toString()"),
"\"URIError: hello\""
);
assert_eq!(
forward(&mut ctx, "new URIError().toString()"),
forward(&mut context, "new URIError().toString()"),
"\"URIError\""
);
}

8
boa/src/builtins/error/type.rs

@ -59,9 +59,13 @@ impl TypeError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
this.set_field("message", message.to_string(context)?);
}
// This value is used by console.log and other routines to match Object type

8
boa/src/builtins/error/uri.rs

@ -55,9 +55,13 @@ impl UriError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
this.set_field("message", message.to_string(context)?);
}
// This value is used by console.log and other routines to match Object type

12
boa/src/builtins/function/mod.rs

@ -14,26 +14,26 @@
use crate::{
builtins::{Array, BuiltIn},
environment::lexical_environment::Environment,
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, FunctionBuilder, GcObject, Object, ObjectData},
property::{Attribute, DataDescriptor},
syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, Result, Value,
};
use bitflags::bitflags;
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt::{self, Debug};
#[cfg(test)]
mod tests;
/// _fn(this, arguments, ctx) -> ResultValue_ - The signature of a built-in function
/// _fn(this, arguments, context) -> ResultValue_ - The signature of a built-in function
pub type NativeFunction = fn(&Value, &[Value], &mut Context) -> Result<Value>;
#[derive(Clone, Copy, Finalize)]
pub struct BuiltInFunction(pub(crate) NativeFunction);
unsafe impl Trace for BuiltInFunction {
unsafe_empty_trace!();
empty_trace!();
}
impl From<NativeFunction> for BuiltInFunction {
@ -88,7 +88,7 @@ impl FunctionFlags {
}
unsafe impl Trace for FunctionFlags {
unsafe_empty_trace!();
empty_trace!();
}
/// Boa representation of a Function Object.
@ -114,11 +114,11 @@ impl Function {
param: &FormalParameter,
index: usize,
args_list: &[Value],
interpreter: &mut Context,
context: &mut Context,
local_env: &Environment,
) {
// Create array of values
let array = Array::new_array(interpreter).unwrap();
let array = Array::new_array(context).unwrap();
Array::add_to_array_object(&array, &args_list[index..]).unwrap();
// Create binding

78
boa/src/builtins/function/tests.rs

@ -3,7 +3,7 @@ use crate::{forward, forward_val, Context};
#[allow(clippy::float_cmp)]
#[test]
fn arguments_object() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
function jason(a, b) {
@ -12,13 +12,13 @@ fn arguments_object() {
var val = jason(100, 6);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let return_val = forward_val(&mut engine, "val").expect("value expected");
let return_val = forward_val(&mut context, "val").expect("value expected");
assert_eq!(return_val.is_integer(), true);
assert_eq!(
return_val
.to_i32(&mut engine)
.to_i32(&mut context)
.expect("Could not convert value to i32"),
100
);
@ -26,18 +26,18 @@ fn arguments_object() {
#[test]
fn self_mutating_function_when_calling() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
function x() {
x.y = 3;
}
x();
"#;
eprintln!("{}", forward(&mut engine, func));
let y = forward_val(&mut engine, "x.y").expect("value expected");
eprintln!("{}", forward(&mut context, func));
let y = forward_val(&mut context, "x.y").expect("value expected");
assert_eq!(y.is_integer(), true);
assert_eq!(
y.to_i32(&mut engine)
y.to_i32(&mut context)
.expect("Could not convert value to i32"),
3
);
@ -45,18 +45,18 @@ fn self_mutating_function_when_calling() {
#[test]
fn self_mutating_function_when_constructing() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
function x() {
x.y = 3;
}
new x();
"#;
eprintln!("{}", forward(&mut engine, func));
let y = forward_val(&mut engine, "x.y").expect("value expected");
eprintln!("{}", forward(&mut context, func));
let y = forward_val(&mut context, "x.y").expect("value expected");
assert_eq!(y.is_integer(), true);
assert_eq!(
y.to_i32(&mut engine)
y.to_i32(&mut context)
.expect("Could not convert value to i32"),
3
);
@ -64,41 +64,41 @@ fn self_mutating_function_when_constructing() {
#[test]
fn call_function_prototype() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
Function.prototype()
"#;
let value = forward_val(&mut engine, func).unwrap();
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_undefined());
}
#[test]
fn call_function_prototype_with_arguments() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
Function.prototype(1, "", new String(""))
"#;
let value = forward_val(&mut engine, func).unwrap();
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_undefined());
}
#[test]
fn call_function_prototype_with_new() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
new Function.prototype()
"#;
let value = forward_val(&mut engine, func);
let value = forward_val(&mut context, func);
assert!(value.is_err());
}
#[test]
fn function_prototype_name() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
Function.prototype.name
"#;
let value = forward_val(&mut engine, func).unwrap();
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_string());
assert!(value.as_string().unwrap().is_empty());
}
@ -106,43 +106,43 @@ fn function_prototype_name() {
#[test]
#[allow(clippy::float_cmp)]
fn function_prototype_length() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
Function.prototype.length
"#;
let value = forward_val(&mut engine, func).unwrap();
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_number());
assert_eq!(value.as_number().unwrap(), 0.0);
}
#[test]
fn function_prototype_call() {
let mut engine = Context::new();
let mut context = Context::new();
let func = r#"
let e = new Error()
Object.prototype.toString.call(e)
"#;
let value = forward_val(&mut engine, func).unwrap();
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_string());
assert_eq!(value.as_string().unwrap(), "[object Error]");
}
#[test]
fn function_prototype_call_throw() {
let mut engine = Context::new();
let mut context = Context::new();
let throw = r#"
let call = Function.prototype.call;
call(call)
"#;
let value = forward_val(&mut engine, throw).unwrap_err();
let value = forward_val(&mut context, throw).unwrap_err();
assert!(value.is_object());
let string = value.to_string(&mut engine).unwrap();
let string = value.to_string(&mut context).unwrap();
assert!(string.starts_with("TypeError"))
}
#[test]
fn function_prototype_call_multiple_args() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
function f(a, b) {
this.a = a;
@ -151,13 +151,13 @@ fn function_prototype_call_multiple_args() {
let o = {a: 0, b: 0};
f.call(o, 1, 2);
"#;
forward_val(&mut engine, init).unwrap();
let boolean = forward_val(&mut engine, "o.a == 1")
forward_val(&mut context, init).unwrap();
let boolean = forward_val(&mut context, "o.a == 1")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut engine, "o.b == 2")
let boolean = forward_val(&mut context, "o.b == 2")
.unwrap()
.as_boolean()
.unwrap();
@ -166,21 +166,21 @@ fn function_prototype_call_multiple_args() {
#[test]
fn function_prototype_apply() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const numbers = [6, 7, 3, 4, 2];
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
"#;
forward_val(&mut engine, init).unwrap();
forward_val(&mut context, init).unwrap();
let boolean = forward_val(&mut engine, "max == 7")
let boolean = forward_val(&mut context, "max == 7")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut engine, "min == 2")
let boolean = forward_val(&mut context, "min == 2")
.unwrap()
.as_boolean()
.unwrap();
@ -189,7 +189,7 @@ fn function_prototype_apply() {
#[test]
fn function_prototype_apply_on_object() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
function f(a, b) {
this.a = a;
@ -198,15 +198,15 @@ fn function_prototype_apply_on_object() {
let o = {a: 0, b: 0};
f.apply(o, [1, 2]);
"#;
forward_val(&mut engine, init).unwrap();
forward_val(&mut context, init).unwrap();
let boolean = forward_val(&mut engine, "o.a == 1")
let boolean = forward_val(&mut context, "o.a == 1")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut engine, "o.b == 2")
let boolean = forward_val(&mut context, "o.b == 2")
.unwrap()
.as_boolean()
.unwrap();

2
boa/src/builtins/infinity/mod.rs

@ -25,7 +25,7 @@ impl BuiltIn for Infinity {
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
}
fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
fn init(_: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, f64::INFINITY.into(), Self::attribute())

39
boa/src/builtins/iterable/mod.rs

@ -16,32 +16,35 @@ pub struct IteratorPrototypes {
}
impl IteratorPrototypes {
pub fn init(ctx: &mut Context) -> Self {
let iterator_prototype = create_iterator_prototype(ctx);
pub(crate) fn init(context: &mut Context) -> Self {
let iterator_prototype = create_iterator_prototype(context);
Self {
iterator_prototype: iterator_prototype
.as_object()
.expect("Iterator prototype is not an object"),
array_iterator: ArrayIterator::create_prototype(ctx, iterator_prototype.clone())
array_iterator: ArrayIterator::create_prototype(context, iterator_prototype.clone())
.as_object()
.expect("Array Iterator Prototype is not an object"),
string_iterator: StringIterator::create_prototype(ctx, iterator_prototype.clone())
string_iterator: StringIterator::create_prototype(context, iterator_prototype.clone())
.as_object()
.expect("String Iterator Prototype is not an object"),
map_iterator: MapIterator::create_prototype(ctx, iterator_prototype)
map_iterator: MapIterator::create_prototype(context, iterator_prototype)
.as_object()
.expect("Map Iterator Prototype is not an object"),
}
}
#[inline]
pub fn array_iterator(&self) -> GcObject {
self.array_iterator.clone()
}
#[inline]
pub fn iterator_prototype(&self) -> GcObject {
self.iterator_prototype.clone()
}
#[inline]
pub fn string_iterator(&self) -> GcObject {
self.string_iterator.clone()
}
@ -54,8 +57,8 @@ impl IteratorPrototypes {
/// CreateIterResultObject( value, done )
///
/// Generates an object supporting the IteratorResult interface.
pub fn create_iter_result_object(ctx: &mut Context, value: Value, done: bool) -> Value {
let object = Value::new_object(Some(ctx.global_object()));
pub fn create_iter_result_object(context: &mut Context, value: Value, done: bool) -> Value {
let object = Value::new_object(Some(context.global_object()));
// TODO: Fix attributes of value and done
let value_property = DataDescriptor::new(value, Attribute::all());
let done_property = DataDescriptor::new(done, Attribute::all());
@ -65,17 +68,17 @@ pub fn create_iter_result_object(ctx: &mut Context, value: Value, done: bool) ->
}
/// Get an iterator record
pub fn get_iterator(ctx: &mut Context, iterable: Value) -> Result<IteratorRecord> {
pub fn get_iterator(context: &mut Context, iterable: Value) -> Result<IteratorRecord> {
// TODO: Fix the accessor handling
let iterator_function = iterable
.get_property(ctx.well_known_symbols().iterator_symbol())
.get_property(context.well_known_symbols().iterator_symbol())
.map(|p| p.as_data_descriptor().unwrap().value())
.ok_or_else(|| ctx.construct_type_error("Not an iterable"))?;
let iterator_object = ctx.call(&iterator_function, &iterable, &[])?;
.ok_or_else(|| context.construct_type_error("Not an iterable"))?;
let iterator_object = context.call(&iterator_function, &iterable, &[])?;
let next_function = iterator_object
.get_property("next")
.map(|p| p.as_data_descriptor().unwrap().value())
.ok_or_else(|| ctx.construct_type_error("Could not find property `next`"))?;
.ok_or_else(|| context.construct_type_error("Could not find property `next`"))?;
Ok(IteratorRecord::new(iterator_object, next_function))
}
@ -85,11 +88,11 @@ pub fn get_iterator(ctx: &mut Context, iterable: Value) -> Result<IteratorRecord
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%iteratorprototype%-object
fn create_iterator_prototype(ctx: &mut Context) -> Value {
fn create_iterator_prototype(context: &mut Context) -> Value {
let _timer = BoaProfiler::global().start_event("Iterator Prototype", "init");
let symbol_iterator = ctx.well_known_symbols().iterator_symbol();
let iterator_prototype = ObjectInitializer::new(ctx)
let symbol_iterator = context.well_known_symbols().iterator_symbol();
let iterator_prototype = ObjectInitializer::new(context)
.function(
|v, _, _| Ok(v.clone()),
(symbol_iterator, "[Symbol.iterator]"),
@ -120,14 +123,14 @@ impl IteratorRecord {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-iteratornext
pub(crate) fn next(&self, ctx: &mut Context) -> Result<IteratorResult> {
let next = ctx.call(&self.next_function, &self.iterator_object, &[])?;
pub(crate) fn next(&self, context: &mut Context) -> Result<IteratorResult> {
let next = context.call(&self.next_function, &self.iterator_object, &[])?;
// FIXME: handle accessor descriptors
let done = next
.get_property("done")
.map(|p| p.as_data_descriptor().unwrap().value())
.and_then(|v| v.as_boolean())
.ok_or_else(|| ctx.construct_type_error("Could not find property `done`"))?;
.ok_or_else(|| context.construct_type_error("Could not find property `done`"))?;
// FIXME: handle accessor descriptors
let next_result = next

32
boa/src/builtins/json/mod.rs

@ -60,26 +60,26 @@ impl Json {
///
/// [spec]: https://tc39.es/ecma262/#sec-json.parse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
pub(crate) fn parse(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn parse(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let arg = args
.get(0)
.cloned()
.unwrap_or_else(Value::undefined)
.to_string(ctx)?;
.to_string(context)?;
match serde_json::from_str::<JSONValue>(&arg) {
Ok(json) => {
let j = Value::from_json(json, ctx);
let j = Value::from_json(json, context);
match args.get(1) {
Some(reviver) if reviver.is_function() => {
let mut holder = Value::new_object(None);
holder.set_field("", j);
Self::walk(reviver, ctx, &mut holder, &PropertyKey::from(""))
Self::walk(reviver, context, &mut holder, &PropertyKey::from(""))
}
_ => Ok(j),
}
}
Err(err) => ctx.throw_syntax_error(err.to_string()),
Err(err) => context.throw_syntax_error(err.to_string()),
}
}
@ -91,7 +91,7 @@ impl Json {
/// [polyfill]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
fn walk(
reviver: &Value,
ctx: &mut Context,
context: &mut Context,
holder: &mut Value,
key: &PropertyKey,
) -> Result<Value> {
@ -101,7 +101,7 @@ impl Json {
let keys: Vec<_> = object.borrow().keys().collect();
for key in keys {
let v = Self::walk(reviver, ctx, &mut value.clone(), &key);
let v = Self::walk(reviver, context, &mut value.clone(), &key);
match v {
Ok(v) if !v.is_undefined() => {
value.set_field(key, v);
@ -113,7 +113,7 @@ impl Json {
}
}
}
ctx.call(reviver, holder, &[key.into(), value])
context.call(reviver, holder, &[key.into(), value])
}
/// `JSON.stringify( value[, replacer[, space]] )`
@ -132,7 +132,7 @@ impl Json {
///
/// [spec]: https://tc39.es/ecma262/#sec-json.stringify
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
pub(crate) fn stringify(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn stringify(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let object = match args.get(0) {
Some(obj) if obj.is_symbol() || obj.is_function() || obj.is_undefined() => {
return Ok(Value::undefined())
@ -142,7 +142,7 @@ impl Json {
};
let replacer = match args.get(1) {
Some(replacer) if replacer.is_object() => replacer,
_ => return Ok(Value::from(object.to_json(ctx)?.to_string())),
_ => return Ok(Value::from(object.to_json(context)?.to_string())),
};
let replacer_as_object = replacer
@ -163,7 +163,7 @@ impl Json {
object_to_return.set_property(
key.to_owned(),
DataDescriptor::new(
ctx.call(
context.call(
replacer,
&this_arg,
&[Value::from(key.clone()), val.clone()],
@ -172,7 +172,7 @@ impl Json {
),
);
}
Ok(Value::from(object_to_return.to_json(ctx)?.to_string()))
Ok(Value::from(object_to_return.to_json(context)?.to_string()))
})
.ok_or_else(Value::undefined)?
} else if replacer_as_object.is_array() {
@ -187,17 +187,17 @@ impl Json {
});
for field in fields {
if let Some(value) = object
.get_property(field.to_string(ctx)?)
.get_property(field.to_string(context)?)
// FIXME: handle accessor descriptors
.map(|prop| prop.as_data_descriptor().unwrap().value().to_json(ctx))
.map(|prop| prop.as_data_descriptor().unwrap().value().to_json(context))
.transpose()?
{
obj_to_return.insert(field.to_string(ctx)?.to_string(), value);
obj_to_return.insert(field.to_string(context)?.to_string(), value);
}
}
Ok(Value::from(JSONValue::Object(obj_to_return).to_string()))
} else {
Ok(Value::from(object.to_json(ctx)?.to_string()))
Ok(Value::from(object.to_json(context)?.to_string()))
}
}
}

142
boa/src/builtins/json/tests.rs

@ -2,14 +2,14 @@ use crate::{forward, forward_val, object::PROTOTYPE, value::same_value, Context}
#[test]
fn json_sanity() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(
forward(&mut engine, r#"JSON.parse('{"aaa":"bbb"}').aaa == 'bbb'"#),
forward(&mut context, r#"JSON.parse('{"aaa":"bbb"}').aaa == 'bbb'"#),
"true"
);
assert_eq!(
forward(
&mut engine,
&mut context,
r#"JSON.stringify({aaa: 'bbb'}) == '{"aaa":"bbb"}'"#
),
"true"
@ -18,10 +18,10 @@ fn json_sanity() {
#[test]
fn json_stringify_remove_undefined_values_from_objects() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(
&mut engine,
&mut context,
r#"JSON.stringify({ aaa: undefined, bbb: 'ccc' })"#,
);
let expected = r#""{"bbb":"ccc"}""#;
@ -31,10 +31,10 @@ fn json_stringify_remove_undefined_values_from_objects() {
#[test]
fn json_stringify_remove_function_values_from_objects() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(
&mut engine,
&mut context,
r#"JSON.stringify({ aaa: () => {}, bbb: 'ccc' })"#,
);
let expected = r#""{"bbb":"ccc"}""#;
@ -44,10 +44,10 @@ fn json_stringify_remove_function_values_from_objects() {
#[test]
fn json_stringify_remove_symbols_from_objects() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(
&mut engine,
&mut context,
r#"JSON.stringify({ aaa: Symbol(), bbb: 'ccc' })"#,
);
let expected = r#""{"bbb":"ccc"}""#;
@ -57,31 +57,31 @@ fn json_stringify_remove_symbols_from_objects() {
#[test]
fn json_stringify_replacer_array_strings() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(
&mut engine,
&mut context,
r#"JSON.stringify({aaa: 'bbb', bbb: 'ccc', ccc: 'ddd'}, ['aaa', 'bbb'])"#,
);
let expected = forward(&mut engine, r#"'{"aaa":"bbb","bbb":"ccc"}'"#);
let expected = forward(&mut context, r#"'{"aaa":"bbb","bbb":"ccc"}'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_replacer_array_numbers() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(
&mut engine,
&mut context,
r#"JSON.stringify({ 0: 'aaa', 1: 'bbb', 2: 'ccc'}, [1, 2])"#,
);
let expected = forward(&mut engine, r#"'{"1":"bbb","2":"ccc"}'"#);
let expected = forward(&mut context, r#"'{"1":"bbb","2":"ccc"}'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_replacer_function() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(
&mut engine,
&mut context,
r#"JSON.stringify({ aaa: 1, bbb: 2}, (key, value) => {
if (key === 'aaa') {
return undefined;
@ -90,60 +90,60 @@ fn json_stringify_replacer_function() {
return value;
})"#,
);
let expected = forward(&mut engine, r#"'{"bbb":2}'"#);
let expected = forward(&mut context, r#"'{"bbb":2}'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_arrays() {
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify(['a', 'b'])"#);
let expected = forward(&mut engine, r#"'["a","b"]'"#);
let mut context = Context::new();
let actual = forward(&mut context, r#"JSON.stringify(['a', 'b'])"#);
let expected = forward(&mut context, r#"'["a","b"]'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_object_array() {
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify([{a: 'b'}, {b: 'c'}])"#);
let expected = forward(&mut engine, r#"'[{"a":"b"},{"b":"c"}]'"#);
let mut context = Context::new();
let actual = forward(&mut context, r#"JSON.stringify([{a: 'b'}, {b: 'c'}])"#);
let expected = forward(&mut context, r#"'[{"a":"b"},{"b":"c"}]'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_array_converts_undefined_to_null() {
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify([undefined])"#);
let expected = forward(&mut engine, r#"'[null]'"#);
let mut context = Context::new();
let actual = forward(&mut context, r#"JSON.stringify([undefined])"#);
let expected = forward(&mut context, r#"'[null]'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_array_converts_function_to_null() {
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify([() => {}])"#);
let expected = forward(&mut engine, r#"'[null]'"#);
let mut context = Context::new();
let actual = forward(&mut context, r#"JSON.stringify([() => {}])"#);
let expected = forward(&mut context, r#"'[null]'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_array_converts_symbol_to_null() {
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify([Symbol()])"#);
let expected = forward(&mut engine, r#"'[null]'"#);
let mut context = Context::new();
let actual = forward(&mut context, r#"JSON.stringify([Symbol()])"#);
let expected = forward(&mut context, r#"'[null]'"#);
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_function_replacer_propogate_error() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(
&mut engine,
&mut context,
r#"
let thrown = 0;
try {
@ -154,64 +154,64 @@ fn json_stringify_function_replacer_propogate_error() {
thrown
"#,
);
let expected = forward(&mut engine, "1");
let expected = forward(&mut context, "1");
assert_eq!(actual, expected);
}
#[test]
fn json_stringify_function() {
let mut engine = Context::new();
let mut context = Context::new();
let actual_function = forward(&mut engine, r#"JSON.stringify(() => {})"#);
let expected = forward(&mut engine, r#"undefined"#);
let actual_function = forward(&mut context, r#"JSON.stringify(() => {})"#);
let expected = forward(&mut context, r#"undefined"#);
assert_eq!(actual_function, expected);
}
#[test]
fn json_stringify_undefined() {
let mut engine = Context::new();
let actual_undefined = forward(&mut engine, r#"JSON.stringify(undefined)"#);
let expected = forward(&mut engine, r#"undefined"#);
let mut context = Context::new();
let actual_undefined = forward(&mut context, r#"JSON.stringify(undefined)"#);
let expected = forward(&mut context, r#"undefined"#);
assert_eq!(actual_undefined, expected);
}
#[test]
fn json_stringify_symbol() {
let mut engine = Context::new();
let mut context = Context::new();
let actual_symbol = forward(&mut engine, r#"JSON.stringify(Symbol())"#);
let expected = forward(&mut engine, r#"undefined"#);
let actual_symbol = forward(&mut context, r#"JSON.stringify(Symbol())"#);
let expected = forward(&mut context, r#"undefined"#);
assert_eq!(actual_symbol, expected);
}
#[test]
fn json_stringify_no_args() {
let mut engine = Context::new();
let mut context = Context::new();
let actual_no_args = forward(&mut engine, r#"JSON.stringify()"#);
let expected = forward(&mut engine, r#"undefined"#);
let actual_no_args = forward(&mut context, r#"JSON.stringify()"#);
let expected = forward(&mut context, r#"undefined"#);
assert_eq!(actual_no_args, expected);
}
#[test]
fn json_stringify_fractional_numbers() {
let mut engine = Context::new();
let mut context = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify(Math.round(1.0))"#);
let expected = forward(&mut engine, r#""1""#);
let actual = forward(&mut context, r#"JSON.stringify(Math.round(1.0))"#);
let expected = forward(&mut context, r#""1""#);
assert_eq!(actual, expected);
}
#[test]
fn json_parse_array_with_reviver() {
let mut engine = Context::new();
let mut context = Context::new();
let result = forward_val(
&mut engine,
&mut context,
r#"JSON.parse('[1,2,3,4]', function(k, v){
if (typeof v == 'number') {
return v * 2;
@ -221,28 +221,28 @@ fn json_parse_array_with_reviver() {
)
.unwrap();
assert_eq!(
result.get_field("0").to_number(&mut engine).unwrap() as u8,
result.get_field("0").to_number(&mut context).unwrap() as u8,
2u8
);
assert_eq!(
result.get_field("1").to_number(&mut engine).unwrap() as u8,
result.get_field("1").to_number(&mut context).unwrap() as u8,
4u8
);
assert_eq!(
result.get_field("2").to_number(&mut engine).unwrap() as u8,
result.get_field("2").to_number(&mut context).unwrap() as u8,
6u8
);
assert_eq!(
result.get_field("3").to_number(&mut engine).unwrap() as u8,
result.get_field("3").to_number(&mut context).unwrap() as u8,
8u8
);
}
#[test]
fn json_parse_object_with_reviver() {
let mut engine = Context::new();
let mut context = Context::new();
let result = forward(
&mut engine,
&mut context,
r#"
var myObj = new Object();
myObj.firstname = "boa";
@ -266,7 +266,7 @@ fn json_parse_object_with_reviver() {
#[test]
fn json_parse_sets_prototypes() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const jsonString = "{
\"ob\":{\"ject\":1},
@ -274,22 +274,22 @@ fn json_parse_sets_prototypes() {
}";
const jsonObj = JSON.parse(jsonString);
"#;
eprintln!("{}", forward(&mut engine, init));
let object_prototype = forward_val(&mut engine, r#"jsonObj.ob"#)
eprintln!("{}", forward(&mut context, init));
let object_prototype = forward_val(&mut context, r#"jsonObj.ob"#)
.unwrap()
.as_object()
.unwrap()
.prototype_instance();
let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#)
let array_prototype = forward_val(&mut context, r#"jsonObj.arr"#)
.unwrap()
.as_object()
.unwrap()
.prototype_instance();
let global_object_prototype = engine
let global_object_prototype = context
.global_object()
.get_field("Object")
.get_field(PROTOTYPE);
let global_array_prototype = engine
let global_array_prototype = context
.global_object()
.get_field("Array")
.get_field(PROTOTYPE);
@ -302,22 +302,22 @@ fn json_parse_sets_prototypes() {
#[test]
fn json_fields_should_be_enumerable() {
let mut engine = Context::new();
let mut context = Context::new();
let actual_object = forward(
&mut engine,
&mut context,
r#"
var a = JSON.parse('{"x":0}');
a.propertyIsEnumerable('x');
"#,
);
let actual_array_index = forward(
&mut engine,
&mut context,
r#"
var b = JSON.parse('[0, 1]');
b.propertyIsEnumerable('0');
"#,
);
let expected = forward(&mut engine, r#"true"#);
let expected = forward(&mut context, r#"true"#);
assert_eq!(actual_object, expected);
assert_eq!(actual_array_index, expected);
@ -325,7 +325,7 @@ fn json_fields_should_be_enumerable() {
#[test]
fn json_parse_with_no_args_throws_syntax_error() {
let mut engine = Context::new();
let result = forward(&mut engine, "JSON.parse();");
let mut context = Context::new();
let result = forward(&mut context, "JSON.parse();");
assert!(result.contains("SyntaxError"));
}

38
boa/src/builtins/map/map_iterator.rs

@ -46,16 +46,16 @@ impl MapIterator {
///
/// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-createmapiterator
pub(crate) fn create_map_iterator(
ctx: &Context,
context: &Context,
map: Value,
kind: MapIterationKind,
) -> Result<Value> {
let map_iterator = Value::new_object(Some(ctx.global_object()));
let map_iterator = Value::new_object(Some(context.global_object()));
map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind)));
map_iterator
.as_object()
.expect("map iterator object")
.set_prototype_instance(ctx.iterator_prototypes().map_iterator().into());
.set_prototype_instance(context.iterator_prototypes().map_iterator().into());
Ok(map_iterator)
}
@ -67,7 +67,7 @@ impl MapIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next
pub(crate) fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn next(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
if let Value::Object(ref object) = this {
let mut object = object.borrow_mut();
if let Some(map_iterator) = object.as_map_iterator_mut() {
@ -76,7 +76,7 @@ impl MapIterator {
let item_kind = &map_iterator.map_iteration_kind;
if map_iterator.iterated_map.is_undefined() {
return Ok(create_iter_result_object(ctx, Value::undefined(), true));
return Ok(create_iter_result_object(context, Value::undefined(), true));
}
if let Value::Object(ref object) = m {
@ -90,42 +90,44 @@ impl MapIterator {
match item_kind {
MapIterationKind::Key => {
return Ok(create_iter_result_object(
ctx,
context,
key.clone(),
false,
));
}
MapIterationKind::Value => {
return Ok(create_iter_result_object(
ctx,
context,
value.clone(),
false,
));
}
MapIterationKind::KeyAndValue => {
let result = Array::construct_array(
&Array::new_array(ctx)?,
&Array::new_array(context)?,
&[key.clone(), value.clone()],
)?;
return Ok(create_iter_result_object(ctx, result, false));
return Ok(create_iter_result_object(
context, result, false,
));
}
}
}
}
} else {
return Err(ctx.construct_type_error("'this' is not a Map"));
return Err(context.construct_type_error("'this' is not a Map"));
}
} else {
return Err(ctx.construct_type_error("'this' is not a Map"));
return Err(context.construct_type_error("'this' is not a Map"));
}
map_iterator.iterated_map = Value::undefined();
Ok(create_iter_result_object(ctx, Value::undefined(), true))
Ok(create_iter_result_object(context, Value::undefined(), true))
} else {
ctx.throw_type_error("`this` is not an MapIterator")
context.throw_type_error("`this` is not an MapIterator")
}
} else {
ctx.throw_type_error("`this` is not an MapIterator")
context.throw_type_error("`this` is not an MapIterator")
}
}
@ -135,19 +137,19 @@ impl MapIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%-object
pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value {
let global = ctx.global_object();
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: Value) -> Value {
let global = context.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let map_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &map_iterator, 0, ctx);
make_builtin_fn(Self::next, "next", &map_iterator, 0, context);
map_iterator
.as_object()
.expect("map iterator prototype object")
.set_prototype_instance(iterator_prototype);
let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol();
let to_string_tag = context.well_known_symbols().to_string_tag_symbol();
let to_string_tag_property = DataDescriptor::new("Map Iterator", Attribute::CONFIGURABLE);
map_iterator.set_property(to_string_tag, to_string_tag_property);
map_iterator

62
boa/src/builtins/map/mod.rs

@ -69,9 +69,16 @@ impl Map {
pub(crate) const LENGTH: usize = 1;
/// Create a new map
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
// Set Prototype
let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE);
let prototype = context
.global_object()
.get_field("Map")
.get_field(PROTOTYPE);
this.as_object()
.expect("this is map object")
@ -89,11 +96,11 @@ impl Map {
map
} else if object.is_array() {
let mut map = OrderedMap::new();
let len = args[0].get_field("length").to_integer(ctx)? as i32;
let len = args[0].get_field("length").to_integer(context)? as i32;
for i in 0..len {
let val = &args[0].get_field(i.to_string());
let (key, value) = Self::get_key_value(val).ok_or_else(|| {
ctx.construct_type_error(
context.construct_type_error(
"iterable for Map should have array-like objects",
)
})?;
@ -101,15 +108,14 @@ impl Map {
}
map
} else {
return Err(ctx.construct_type_error(
return Err(context.construct_type_error(
"iterable for Map should have array-like objects",
));
}
}
_ => {
return Err(
ctx.construct_type_error("iterable for Map should have array-like objects")
)
return Err(context
.construct_type_error("iterable for Map should have array-like objects"))
}
},
};
@ -132,8 +138,8 @@ impl Map {
///
/// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-map.prototype.entries
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
pub(crate) fn entries(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
MapIterator::create_map_iterator(ctx, this.clone(), MapIterationKind::KeyAndValue)
pub(crate) fn entries(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
MapIterator::create_map_iterator(context, this.clone(), MapIterationKind::KeyAndValue)
}
/// `Map.prototype.keys()`
@ -146,8 +152,8 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.keys
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
pub(crate) fn keys(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
MapIterator::create_map_iterator(ctx, this.clone(), MapIterationKind::Key)
pub(crate) fn keys(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
MapIterator::create_map_iterator(context, this.clone(), MapIterationKind::Key)
}
/// Helper function to set the size property.
@ -170,7 +176,7 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.set
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
pub(crate) fn set(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn set(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let (key, value) = match args.len() {
0 => (Value::Undefined, Value::Undefined),
1 => (args[0].clone(), Value::Undefined),
@ -183,10 +189,10 @@ impl Map {
map.insert(key, value);
map.len()
} else {
return Err(ctx.construct_type_error("'this' is not a Map"));
return Err(context.construct_type_error("'this' is not a Map"));
}
} else {
return Err(ctx.construct_type_error("'this' is not a Map"));
return Err(context.construct_type_error("'this' is not a Map"));
};
Self::set_size(this, size);
@ -203,7 +209,7 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.delete
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
pub(crate) fn delete(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn delete(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let undefined = Value::Undefined;
let key = match args.len() {
0 => &undefined,
@ -216,10 +222,10 @@ impl Map {
let deleted = map.remove(key).is_some();
(deleted, map.len())
} else {
return Err(ctx.construct_type_error("'this' is not a Map"));
return Err(context.construct_type_error("'this' is not a Map"));
}
} else {
return Err(ctx.construct_type_error("'this' is not a Map"));
return Err(context.construct_type_error("'this' is not a Map"));
};
Self::set_size(this, size);
Ok(deleted.into())
@ -235,7 +241,7 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.get
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
pub(crate) fn get(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn get(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let undefined = Value::Undefined;
let key = match args.len() {
0 => &undefined,
@ -253,7 +259,7 @@ impl Map {
}
}
Err(ctx.construct_type_error("'this' is not a Map"))
Err(context.construct_type_error("'this' is not a Map"))
}
/// `Map.prototype.clear( )`
@ -284,7 +290,7 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
pub(crate) fn has(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn has(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let undefined = Value::Undefined;
let key = match args.len() {
0 => &undefined,
@ -298,7 +304,7 @@ impl Map {
}
}
Err(ctx.construct_type_error("'this' is not a Map"))
Err(context.construct_type_error("'this' is not a Map"))
}
/// `Map.prototype.forEach( callbackFn [ , thisArg ] )`
@ -311,11 +317,7 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.foreach
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
pub(crate) fn for_each(
this: &Value,
args: &[Value],
interpreter: &mut Context,
) -> Result<Value> {
pub(crate) fn for_each(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from("Missing argument for Map.prototype.forEach"));
}
@ -329,7 +331,7 @@ impl Map {
for (key, value) in map {
let arguments = [value, key, this.clone()];
interpreter.call(callback_arg, &this_arg, &arguments)?;
context.call(callback_arg, &this_arg, &arguments)?;
}
}
}
@ -347,8 +349,8 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
pub(crate) fn values(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
MapIterator::create_map_iterator(ctx, this.clone(), MapIterationKind::Value)
pub(crate) fn values(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
MapIterator::create_map_iterator(context, this.clone(), MapIterationKind::Value)
}
/// Helper function to get a key-value pair from an array.

10
boa/src/builtins/map/ordered_map.rs

@ -1,8 +1,10 @@
use gc::{custom_trace, Finalize, Trace};
use crate::gc::{custom_trace, Finalize, Trace};
use indexmap::{map::IntoIter, map::Iter, map::IterMut, IndexMap};
use std::collections::hash_map::RandomState;
use std::fmt::Debug;
use std::hash::{BuildHasher, Hash};
use std::{
collections::hash_map::RandomState,
fmt::Debug,
hash::{BuildHasher, Hash},
};
/// A newtype wrapping indexmap::IndexMap
#[derive(Clone)]

202
boa/src/builtins/map/tests.rs

@ -2,50 +2,50 @@ use crate::{forward, Context};
#[test]
fn construct_empty() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var empty = new Map();
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "empty.size");
forward(&mut context, init);
let result = forward(&mut context, "empty.size");
assert_eq!(result, "0");
}
#[test]
fn construct_from_array() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.size");
forward(&mut context, init);
let result = forward(&mut context, "map.size");
assert_eq!(result, "2");
}
#[test]
fn clone() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let original = new Map([["1", "one"], ["2", "two"]]);
let clone = new Map(original);
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "clone.size");
forward(&mut context, init);
let result = forward(&mut context, "clone.size");
assert_eq!(result, "2");
let result = forward(
&mut engine,
&mut context,
r#"
original.set("3", "three");
original.size"#,
);
assert_eq!(result, "3");
let result = forward(&mut engine, "clone.size");
let result = forward(&mut context, "clone.size");
assert_eq!(result, "2");
}
#[test]
fn symbol_iterator() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const map1 = new Map();
map1.set('0', 'foo');
@ -55,33 +55,33 @@ fn symbol_iterator() {
let item2 = iterator.next();
let item3 = iterator.next();
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "item1.value.length");
forward(&mut context, init);
let result = forward(&mut context, "item1.value.length");
assert_eq!(result, "2");
let result = forward(&mut engine, "item1.value[0]");
let result = forward(&mut context, "item1.value[0]");
assert_eq!(result, "\"0\"");
let result = forward(&mut engine, "item1.value[1]");
let result = forward(&mut context, "item1.value[1]");
assert_eq!(result, "\"foo\"");
let result = forward(&mut engine, "item1.done");
let result = forward(&mut context, "item1.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item2.value.length");
let result = forward(&mut context, "item2.value.length");
assert_eq!(result, "2");
let result = forward(&mut engine, "item2.value[0]");
let result = forward(&mut context, "item2.value[0]");
assert_eq!(result, "1");
let result = forward(&mut engine, "item2.value[1]");
let result = forward(&mut context, "item2.value[1]");
assert_eq!(result, "\"bar\"");
let result = forward(&mut engine, "item2.done");
let result = forward(&mut context, "item2.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item3.value");
let result = forward(&mut context, "item3.value");
assert_eq!(result, "undefined");
let result = forward(&mut engine, "item3.done");
let result = forward(&mut context, "item3.done");
assert_eq!(result, "true");
}
// Should behave the same as symbol_iterator
#[test]
fn entries() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const map1 = new Map();
map1.set('0', 'foo');
@ -91,32 +91,32 @@ fn entries() {
let item2 = entriesIterator.next();
let item3 = entriesIterator.next();
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "item1.value.length");
forward(&mut context, init);
let result = forward(&mut context, "item1.value.length");
assert_eq!(result, "2");
let result = forward(&mut engine, "item1.value[0]");
let result = forward(&mut context, "item1.value[0]");
assert_eq!(result, "\"0\"");
let result = forward(&mut engine, "item1.value[1]");
let result = forward(&mut context, "item1.value[1]");
assert_eq!(result, "\"foo\"");
let result = forward(&mut engine, "item1.done");
let result = forward(&mut context, "item1.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item2.value.length");
let result = forward(&mut context, "item2.value.length");
assert_eq!(result, "2");
let result = forward(&mut engine, "item2.value[0]");
let result = forward(&mut context, "item2.value[0]");
assert_eq!(result, "1");
let result = forward(&mut engine, "item2.value[1]");
let result = forward(&mut context, "item2.value[1]");
assert_eq!(result, "\"bar\"");
let result = forward(&mut engine, "item2.done");
let result = forward(&mut context, "item2.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item3.value");
let result = forward(&mut context, "item3.value");
assert_eq!(result, "undefined");
let result = forward(&mut engine, "item3.done");
let result = forward(&mut context, "item3.done");
assert_eq!(result, "true");
}
#[test]
fn merge() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let first = new Map([["1", "one"], ["2", "two"]]);
let second = new Map([["2", "second two"], ["3", "three"]]);
@ -124,44 +124,44 @@ fn merge() {
let merged1 = new Map([...first, ...second]);
let merged2 = new Map([...second, ...third]);
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "merged1.size");
forward(&mut context, init);
let result = forward(&mut context, "merged1.size");
assert_eq!(result, "3");
let result = forward(&mut engine, "merged1.get('2')");
let result = forward(&mut context, "merged1.get('2')");
assert_eq!(result, "\"second two\"");
let result = forward(&mut engine, "merged2.size");
let result = forward(&mut context, "merged2.size");
assert_eq!(result, "4");
}
#[test]
fn get() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.get('1')");
forward(&mut context, init);
let result = forward(&mut context, "map.get('1')");
assert_eq!(result, "\"one\"");
let result = forward(&mut engine, "map.get('2')");
let result = forward(&mut context, "map.get('2')");
assert_eq!(result, "\"two\"");
let result = forward(&mut engine, "map.get('3')");
let result = forward(&mut context, "map.get('3')");
assert_eq!(result, "undefined");
let result = forward(&mut engine, "map.get()");
let result = forward(&mut context, "map.get()");
assert_eq!(result, "undefined");
}
#[test]
fn set() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map();
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.set()");
forward(&mut context, init);
let result = forward(&mut context, "map.set()");
assert_eq!(result, "Map { undefined → undefined }");
let result = forward(&mut engine, "map.set('1', 'one')");
let result = forward(&mut context, "map.set('1', 'one')");
assert_eq!(result, "Map { undefined → undefined, \"1\" → \"one\" }");
let result = forward(&mut engine, "map.set('2')");
let result = forward(&mut context, "map.set('2')");
assert_eq!(
result,
"Map { undefined → undefined, \"1\" → \"one\", \"2\" → undefined }"
@ -170,49 +170,49 @@ fn set() {
#[test]
fn clear() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
map.clear();
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.size");
forward(&mut context, init);
let result = forward(&mut context, "map.size");
assert_eq!(result, "0");
}
#[test]
fn delete() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.delete('1')");
forward(&mut context, init);
let result = forward(&mut context, "map.delete('1')");
assert_eq!(result, "true");
let result = forward(&mut engine, "map.size");
let result = forward(&mut context, "map.size");
assert_eq!(result, "1");
let result = forward(&mut engine, "map.delete('1')");
let result = forward(&mut context, "map.delete('1')");
assert_eq!(result, "false");
}
#[test]
fn has() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map([["1", "one"]]);
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.has()");
forward(&mut context, init);
let result = forward(&mut context, "map.has()");
assert_eq!(result, "false");
let result = forward(&mut engine, "map.has('1')");
let result = forward(&mut context, "map.has('1')");
assert_eq!(result, "true");
let result = forward(&mut engine, "map.has('2')");
let result = forward(&mut context, "map.has('2')");
assert_eq!(result, "false");
}
#[test]
fn keys() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const map1 = new Map();
map1.set('0', 'foo');
@ -222,24 +222,24 @@ fn keys() {
let item2 = keysIterator.next();
let item3 = keysIterator.next();
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "item1.value");
forward(&mut context, init);
let result = forward(&mut context, "item1.value");
assert_eq!(result, "\"0\"");
let result = forward(&mut engine, "item1.done");
let result = forward(&mut context, "item1.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item2.value");
let result = forward(&mut context, "item2.value");
assert_eq!(result, "1");
let result = forward(&mut engine, "item2.done");
let result = forward(&mut context, "item2.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item3.value");
let result = forward(&mut context, "item3.value");
assert_eq!(result, "undefined");
let result = forward(&mut engine, "item3.done");
let result = forward(&mut context, "item3.done");
assert_eq!(result, "true");
}
#[test]
fn for_each() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map([[1, 5], [2, 10], [3, 15]]);
let valueSum = 0;
@ -252,15 +252,15 @@ fn for_each() {
}
map.forEach(callingCallback);
"#;
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "valueSum"), "30");
assert_eq!(forward(&mut engine, "keySum"), "6");
assert_eq!(forward(&mut engine, "sizeSum"), "9");
forward(&mut context, init);
assert_eq!(forward(&mut context, "valueSum"), "30");
assert_eq!(forward(&mut context, "keySum"), "6");
assert_eq!(forward(&mut context, "sizeSum"), "9");
}
#[test]
fn values() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const map1 = new Map();
map1.set('0', 'foo');
@ -270,54 +270,54 @@ fn values() {
let item2 = valuesIterator.next();
let item3 = valuesIterator.next();
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "item1.value");
forward(&mut context, init);
let result = forward(&mut context, "item1.value");
assert_eq!(result, "\"foo\"");
let result = forward(&mut engine, "item1.done");
let result = forward(&mut context, "item1.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item2.value");
let result = forward(&mut context, "item2.value");
assert_eq!(result, "\"bar\"");
let result = forward(&mut engine, "item2.done");
let result = forward(&mut context, "item2.done");
assert_eq!(result, "false");
let result = forward(&mut engine, "item3.value");
let result = forward(&mut context, "item3.value");
assert_eq!(result, "undefined");
let result = forward(&mut engine, "item3.done");
let result = forward(&mut context, "item3.done");
assert_eq!(result, "true");
}
#[test]
fn modify_key() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let obj = new Object();
let map = new Map([[obj, "one"]]);
obj.field = "Value";
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.get(obj)");
forward(&mut context, init);
let result = forward(&mut context, "map.get(obj)");
assert_eq!(result, "\"one\"");
}
#[test]
fn order() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map([[1, "one"]]);
map.set(2, "two");
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map");
forward(&mut context, init);
let result = forward(&mut context, "map");
assert_eq!(result, "Map { 1 → \"one\", 2 → \"two\" }");
let result = forward(&mut engine, "map.set(1, \"five\");map");
let result = forward(&mut context, "map.set(1, \"five\");map");
assert_eq!(result, "Map { 1 → \"five\", 2 → \"two\" }");
let result = forward(&mut engine, "map.set();map");
let result = forward(&mut context, "map.set();map");
assert_eq!(
result,
"Map { 1 → \"five\", 2 → \"two\", undefined → undefined }"
);
let result = forward(&mut engine, "map.delete(2);map");
let result = forward(&mut context, "map.delete(2);map");
assert_eq!(result, "Map { 1 → \"five\", undefined → undefined }");
let result = forward(&mut engine, "map.set(2, \"two\");map");
let result = forward(&mut context, "map.set(2, \"two\");map");
assert_eq!(
result,
"Map { 1 → \"five\", undefined → undefined, 2 → \"two\" }"
@ -326,22 +326,22 @@ fn order() {
#[test]
fn recursive_display() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let map = new Map();
let array = new Array([map]);
map.set("y", map);
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map");
forward(&mut context, init);
let result = forward(&mut context, "map");
assert_eq!(result, "Map { \"y\" → Map(1) }");
let result = forward(&mut engine, "map.set(\"z\", array)");
let result = forward(&mut context, "map.set(\"z\", array)");
assert_eq!(result, "Map { \"y\" → Map(2), \"z\" → Array(1) }");
}
#[test]
fn not_a_function() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r"
try {
let map = Map()
@ -350,7 +350,7 @@ fn not_a_function() {
}
";
assert_eq!(
forward(&mut engine, init),
forward(&mut context, init),
"\"TypeError: function object is not callable\""
);
}

142
boa/src/builtins/math/mod.rs

@ -94,10 +94,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.abs
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
pub(crate) fn abs(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn abs(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::abs)
.into())
@ -111,10 +111,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.acos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
pub(crate) fn acos(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn acos(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::acos)
.into())
@ -128,10 +128,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.acosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
pub(crate) fn acosh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn acosh(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::acosh)
.into())
@ -145,10 +145,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.asin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
pub(crate) fn asin(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn asin(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::asin)
.into())
@ -162,10 +162,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.asinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
pub(crate) fn asinh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn asinh(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::asinh)
.into())
@ -179,10 +179,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.atan
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
pub(crate) fn atan(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn atan(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::atan)
.into())
@ -196,10 +196,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.atanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
pub(crate) fn atanh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn atanh(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::atanh)
.into())
@ -213,10 +213,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.atan2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
pub(crate) fn atan2(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn atan2(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(match (
args.get(0).map(|x| x.to_number(ctx)).transpose()?,
args.get(1).map(|x| x.to_number(ctx)).transpose()?,
args.get(0).map(|x| x.to_number(context)).transpose()?,
args.get(1).map(|x| x.to_number(context)).transpose()?,
) {
(Some(x), Some(y)) => x.atan2(y),
(_, _) => f64::NAN,
@ -232,10 +232,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
pub(crate) fn cbrt(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn cbrt(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::cbrt)
.into())
@ -249,10 +249,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.ceil
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
pub(crate) fn ceil(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn ceil(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::ceil)
.into())
@ -266,10 +266,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.clz32
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
pub(crate) fn clz32(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn clz32(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_u32(ctx))
.map(|x| x.to_u32(context))
.transpose()?
.map(u32::leading_zeros)
.unwrap_or(32)
@ -284,10 +284,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.cos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
pub(crate) fn cos(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn cos(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::cos)
.into())
@ -301,10 +301,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.cosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
pub(crate) fn cosh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn cosh(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::cosh)
.into())
@ -318,10 +318,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.exp
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
pub(crate) fn exp(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn exp(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::exp)
.into())
@ -337,10 +337,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.expm1
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1
pub(crate) fn expm1(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn expm1(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::exp_m1)
.into())
@ -354,10 +354,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.floor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
pub(crate) fn floor(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn floor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::floor)
.into())
@ -371,10 +371,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.fround
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround
pub(crate) fn fround(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn fround(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, |x| (x as f32) as f64)
.into())
@ -388,10 +388,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.hypot
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
pub(crate) fn hypot(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn hypot(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let mut result = 0f64;
for arg in args {
let x = arg.to_number(ctx)?;
let x = arg.to_number(context)?;
result = result.hypot(x);
}
Ok(result.into())
@ -405,10 +405,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.imul
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
pub(crate) fn imul(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn imul(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(match (
args.get(0).map(|x| x.to_u32(ctx)).transpose()?,
args.get(1).map(|x| x.to_u32(ctx)).transpose()?,
args.get(0).map(|x| x.to_u32(context)).transpose()?,
args.get(1).map(|x| x.to_u32(context)).transpose()?,
) {
(Some(x), Some(y)) => x.wrapping_mul(y) as i32,
(_, _) => 0,
@ -424,10 +424,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
pub(crate) fn log(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn log(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, |x| if x <= 0.0 { f64::NAN } else { x.ln() })
.into())
@ -441,10 +441,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log1p
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p
pub(crate) fn log1p(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn log1p(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::ln_1p)
.into())
@ -458,10 +458,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log10
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
pub(crate) fn log10(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn log10(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, |x| if x <= 0.0 { f64::NAN } else { x.log10() })
.into())
@ -475,10 +475,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
pub(crate) fn log2(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn log2(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, |x| if x <= 0.0 { f64::NAN } else { x.log2() })
.into())
@ -492,10 +492,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.max
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
pub(crate) fn max(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn max(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let mut max = f64::NEG_INFINITY;
for arg in args {
let num = arg.to_number(ctx)?;
let num = arg.to_number(context)?;
max = max.max(num);
}
Ok(max.into())
@ -509,10 +509,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.min
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
pub(crate) fn min(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn min(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let mut min = f64::INFINITY;
for arg in args {
let num = arg.to_number(ctx)?;
let num = arg.to_number(context)?;
min = min.min(num);
}
Ok(min.into())
@ -526,10 +526,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.pow
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
pub(crate) fn pow(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn pow(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(match (
args.get(0).map(|x| x.to_number(ctx)).transpose()?,
args.get(1).map(|x| x.to_number(ctx)).transpose()?,
args.get(0).map(|x| x.to_number(context)).transpose()?,
args.get(1).map(|x| x.to_number(context)).transpose()?,
) {
(Some(x), Some(y)) => x.powf(y),
(_, _) => f64::NAN,
@ -557,10 +557,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.round
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
pub(crate) fn round(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn round(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::round)
.into())
@ -574,10 +574,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sign
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
pub(crate) fn sign(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn sign(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(
f64::NAN,
@ -600,10 +600,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
pub(crate) fn sin(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn sin(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::sin)
.into())
@ -617,10 +617,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
pub(crate) fn sinh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn sinh(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::sinh)
.into())
@ -634,10 +634,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
pub(crate) fn sqrt(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn sqrt(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::sqrt)
.into())
@ -651,10 +651,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.tan
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan
pub(crate) fn tan(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn tan(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::tan)
.into())
@ -668,10 +668,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.tanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
pub(crate) fn tanh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn tanh(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::tanh)
.into())
@ -685,10 +685,10 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.trunc
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
pub(crate) fn trunc(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn trunc(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
.map(|x| x.to_number(context))
.transpose()?
.map_or(f64::NAN, f64::trunc)
.into())

546
boa/src/builtins/math/tests.rs

@ -5,24 +5,24 @@ use std::f64;
#[test]
fn abs() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.abs(3 - 5);
var b = Math.abs(1.23456 - 7.89012);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 2.0);
assert_eq!(b.to_number(&mut engine).unwrap(), 6.655_559_999_999_999_5);
assert_eq!(a.to_number(&mut context).unwrap(), 2.0);
assert_eq!(b.to_number(&mut context).unwrap(), 6.655_559_999_999_999_5);
}
#[test]
fn acos() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.acos(8 / 10);
var b = Math.acos(5 / 3);
@ -30,154 +30,154 @@ fn acos() {
var d = Math.acos(2);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward(&mut engine, "b");
let c = forward_val(&mut engine, "c").unwrap();
let d = forward(&mut engine, "d");
let a = forward_val(&mut context, "a").unwrap();
let b = forward(&mut context, "b");
let c = forward_val(&mut context, "c").unwrap();
let d = forward(&mut context, "d");
assert_eq!(a.to_number(&mut engine).unwrap(), 0.643_501_108_793_284_3);
assert_eq!(a.to_number(&mut context).unwrap(), 0.643_501_108_793_284_3);
assert_eq!(b, "NaN");
assert_eq!(c.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(c.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(d, "NaN");
}
#[test]
fn acosh() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.acosh(2);
var b = Math.acosh(-1);
var c = Math.acosh(0.5);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward(&mut engine, "b");
let c = forward(&mut engine, "c");
let a = forward_val(&mut context, "a").unwrap();
let b = forward(&mut context, "b");
let c = forward(&mut context, "c");
assert_eq!(a.to_number(&mut engine).unwrap(), 1.316_957_896_924_816_6);
assert_eq!(a.to_number(&mut context).unwrap(), 1.316_957_896_924_816_6);
assert_eq!(b, "NaN");
assert_eq!(c, "NaN");
}
#[test]
fn asin() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.asin(6 / 10);
var b = Math.asin(5 / 3);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward(&mut engine, "b");
let a = forward_val(&mut context, "a").unwrap();
let b = forward(&mut context, "b");
assert_eq!(a.to_number(&mut engine).unwrap(), 0.643_501_108_793_284_4);
assert_eq!(a.to_number(&mut context).unwrap(), 0.643_501_108_793_284_4);
assert_eq!(b, String::from("NaN"));
}
#[test]
fn asinh() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.asinh(1);
var b = Math.asinh(0);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 0.881_373_587_019_542_9);
assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 0.881_373_587_019_542_9);
assert_eq!(b.to_number(&mut context).unwrap(), 0_f64);
}
#[test]
fn atan() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.atan(1);
var b = Math.atan(0);
var c = Math.atan(-0);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), f64::consts::FRAC_PI_4);
assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), f64::from(-0));
assert_eq!(a.to_number(&mut context).unwrap(), f64::consts::FRAC_PI_4);
assert_eq!(b.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(c.to_number(&mut context).unwrap(), f64::from(-0));
}
#[test]
fn atan2() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.atan2(90, 15);
var b = Math.atan2(15, 90);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 1.405_647_649_380_269_9);
assert_eq!(b.to_number(&mut engine).unwrap(), 0.165_148_677_414_626_83);
assert_eq!(a.to_number(&mut context).unwrap(), 1.405_647_649_380_269_9);
assert_eq!(b.to_number(&mut context).unwrap(), 0.165_148_677_414_626_83);
}
#[test]
fn cbrt() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.cbrt(64);
var b = Math.cbrt(-1);
var c = Math.cbrt(1);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 4_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), -1_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), 1_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 4_f64);
assert_eq!(b.to_number(&mut context).unwrap(), -1_f64);
assert_eq!(c.to_number(&mut context).unwrap(), 1_f64);
}
#[test]
fn ceil() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.ceil(1.95);
var b = Math.ceil(4);
var c = Math.ceil(-7.004);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 2_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 4_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), -7_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 2_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 4_f64);
assert_eq!(c.to_number(&mut context).unwrap(), -7_f64);
}
#[test]
#[allow(clippy::many_single_char_names)]
fn clz32() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.clz32();
var b = Math.clz32({});
@ -189,88 +189,88 @@ fn clz32() {
var h = Math.clz32(0);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();
let g = forward_val(&mut engine, "g").unwrap();
let h = forward_val(&mut engine, "h").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
let d = forward_val(&mut context, "d").unwrap();
let e = forward_val(&mut context, "e").unwrap();
let f = forward_val(&mut context, "f").unwrap();
let g = forward_val(&mut context, "g").unwrap();
let h = forward_val(&mut context, "h").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 32_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 32_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(d.to_number(&mut engine).unwrap(), 31_f64);
assert_eq!(e.to_number(&mut engine).unwrap(), 1_f64);
assert_eq!(f.to_number(&mut engine).unwrap(), 32_f64);
assert_eq!(g.to_number(&mut engine).unwrap(), 31_f64);
assert_eq!(h.to_number(&mut engine).unwrap(), 32_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 32_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 32_f64);
assert_eq!(c.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(d.to_number(&mut context).unwrap(), 31_f64);
assert_eq!(e.to_number(&mut context).unwrap(), 1_f64);
assert_eq!(f.to_number(&mut context).unwrap(), 32_f64);
assert_eq!(g.to_number(&mut context).unwrap(), 31_f64);
assert_eq!(h.to_number(&mut context).unwrap(), 32_f64);
}
#[test]
fn cos() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.cos(0);
var b = Math.cos(1);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 0.540_302_305_868_139_8);
assert_eq!(a.to_number(&mut context).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 0.540_302_305_868_139_8);
}
#[test]
fn cosh() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.cosh(0);
var b = Math.cosh(1);
var c = Math.cosh(-1);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 1.543_080_634_815_243_7);
assert_eq!(c.to_number(&mut engine).unwrap(), 1.543_080_634_815_243_7);
assert_eq!(a.to_number(&mut context).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 1.543_080_634_815_243_7);
assert_eq!(c.to_number(&mut context).unwrap(), 1.543_080_634_815_243_7);
}
#[test]
fn exp() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.exp(0);
var b = Math.exp(-1);
var c = Math.exp(2);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 0.367_879_441_171_442_33);
assert_eq!(c.to_number(&mut engine).unwrap(), 7.389_056_098_930_65);
assert_eq!(a.to_number(&mut context).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 0.367_879_441_171_442_33);
assert_eq!(c.to_number(&mut context).unwrap(), 7.389_056_098_930_65);
}
#[test]
#[allow(clippy::many_single_char_names)]
fn expm1() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.expm1();
var b = Math.expm1({});
@ -280,63 +280,63 @@ fn expm1() {
var f = Math.expm1(2);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward(&mut engine, "a");
let b = forward(&mut engine, "b");
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();
let a = forward(&mut context, "a");
let b = forward(&mut context, "b");
let c = forward_val(&mut context, "c").unwrap();
let d = forward_val(&mut context, "d").unwrap();
let e = forward_val(&mut context, "e").unwrap();
let f = forward_val(&mut context, "f").unwrap();
assert_eq!(a, String::from("NaN"));
assert_eq!(b, String::from("NaN"));
assert!(float_cmp::approx_eq!(
f64,
c.to_number(&mut engine).unwrap(),
c.to_number(&mut context).unwrap(),
1.718_281_828_459_045
));
assert!(float_cmp::approx_eq!(
f64,
d.to_number(&mut engine).unwrap(),
d.to_number(&mut context).unwrap(),
-0.632_120_558_828_557_7
));
assert!(float_cmp::approx_eq!(
f64,
e.to_number(&mut engine).unwrap(),
e.to_number(&mut context).unwrap(),
0_f64
));
assert!(float_cmp::approx_eq!(
f64,
f.to_number(&mut engine).unwrap(),
f.to_number(&mut context).unwrap(),
6.389_056_098_930_65
));
}
#[test]
fn floor() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.floor(1.95);
var b = Math.floor(-3.01);
var c = Math.floor(3.01);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), -4_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), 3_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut context).unwrap(), -4_f64);
assert_eq!(c.to_number(&mut context).unwrap(), 3_f64);
}
#[test]
#[allow(clippy::many_single_char_names)]
fn fround() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.fround(NaN);
var b = Math.fround(Infinity);
@ -347,29 +347,29 @@ fn fround() {
var g = Math.fround();
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward(&mut engine, "a");
let b = forward(&mut engine, "b");
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();
let g = forward(&mut engine, "g");
let a = forward(&mut context, "a");
let b = forward(&mut context, "b");
let c = forward_val(&mut context, "c").unwrap();
let d = forward_val(&mut context, "d").unwrap();
let e = forward_val(&mut context, "e").unwrap();
let f = forward_val(&mut context, "f").unwrap();
let g = forward(&mut context, "g");
assert_eq!(a, String::from("NaN"));
assert_eq!(b, String::from("Infinity"));
assert_eq!(c.to_number(&mut engine).unwrap(), 5f64);
assert_eq!(d.to_number(&mut engine).unwrap(), 5.5f64);
assert_eq!(e.to_number(&mut engine).unwrap(), 5.050_000_190_734_863);
assert_eq!(f.to_number(&mut engine).unwrap(), -5.050_000_190_734_863);
assert_eq!(c.to_number(&mut context).unwrap(), 5f64);
assert_eq!(d.to_number(&mut context).unwrap(), 5.5f64);
assert_eq!(e.to_number(&mut context).unwrap(), 5.050_000_190_734_863);
assert_eq!(f.to_number(&mut context).unwrap(), -5.050_000_190_734_863);
assert_eq!(g, String::from("NaN"));
}
#[test]
#[allow(clippy::many_single_char_names)]
fn hypot() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.hypot();
var b = Math.hypot(3, 4);
@ -380,29 +380,29 @@ fn hypot() {
var g = Math.hypot(12);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();
let g = forward_val(&mut engine, "g").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
let d = forward_val(&mut context, "d").unwrap();
let e = forward_val(&mut context, "e").unwrap();
let f = forward_val(&mut context, "f").unwrap();
let g = forward_val(&mut context, "g").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 0f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 5f64);
assert_eq!(c.to_number(&mut engine).unwrap(), 13f64);
assert_eq!(d.to_number(&mut engine).unwrap(), 7.071_067_811_865_475_5);
assert_eq!(e.to_number(&mut engine).unwrap(), 8.774964387392123);
assert!(f.to_number(&mut engine).unwrap().is_infinite());
assert_eq!(g.to_number(&mut engine).unwrap(), 12f64);
assert_eq!(a.to_number(&mut context).unwrap(), 0f64);
assert_eq!(b.to_number(&mut context).unwrap(), 5f64);
assert_eq!(c.to_number(&mut context).unwrap(), 13f64);
assert_eq!(d.to_number(&mut context).unwrap(), 7.071_067_811_865_475_5);
assert_eq!(e.to_number(&mut context).unwrap(), 8.774964387392123);
assert!(f.to_number(&mut context).unwrap().is_infinite());
assert_eq!(g.to_number(&mut context).unwrap(), 12f64);
}
#[test]
#[allow(clippy::many_single_char_names)]
fn imul() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.imul(3, 4);
var b = Math.imul(-5, 12);
@ -412,47 +412,47 @@ fn imul() {
var f = Math.imul();
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
let d = forward_val(&mut context, "d").unwrap();
let e = forward_val(&mut context, "e").unwrap();
let f = forward_val(&mut context, "f").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 12f64);
assert_eq!(b.to_number(&mut engine).unwrap(), -60f64);
assert_eq!(c.to_number(&mut engine).unwrap(), -5f64);
assert_eq!(d.to_number(&mut engine).unwrap(), -10f64);
assert_eq!(e.to_number(&mut engine).unwrap(), 0f64);
assert_eq!(f.to_number(&mut engine).unwrap(), 0f64);
assert_eq!(a.to_number(&mut context).unwrap(), 12f64);
assert_eq!(b.to_number(&mut context).unwrap(), -60f64);
assert_eq!(c.to_number(&mut context).unwrap(), -5f64);
assert_eq!(d.to_number(&mut context).unwrap(), -10f64);
assert_eq!(e.to_number(&mut context).unwrap(), 0f64);
assert_eq!(f.to_number(&mut context).unwrap(), 0f64);
}
#[test]
fn log() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.log(1);
var b = Math.log(10);
var c = Math.log(-1);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward(&mut engine, "c");
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward(&mut context, "c");
assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), f64::consts::LN_10);
assert_eq!(a.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut context).unwrap(), f64::consts::LN_10);
assert_eq!(c, String::from("NaN"));
}
#[test]
#[allow(clippy::many_single_char_names)]
fn log1p() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.log1p(1);
var b = Math.log1p(0);
@ -463,19 +463,19 @@ fn log1p() {
var g = Math.log1p();
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward(&mut engine, "d");
let e = forward(&mut engine, "e");
let f = forward(&mut engine, "f");
let g = forward(&mut engine, "g");
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
let d = forward(&mut context, "d");
let e = forward(&mut context, "e");
let f = forward(&mut context, "f");
let g = forward(&mut context, "g");
assert_eq!(a.to_number(&mut engine).unwrap(), f64::consts::LN_2);
assert_eq!(b.to_number(&mut engine).unwrap(), 0f64);
assert_eq!(c.to_number(&mut engine).unwrap(), -36.736_800_569_677_1);
assert_eq!(a.to_number(&mut context).unwrap(), f64::consts::LN_2);
assert_eq!(b.to_number(&mut context).unwrap(), 0f64);
assert_eq!(c.to_number(&mut context).unwrap(), -36.736_800_569_677_1);
assert_eq!(d, "-Infinity");
assert_eq!(e, String::from("NaN"));
assert_eq!(f, String::from("NaN"));
@ -484,87 +484,87 @@ fn log1p() {
#[test]
fn log10() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.log10(2);
var b = Math.log10(1);
var c = Math.log10(-2);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward(&mut engine, "c");
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward(&mut context, "c");
assert_eq!(a.to_number(&mut engine).unwrap(), f64::consts::LOG10_2);
assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(a.to_number(&mut context).unwrap(), f64::consts::LOG10_2);
assert_eq!(b.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(c, String::from("NaN"));
}
#[test]
fn log2() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.log2(3);
var b = Math.log2(1);
var c = Math.log2(-2);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward(&mut engine, "c");
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward(&mut context, "c");
assert_eq!(a.to_number(&mut engine).unwrap(), 1.584_962_500_721_156);
assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 1.584_962_500_721_156);
assert_eq!(b.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(c, String::from("NaN"));
}
#[test]
fn max() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.max(10, 20);
var b = Math.max(-10, -20);
var c = Math.max(-10, 20);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 20_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), -10_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), 20_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 20_f64);
assert_eq!(b.to_number(&mut context).unwrap(), -10_f64);
assert_eq!(c.to_number(&mut context).unwrap(), 20_f64);
}
#[test]
fn min() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.min(10, 20);
var b = Math.min(-10, -20);
var c = Math.min(-10, 20);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 10_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), -20_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), -10_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 10_f64);
assert_eq!(b.to_number(&mut context).unwrap(), -20_f64);
assert_eq!(c.to_number(&mut context).unwrap(), -10_f64);
}
#[test]
fn pow() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.pow(2, 10);
var b = Math.pow(-7, 2);
@ -572,158 +572,158 @@ fn pow() {
var d = Math.pow(7, -2);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
let d = forward_val(&mut context, "d").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 1_024_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 49_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), 2.0);
assert_eq!(d.to_number(&mut engine).unwrap(), 0.020_408_163_265_306_12);
assert_eq!(a.to_number(&mut context).unwrap(), 1_024_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 49_f64);
assert_eq!(c.to_number(&mut context).unwrap(), 2.0);
assert_eq!(d.to_number(&mut context).unwrap(), 0.020_408_163_265_306_12);
}
#[test]
fn round() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.round(20.5);
var b = Math.round(-20.3);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 21.0);
assert_eq!(b.to_number(&mut engine).unwrap(), -20.0);
assert_eq!(a.to_number(&mut context).unwrap(), 21.0);
assert_eq!(b.to_number(&mut context).unwrap(), -20.0);
}
#[test]
fn sign() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.sign(3);
var b = Math.sign(-3);
var c = Math.sign(0);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), -1_f64);
assert_eq!(c.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 1_f64);
assert_eq!(b.to_number(&mut context).unwrap(), -1_f64);
assert_eq!(c.to_number(&mut context).unwrap(), 0_f64);
}
#[test]
fn sin() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.sin(0);
var b = Math.sin(1);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 0.841_470_984_807_896_5);
assert_eq!(a.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 0.841_470_984_807_896_5);
}
#[test]
fn sinh() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.sinh(0);
var b = Math.sinh(1);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 1.175_201_193_643_801_4);
assert_eq!(a.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 1.175_201_193_643_801_4);
}
#[test]
fn sqrt() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.sqrt(0);
var b = Math.sqrt(2);
var c = Math.sqrt(9);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
let c = forward_val(&mut context, "c").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), f64::consts::SQRT_2);
assert_eq!(c.to_number(&mut engine).unwrap(), 3_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 0_f64);
assert_eq!(b.to_number(&mut context).unwrap(), f64::consts::SQRT_2);
assert_eq!(c.to_number(&mut context).unwrap(), 3_f64);
}
#[test]
fn tan() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.tan(1.1);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let a = forward_val(&mut context, "a").unwrap();
assert!(float_cmp::approx_eq!(
f64,
a.to_number(&mut engine).unwrap(),
a.to_number(&mut context).unwrap(),
1.964_759_657_248_652_5
));
}
#[test]
fn tanh() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.tanh(1);
var b = Math.tanh(0);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 0.761_594_155_955_764_9);
assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 0.761_594_155_955_764_9);
assert_eq!(b.to_number(&mut context).unwrap(), 0_f64);
}
#[test]
fn trunc() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var a = Math.trunc(13.37);
var b = Math.trunc(0.123);
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let a = forward_val(&mut context, "a").unwrap();
let b = forward_val(&mut context, "b").unwrap();
assert_eq!(a.to_number(&mut engine).unwrap(), 13_f64);
assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64);
assert_eq!(a.to_number(&mut context).unwrap(), 13_f64);
assert_eq!(b.to_number(&mut context).unwrap(), 0_f64);
}

2
boa/src/builtins/nan/mod.rs

@ -26,7 +26,7 @@ impl BuiltIn for NaN {
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
}
fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
fn init(_: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, f64::NAN.into(), Self::attribute())

90
boa/src/builtins/number/mod.rs

@ -153,9 +153,13 @@ impl Number {
pub(crate) const MIN_VALUE: f64 = f64::MIN;
/// `Number( value )`
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_numeric_number(ctx)?,
Some(ref value) => value.to_numeric_number(context)?,
None => 0.0,
};
this.set_data(ObjectData::Number(data));
@ -172,7 +176,7 @@ impl Number {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue
fn this_number_value(value: &Value, ctx: &mut Context) -> Result<f64> {
fn this_number_value(value: &Value, context: &mut Context) -> Result<f64> {
match *value {
Value::Integer(integer) => return Ok(f64::from(integer)),
Value::Rational(rational) => return Ok(rational),
@ -184,7 +188,7 @@ impl Number {
_ => {}
}
Err(ctx.construct_type_error("'this' is not a number"))
Err(context.construct_type_error("'this' is not a number"))
}
/// Helper function that formats a float as a ES6-style exponential number string.
@ -209,10 +213,10 @@ impl Number {
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_exponential(
this: &Value,
_args: &[Value],
ctx: &mut Context,
_: &[Value],
context: &mut Context,
) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
let this_num = Self::this_number_value(this, context)?;
let this_str_num = Self::num_to_exponential(this_num);
Ok(Value::from(this_str_num))
}
@ -228,11 +232,11 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_fixed(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
pub(crate) fn to_fixed(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let this_num = Self::this_number_value(this, context)?;
let precision = match args.get(0) {
Some(n) => match n.to_integer(ctx)? as i32 {
x if x > 0 => n.to_integer(ctx)? as usize,
Some(n) => match n.to_integer(context)? as i32 {
x if x > 0 => n.to_integer(context)? as usize,
_ => 0,
},
None => 0,
@ -257,10 +261,10 @@ impl Number {
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_locale_string(
this: &Value,
_args: &[Value],
ctx: &mut Context,
_: &[Value],
context: &mut Context,
) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
let this_num = Self::this_number_value(this, context)?;
let this_str_num = format!("{}", this_num);
Ok(Value::from(this_str_num))
}
@ -276,12 +280,16 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_precision(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
pub(crate) fn to_precision(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
let this_num = Self::this_number_value(this, context)?;
let _num_str_len = format!("{}", this_num).len();
let _precision = match args.get(0) {
Some(n) => match n.to_integer(ctx)? as i32 {
x if x > 0 => n.to_integer(ctx)? as usize,
Some(n) => match n.to_integer(context)? as i32 {
x if x > 0 => n.to_integer(context)? as usize,
_ => 0,
},
None => 0,
@ -430,21 +438,21 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn to_string(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// 1. Let x be ? thisNumberValue(this value).
let x = Self::this_number_value(this, ctx)?;
let x = Self::this_number_value(this, context)?;
// 2. If radix is undefined, let radixNumber be 10.
// 3. Else, let radixNumber be ? ToInteger(radix).
let radix = args
.get(0)
.map(|arg| arg.to_integer(ctx))
.map(|arg| arg.to_integer(context))
.transpose()?
.map_or(10, |radix| radix as u8);
// 4. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
if radix < 2 || radix > 36 {
return ctx
return context
.throw_range_error("radix must be an integer at least 2 and no greater than 36");
}
@ -485,8 +493,8 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf
pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_number_value(this, ctx)?))
pub(crate) fn value_of(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_number_value(this, context)?))
}
/// Builtin javascript 'parseInt(str, radix)' function.
@ -503,7 +511,7 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
pub(crate) fn parse_int(_this: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
pub(crate) fn parse_int(_: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
if let (Some(val), r) = (args.get(0), args.get(1)) {
let mut radix = if let Some(rx) = r {
if let Value::Integer(i) = rx {
@ -569,7 +577,7 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
pub(crate) fn parse_float(_this: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
pub(crate) fn parse_float(_: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
if let Some(val) = args.get(0) {
match val {
Value::String(s) => {
@ -612,12 +620,12 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-isfinite-number
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite
pub(crate) fn global_is_finite(
_this: &Value,
_: &Value,
args: &[Value],
ctx: &mut Context,
context: &mut Context,
) -> Result<Value> {
if let Some(value) = args.get(0) {
let number = value.to_number(ctx)?;
let number = value.to_number(context)?;
Ok(number.is_finite().into())
} else {
Ok(false.into())
@ -638,9 +646,9 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-isnan-number
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN
pub(crate) fn global_is_nan(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn global_is_nan(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if let Some(value) = args.get(0) {
let number = value.to_number(ctx)?;
let number = value.to_number(context)?;
Ok(number.is_nan().into())
} else {
Ok(true.into())
@ -661,11 +669,7 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-number.isfinite
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite
pub(crate) fn number_is_finite(
_this: &Value,
args: &[Value],
_ctx: &mut Context,
) -> Result<Value> {
pub(crate) fn number_is_finite(_: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
Ok(Value::from(if let Some(val) = args.get(0) {
match val {
Value::Integer(_) => true,
@ -688,7 +692,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.isinteger
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
pub(crate) fn number_is_integer(
_this: &Value,
_: &Value,
args: &[Value],
_ctx: &mut Context,
) -> Result<Value> {
@ -709,11 +713,7 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-isnan-number
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
pub(crate) fn number_is_nan(
_this: &Value,
args: &[Value],
_ctx: &mut Context,
) -> Result<Value> {
pub(crate) fn number_is_nan(_: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
Ok(Value::from(if let Some(val) = args.get(0) {
match val {
Value::Integer(_) => false,
@ -739,11 +739,7 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-isnan-number
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
pub(crate) fn is_safe_integer(
_this: &Value,
args: &[Value],
_ctx: &mut Context,
) -> Result<Value> {
pub(crate) fn is_safe_integer(_: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
Ok(Value::from(match args.get(0) {
Some(Value::Integer(_)) => true,
Some(Value::Rational(number)) if Self::is_float_integer(*number) => {

575
boa/src/builtins/number/tests.rs

File diff suppressed because it is too large Load Diff

54
boa/src/builtins/object/mod.rs

@ -97,7 +97,7 @@ impl Object {
///
/// [spec]: https://tc39.es/ecma262/#sec-object.create
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
pub fn create(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub fn create(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let prototype = args.get(0).cloned().unwrap_or_else(Value::undefined);
let properties = args.get(1).cloned().unwrap_or_else(Value::undefined);
@ -107,7 +107,7 @@ impl Object {
ObjectData::Ordinary,
)),
_ => {
return ctx.throw_type_error(format!(
return context.throw_type_error(format!(
"Object prototype may only be an Object or null: {}",
prototype.display()
))
@ -115,7 +115,7 @@ impl Object {
};
if !properties.is_undefined() {
return Object::define_properties(&Value::Undefined, &[obj, properties], ctx);
return Object::define_properties(&Value::Undefined, &[obj, properties], context);
}
Ok(obj)
@ -134,14 +134,17 @@ impl Object {
pub fn get_own_property_descriptor(
_: &Value,
args: &[Value],
ctx: &mut Context,
context: &mut Context,
) -> Result<Value> {
let object = args.get(0).unwrap_or(&Value::undefined()).to_object(ctx)?;
let object = args
.get(0)
.unwrap_or(&Value::undefined())
.to_object(context)?;
if let Some(key) = args.get(1) {
let key = key.to_property_key(ctx)?;
let key = key.to_property_key(context)?;
if let Some(desc) = object.get_own_property(&key) {
return Ok(Self::from_property_descriptor(desc, ctx)?);
return Ok(Self::from_property_descriptor(desc, context)?);
}
}
@ -161,17 +164,20 @@ impl Object {
pub fn get_own_property_descriptors(
_: &Value,
args: &[Value],
ctx: &mut Context,
context: &mut Context,
) -> Result<Value> {
let object = args.get(0).unwrap_or(&Value::undefined()).to_object(ctx)?;
let descriptors = ctx.construct_object();
let object = args
.get(0)
.unwrap_or(&Value::undefined())
.to_object(context)?;
let descriptors = context.construct_object();
for key in object.borrow().keys() {
let descriptor = {
let desc = object
.get_own_property(&key)
.expect("Expected property to be on object.");
Self::from_property_descriptor(desc, ctx)?
Self::from_property_descriptor(desc, context)?
};
if !descriptor.is_undefined() {
@ -189,8 +195,8 @@ impl Object {
///
/// [ECMAScript reference][spec]
/// [spec]: https://tc39.es/ecma262/#sec-frompropertydescriptor
fn from_property_descriptor(desc: PropertyDescriptor, ctx: &mut Context) -> Result<Value> {
let mut descriptor = ObjectInitializer::new(ctx);
fn from_property_descriptor(desc: PropertyDescriptor, context: &mut Context) -> Result<Value> {
let mut descriptor = ObjectInitializer::new(context);
if let PropertyDescriptor::Data(data_desc) = &desc {
descriptor.property("value", data_desc.value(), Attribute::all());
@ -278,15 +284,15 @@ impl Object {
///
/// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties
pub fn define_properties(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub fn define_properties(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let arg = args.get(0).cloned().unwrap_or_default();
let arg_obj = arg.as_object();
if let Some(mut obj) = arg_obj {
let props = args.get(1).cloned().unwrap_or_else(Value::undefined);
obj.define_properties(props, ctx)?;
obj.define_properties(props, context)?;
Ok(arg)
} else {
ctx.throw_type_error("Expected an object")
context.throw_type_error("Expected an object")
}
}
/// `Object.prototype.toString()`
@ -300,13 +306,13 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
#[allow(clippy::wrong_self_convention)]
pub fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
pub fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
if this.is_undefined() {
Ok("[object Undefined]".into())
} else if this.is_null() {
Ok("[object Null]".into())
} else {
let o = this.to_object(ctx)?;
let o = this.to_object(context)?;
let builtin_tag = {
let o = o.borrow();
match &o.data {
@ -323,7 +329,7 @@ impl Object {
}
};
let tag = o.get(&ctx.well_known_symbols().to_string_tag_symbol().into());
let tag = o.get(&context.well_known_symbols().to_string_tag_symbol().into());
let tag_str = tag.as_string().map(|s| s.as_str()).unwrap_or(builtin_tag);
@ -342,11 +348,11 @@ impl Object {
///
/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub fn has_own_property(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let prop = if args.is_empty() {
None
} else {
Some(args.get(0).expect("Cannot get object").to_string(ctx)?)
Some(args.get(0).expect("Cannot get object").to_string(context)?)
};
let own_property = this
.as_object()
@ -362,15 +368,15 @@ impl Object {
pub fn property_is_enumerable(
this: &Value,
args: &[Value],
ctx: &mut Context,
context: &mut Context,
) -> Result<Value> {
let key = match args.get(0) {
None => return Ok(Value::from(false)),
Some(key) => key,
};
let key = key.to_property_key(ctx)?;
let own_property = this.to_object(ctx)?.get_own_property(&key);
let key = key.to_property_key(context)?;
let own_property = this.to_object(context)?.get_own_property(&key);
Ok(own_property.map_or(Value::from(false), |own_prop| {
Value::from(own_prop.enumerable())

168
boa/src/builtins/object/tests.rs

@ -2,22 +2,22 @@ use crate::{forward, Context, Value};
#[test]
fn object_create_with_regular_object() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const foo = { a: 5 };
const bar = Object.create(foo);
"#;
forward(&mut engine, init);
forward(&mut context, init);
assert_eq!(forward(&mut engine, "bar.a"), "5");
assert_eq!(forward(&mut engine, "Object.create.length"), "2");
assert_eq!(forward(&mut context, "bar.a"), "5");
assert_eq!(forward(&mut context, "Object.create.length"), "2");
}
#[test]
fn object_create_with_undefined() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
try {
@ -27,7 +27,7 @@ fn object_create_with_undefined() {
}
"#;
let result = forward(&mut engine, init);
let result = forward(&mut context, init);
assert_eq!(
result,
"\"TypeError: Object prototype may only be an Object or null: undefined\""
@ -36,7 +36,7 @@ fn object_create_with_undefined() {
#[test]
fn object_create_with_number() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
try {
@ -46,7 +46,7 @@ fn object_create_with_number() {
}
"#;
let result = forward(&mut engine, init);
let result = forward(&mut context, init);
assert_eq!(
result,
"\"TypeError: Object prototype may only be an Object or null: 5\""
@ -57,7 +57,7 @@ fn object_create_with_number() {
#[ignore]
// TODO: to test on __proto__ somehow. __proto__ getter is not working as expected currently
fn object_create_with_function() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
const x = function (){};
@ -65,80 +65,89 @@ fn object_create_with_function() {
bar.__proto__
"#;
let result = forward(&mut engine, init);
let result = forward(&mut context, init);
assert_eq!(result, "...something on __proto__...");
}
#[test]
fn object_is() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var foo = { a: 1};
var bar = { a: 1};
"#;
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "Object.is('foo', 'foo')"), "true");
assert_eq!(forward(&mut engine, "Object.is('foo', 'bar')"), "false");
assert_eq!(forward(&mut engine, "Object.is([], [])"), "false");
assert_eq!(forward(&mut engine, "Object.is(foo, foo)"), "true");
assert_eq!(forward(&mut engine, "Object.is(foo, bar)"), "false");
assert_eq!(forward(&mut engine, "Object.is(null, null)"), "true");
assert_eq!(forward(&mut engine, "Object.is(0, -0)"), "false");
assert_eq!(forward(&mut engine, "Object.is(-0, -0)"), "true");
assert_eq!(forward(&mut engine, "Object.is(NaN, 0/0)"), "true");
assert_eq!(forward(&mut engine, "Object.is()"), "true");
assert_eq!(forward(&mut engine, "Object.is(undefined)"), "true");
assert!(engine.global_object().is_global());
assert!(!engine.global_object().get_field("Object").is_global());
forward(&mut context, init);
assert_eq!(forward(&mut context, "Object.is('foo', 'foo')"), "true");
assert_eq!(forward(&mut context, "Object.is('foo', 'bar')"), "false");
assert_eq!(forward(&mut context, "Object.is([], [])"), "false");
assert_eq!(forward(&mut context, "Object.is(foo, foo)"), "true");
assert_eq!(forward(&mut context, "Object.is(foo, bar)"), "false");
assert_eq!(forward(&mut context, "Object.is(null, null)"), "true");
assert_eq!(forward(&mut context, "Object.is(0, -0)"), "false");
assert_eq!(forward(&mut context, "Object.is(-0, -0)"), "true");
assert_eq!(forward(&mut context, "Object.is(NaN, 0/0)"), "true");
assert_eq!(forward(&mut context, "Object.is()"), "true");
assert_eq!(forward(&mut context, "Object.is(undefined)"), "true");
assert!(context.global_object().is_global());
assert!(!context.global_object().get_field("Object").is_global());
}
#[test]
fn object_has_own_property() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let x = { someProp: 1, undefinedProp: undefined, nullProp: null };
"#;
eprintln!("{}", forward(&mut engine, init));
assert_eq!(forward(&mut engine, "x.hasOwnProperty('someProp')"), "true");
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(&mut engine, "x.hasOwnProperty('undefinedProp')"),
forward(&mut context, "x.hasOwnProperty('someProp')"),
"true"
);
assert_eq!(forward(&mut engine, "x.hasOwnProperty('nullProp')"), "true");
assert_eq!(
forward(&mut engine, "x.hasOwnProperty('hasOwnProperty')"),
forward(&mut context, "x.hasOwnProperty('undefinedProp')"),
"true"
);
assert_eq!(
forward(&mut context, "x.hasOwnProperty('nullProp')"),
"true"
);
assert_eq!(
forward(&mut context, "x.hasOwnProperty('hasOwnProperty')"),
"false"
);
}
#[test]
fn object_property_is_enumerable() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let x = { enumerableProp: 'yes' };
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(&mut engine, r#"x.propertyIsEnumerable('enumerableProp')"#),
forward(&mut context, r#"x.propertyIsEnumerable('enumerableProp')"#),
"true"
);
assert_eq!(
forward(&mut engine, r#"x.propertyIsEnumerable('hasOwnProperty')"#),
forward(&mut context, r#"x.propertyIsEnumerable('hasOwnProperty')"#),
"false"
);
assert_eq!(
forward(&mut engine, r#"x.propertyIsEnumerable('not_here')"#),
forward(&mut context, r#"x.propertyIsEnumerable('not_here')"#),
"false",
);
assert_eq!(forward(&mut engine, r#"x.propertyIsEnumerable()"#), "false",)
assert_eq!(
forward(&mut context, r#"x.propertyIsEnumerable()"#),
"false",
)
}
#[test]
fn object_to_string() {
let mut ctx = Context::new();
let mut context = Context::new();
let init = r#"
let u = undefined;
let n = null;
@ -160,92 +169,101 @@ fn object_to_string() {
RegExp.prototype.toString = Object.prototype.toString;
let o = Object();
"#;
eprintln!("{}", forward(&mut ctx, init));
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(&mut ctx, "Object.prototype.toString.call(u)"),
forward(&mut context, "Object.prototype.toString.call(u)"),
"\"[object Undefined]\""
);
assert_eq!(
forward(&mut ctx, "Object.prototype.toString.call(n)"),
forward(&mut context, "Object.prototype.toString.call(n)"),
"\"[object Null]\""
);
assert_eq!(forward(&mut ctx, "a.toString()"), "\"[object Array]\"");
assert_eq!(forward(&mut ctx, "f.toString()"), "\"[object Function]\"");
assert_eq!(forward(&mut ctx, "e.toString()"), "\"[object Error]\"");
assert_eq!(forward(&mut ctx, "b.toString()"), "\"[object Boolean]\"");
assert_eq!(forward(&mut ctx, "i.toString()"), "\"[object Number]\"");
assert_eq!(forward(&mut ctx, "s.toString()"), "\"[object String]\"");
assert_eq!(forward(&mut ctx, "d.toString()"), "\"[object Date]\"");
assert_eq!(forward(&mut ctx, "re.toString()"), "\"[object RegExp]\"");
assert_eq!(forward(&mut ctx, "o.toString()"), "\"[object Object]\"");
assert_eq!(forward(&mut context, "a.toString()"), "\"[object Array]\"");
assert_eq!(
forward(&mut context, "f.toString()"),
"\"[object Function]\""
);
assert_eq!(forward(&mut context, "e.toString()"), "\"[object Error]\"");
assert_eq!(
forward(&mut context, "b.toString()"),
"\"[object Boolean]\""
);
assert_eq!(forward(&mut context, "i.toString()"), "\"[object Number]\"");
assert_eq!(forward(&mut context, "s.toString()"), "\"[object String]\"");
assert_eq!(forward(&mut context, "d.toString()"), "\"[object Date]\"");
assert_eq!(
forward(&mut context, "re.toString()"),
"\"[object RegExp]\""
);
assert_eq!(forward(&mut context, "o.toString()"), "\"[object Object]\"");
}
#[test]
fn define_symbol_property() {
let mut ctx = Context::new();
let mut context = Context::new();
let init = r#"
let obj = {};
let sym = Symbol("key");
Object.defineProperty(obj, sym, { value: "val" });
"#;
eprintln!("{}", forward(&mut ctx, init));
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut ctx, "obj[sym]"), "\"val\"");
assert_eq!(forward(&mut context, "obj[sym]"), "\"val\"");
}
#[test]
fn get_own_property_descriptor_1_arg_returns_undefined() {
let mut ctx = Context::new();
let mut context = Context::new();
let code = r#"
let obj = {a: 2};
Object.getOwnPropertyDescriptor(obj)
"#;
assert_eq!(ctx.eval(code).unwrap(), Value::undefined());
assert_eq!(context.eval(code).unwrap(), Value::undefined());
}
#[test]
fn get_own_property_descriptor() {
let mut ctx = Context::new();
let mut context = Context::new();
forward(
&mut ctx,
&mut context,
r#"
let obj = {a: 2};
let result = Object.getOwnPropertyDescriptor(obj, "a");
"#,
);
assert_eq!(forward(&mut ctx, "result.enumerable"), "true");
assert_eq!(forward(&mut ctx, "result.writable"), "true");
assert_eq!(forward(&mut ctx, "result.configurable"), "true");
assert_eq!(forward(&mut ctx, "result.value"), "2");
assert_eq!(forward(&mut context, "result.enumerable"), "true");
assert_eq!(forward(&mut context, "result.writable"), "true");
assert_eq!(forward(&mut context, "result.configurable"), "true");
assert_eq!(forward(&mut context, "result.value"), "2");
}
#[test]
fn get_own_property_descriptors() {
let mut ctx = Context::new();
let mut context = Context::new();
forward(
&mut ctx,
&mut context,
r#"
let obj = {a: 1, b: 2};
let result = Object.getOwnPropertyDescriptors(obj);
"#,
);
assert_eq!(forward(&mut ctx, "result.a.enumerable"), "true");
assert_eq!(forward(&mut ctx, "result.a.writable"), "true");
assert_eq!(forward(&mut ctx, "result.a.configurable"), "true");
assert_eq!(forward(&mut ctx, "result.a.value"), "1");
assert_eq!(forward(&mut context, "result.a.enumerable"), "true");
assert_eq!(forward(&mut context, "result.a.writable"), "true");
assert_eq!(forward(&mut context, "result.a.configurable"), "true");
assert_eq!(forward(&mut context, "result.a.value"), "1");
assert_eq!(forward(&mut ctx, "result.b.enumerable"), "true");
assert_eq!(forward(&mut ctx, "result.b.writable"), "true");
assert_eq!(forward(&mut ctx, "result.b.configurable"), "true");
assert_eq!(forward(&mut ctx, "result.b.value"), "2");
assert_eq!(forward(&mut context, "result.b.enumerable"), "true");
assert_eq!(forward(&mut context, "result.b.writable"), "true");
assert_eq!(forward(&mut context, "result.b.configurable"), "true");
assert_eq!(forward(&mut context, "result.b.value"), "2");
}
#[test]
fn object_define_properties() {
let mut ctx = Context::new();
let mut context = Context::new();
let init = r#"
const obj = {};
@ -257,7 +275,7 @@ fn object_define_properties() {
}
});
"#;
eprintln!("{}", forward(&mut ctx, init));
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut ctx, "obj.p"), "42");
assert_eq!(forward(&mut context, "obj.p"), "42");
}

16
boa/src/builtins/regexp/mod.rs

@ -309,12 +309,12 @@ impl RegExp {
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test
pub(crate) fn test(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn test(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let arg_str = args
.get(0)
.expect("could not get argument")
.to_string(ctx)?;
let mut last_index = this.get_field("lastIndex").to_index(ctx)?;
.to_string(context)?;
let mut last_index = this.get_field("lastIndex").to_index(context)?;
let result = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap();
@ -350,12 +350,12 @@ impl RegExp {
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
pub(crate) fn exec(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn exec(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let arg_str = args
.get(0)
.expect("could not get argument")
.to_string(ctx)?;
let mut last_index = this.get_field("lastIndex").to_index(ctx)?;
.to_string(context)?;
let mut last_index = this.get_field("lastIndex").to_index(context)?;
let result = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap();
@ -405,7 +405,7 @@ impl RegExp {
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match
pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Context) -> Result<Value> {
pub(crate) fn r#match(this: &Value, arg: RcString, context: &mut Context) -> Result<Value> {
let (matcher, flags) = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap();
@ -423,7 +423,7 @@ impl RegExp {
}
Ok(Value::from(matches))
} else {
Self::exec(this, &[Value::from(arg)], ctx)
Self::exec(this, &[Value::from(arg)], context)
}
}

80
boa/src/builtins/regexp/tests.rs

@ -2,99 +2,99 @@ use crate::{forward, Context};
#[test]
fn constructors() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var constructed = new RegExp("[0-9]+(\\.[0-9]+)?");
var literal = /[0-9]+(\.[0-9]+)?/;
var ctor_literal = new RegExp(/[0-9]+(\.[0-9]+)?/);
"#;
eprintln!("{}", forward(&mut engine, init));
assert_eq!(forward(&mut engine, "constructed.test('1.0')"), "true");
assert_eq!(forward(&mut engine, "literal.test('1.0')"), "true");
assert_eq!(forward(&mut engine, "ctor_literal.test('1.0')"), "true");
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut context, "constructed.test('1.0')"), "true");
assert_eq!(forward(&mut context, "literal.test('1.0')"), "true");
assert_eq!(forward(&mut context, "ctor_literal.test('1.0')"), "true");
}
// TODO: uncomment this test when property getters are supported
// #[test]
// fn flags() {
// let mut engine = Context::new();
// let mut context = Context::new();
// let init = r#"
// var re_gi = /test/gi;
// var re_sm = /test/sm;
// "#;
//
// eprintln!("{}", forward(&mut engine, init));
// assert_eq!(forward(&mut engine, "re_gi.global"), "true");
// assert_eq!(forward(&mut engine, "re_gi.ignoreCase"), "true");
// assert_eq!(forward(&mut engine, "re_gi.multiline"), "false");
// assert_eq!(forward(&mut engine, "re_gi.dotAll"), "false");
// assert_eq!(forward(&mut engine, "re_gi.unicode"), "false");
// assert_eq!(forward(&mut engine, "re_gi.sticky"), "false");
// assert_eq!(forward(&mut engine, "re_gi.flags"), "gi");
// eprintln!("{}", forward(&mut context, init));
// assert_eq!(forward(&mut context, "re_gi.global"), "true");
// assert_eq!(forward(&mut context, "re_gi.ignoreCase"), "true");
// assert_eq!(forward(&mut context, "re_gi.multiline"), "false");
// assert_eq!(forward(&mut context, "re_gi.dotAll"), "false");
// assert_eq!(forward(&mut context, "re_gi.unicode"), "false");
// assert_eq!(forward(&mut context, "re_gi.sticky"), "false");
// assert_eq!(forward(&mut context, "re_gi.flags"), "gi");
//
// assert_eq!(forward(&mut engine, "re_sm.global"), "false");
// assert_eq!(forward(&mut engine, "re_sm.ignoreCase"), "false");
// assert_eq!(forward(&mut engine, "re_sm.multiline"), "true");
// assert_eq!(forward(&mut engine, "re_sm.dotAll"), "true");
// assert_eq!(forward(&mut engine, "re_sm.unicode"), "false");
// assert_eq!(forward(&mut engine, "re_sm.sticky"), "false");
// assert_eq!(forward(&mut engine, "re_sm.flags"), "ms");
// assert_eq!(forward(&mut context, "re_sm.global"), "false");
// assert_eq!(forward(&mut context, "re_sm.ignoreCase"), "false");
// assert_eq!(forward(&mut context, "re_sm.multiline"), "true");
// assert_eq!(forward(&mut context, "re_sm.dotAll"), "true");
// assert_eq!(forward(&mut context, "re_sm.unicode"), "false");
// assert_eq!(forward(&mut context, "re_sm.sticky"), "false");
// assert_eq!(forward(&mut context, "re_sm.flags"), "ms");
// }
#[test]
fn last_index() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var regex = /[0-9]+(\.[0-9]+)?/g;
"#;
eprintln!("{}", forward(&mut engine, init));
assert_eq!(forward(&mut engine, "regex.lastIndex"), "0");
assert_eq!(forward(&mut engine, "regex.test('1.0foo')"), "true");
assert_eq!(forward(&mut engine, "regex.lastIndex"), "3");
assert_eq!(forward(&mut engine, "regex.test('1.0foo')"), "false");
assert_eq!(forward(&mut engine, "regex.lastIndex"), "0");
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut context, "regex.lastIndex"), "0");
assert_eq!(forward(&mut context, "regex.test('1.0foo')"), "true");
assert_eq!(forward(&mut context, "regex.lastIndex"), "3");
assert_eq!(forward(&mut context, "regex.test('1.0foo')"), "false");
assert_eq!(forward(&mut context, "regex.lastIndex"), "0");
}
#[test]
fn exec() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var re = /quick\s(brown).+?(jumps)/ig;
var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
"#;
eprintln!("{}", forward(&mut engine, init));
eprintln!("{}", forward(&mut context, init));
assert_eq!(
forward(&mut engine, "result[0]"),
forward(&mut context, "result[0]"),
"\"Quick Brown Fox Jumps\""
);
assert_eq!(forward(&mut engine, "result[1]"), "\"Brown\"");
assert_eq!(forward(&mut engine, "result[2]"), "\"Jumps\"");
assert_eq!(forward(&mut engine, "result.index"), "4");
assert_eq!(forward(&mut context, "result[1]"), "\"Brown\"");
assert_eq!(forward(&mut context, "result[2]"), "\"Jumps\"");
assert_eq!(forward(&mut context, "result.index"), "4");
assert_eq!(
forward(&mut engine, "result.input"),
forward(&mut context, "result.input"),
"\"The Quick Brown Fox Jumps Over The Lazy Dog\""
);
}
#[test]
fn to_string() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(
forward(&mut engine, "(new RegExp('a+b+c')).toString()"),
forward(&mut context, "(new RegExp('a+b+c')).toString()"),
"\"/a+b+c/\""
);
assert_eq!(
forward(&mut engine, "(new RegExp('bar', 'g')).toString()"),
forward(&mut context, "(new RegExp('bar', 'g')).toString()"),
"\"/bar/g\""
);
assert_eq!(
forward(&mut engine, "(new RegExp('\\\\n', 'g')).toString()"),
forward(&mut context, "(new RegExp('\\\\n', 'g')).toString()"),
"\"/\\n/g\""
);
assert_eq!(forward(&mut engine, "/\\n/g.toString()"), "\"/\\n/g\"");
assert_eq!(forward(&mut context, "/\\n/g.toString()"), "\"/\\n/g\"");
}

232
boa/src/builtins/string/mod.rs

@ -127,11 +127,15 @@ impl String {
/// `String( value )`
///
/// <https://tc39.es/ecma262/#sec-string-constructor-string-value>
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
// This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe
// to its Javascript Identifier (global constructor method name)
let string = match args.get(0) {
Some(ref value) => value.to_string(ctx)?,
Some(ref value) => value.to_string(context)?,
None => RcString::default(),
};
@ -144,7 +148,7 @@ impl String {
Ok(Value::from(string))
}
fn this_string_value(this: &Value, ctx: &mut Context) -> Result<RcString> {
fn this_string_value(this: &Value, context: &mut Context) -> Result<RcString> {
match this {
Value::String(ref string) => return Ok(string.clone()),
Value::Object(ref object) => {
@ -156,15 +160,15 @@ impl String {
_ => {}
}
Err(ctx.construct_type_error("'this' is not a string"))
Err(context.construct_type_error("'this' is not a string"))
}
/// Get the string value to a primitive string
#[allow(clippy::wrong_self_convention)]
#[inline]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
// Get String from String Object and send it back as a new value
Ok(Value::from(Self::this_string_value(this, ctx)?))
Ok(Value::from(Self::this_string_value(this, context)?))
}
/// `String.prototype.charAt( index )`
@ -183,15 +187,15 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt
pub(crate) fn char_at(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn char_at(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
let pos = args
.get(0)
.cloned()
.unwrap_or_else(Value::undefined)
.to_integer(ctx)? as i32;
.to_integer(context)? as i32;
// Calling .len() on a string would give the wrong result, as they are bytes not the number of
// unicode code points
@ -226,10 +230,14 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
pub(crate) fn char_code_at(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn char_code_at(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
// Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points
// Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation.
@ -238,7 +246,7 @@ impl String {
.get(0)
.cloned()
.unwrap_or_else(Value::undefined)
.to_integer(ctx)? as i32;
.to_integer(context)? as i32;
if pos >= length as i32 || pos < 0 {
return Ok(Value::from(NAN));
@ -266,12 +274,12 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat
pub(crate) fn concat(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let object = this.require_object_coercible(ctx)?;
let mut string = object.to_string(ctx)?.to_string();
pub(crate) fn concat(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let object = this.require_object_coercible(context)?;
let mut string = object.to_string(context)?.to_string();
for arg in args {
string.push_str(&arg.to_string(ctx)?);
string.push_str(&arg.to_string(context)?);
}
Ok(Value::from(string))
@ -288,22 +296,22 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
pub(crate) fn repeat(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let object = this.require_object_coercible(ctx)?;
let string = object.to_string(ctx)?;
pub(crate) fn repeat(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let object = this.require_object_coercible(context)?;
let string = object.to_string(context)?;
if let Some(arg) = args.get(0) {
let n = arg.to_integer(ctx)?;
let n = arg.to_integer(context)?;
if n < 0.0 {
return ctx.throw_range_error("repeat count cannot be a negative number");
return context.throw_range_error("repeat count cannot be a negative number");
}
if n.is_infinite() {
return ctx.throw_range_error("repeat count cannot be infinity");
return context.throw_range_error("repeat count cannot be infinity");
}
if n * (string.len() as f64) > Self::MAX_STRING_LENGTH {
return ctx
return context
.throw_range_error("repeat count must not overflow maximum string length");
}
Ok(string.repeat(n as usize).into())
@ -322,10 +330,10 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
pub(crate) fn slice(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn slice(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
// Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points
// Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation.
@ -335,12 +343,12 @@ impl String {
.get(0)
.cloned()
.unwrap_or_else(Value::undefined)
.to_integer(ctx)? as i32;
.to_integer(context)? as i32;
let end = args
.get(1)
.cloned()
.unwrap_or_else(|| Value::integer(length))
.to_integer(ctx)? as i32;
.to_integer(context)? as i32;
let from = if start < 0 {
max(length.wrapping_add(start), 0)
@ -373,20 +381,24 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
pub(crate) fn starts_with(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn starts_with(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
let arg = args.get(0).cloned().unwrap_or_else(Value::undefined);
if Self::is_regexp_object(&arg) {
ctx.throw_type_error(
context.throw_type_error(
"First argument to String.prototype.startsWith must not be a regular expression",
)?;
}
let search_string = arg.to_string(ctx)?;
let search_string = arg.to_string(context)?;
let length = primitive_val.chars().count() as i32;
let search_length = search_string.chars().count() as i32;
@ -395,7 +407,9 @@ impl String {
let position = if args.len() < 2 {
0
} else {
args.get(1).expect("failed to get arg").to_integer(ctx)? as i32
args.get(1)
.expect("failed to get arg")
.to_integer(context)? as i32
};
let start = min(max(position, 0), length);
@ -420,20 +434,20 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
pub(crate) fn ends_with(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn ends_with(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
let arg = args.get(0).cloned().unwrap_or_else(Value::undefined);
if Self::is_regexp_object(&arg) {
ctx.throw_type_error(
context.throw_type_error(
"First argument to String.prototype.endsWith must not be a regular expression",
)?;
}
let search_string = arg.to_string(ctx)?;
let search_string = arg.to_string(context)?;
let length = primitive_val.chars().count() as i32;
let search_length = search_string.chars().count() as i32;
@ -445,7 +459,7 @@ impl String {
} else {
args.get(1)
.expect("Could not get argumetn")
.to_integer(ctx)? as i32
.to_integer(context)? as i32
};
let end = min(max(end_position, 0), length);
@ -470,20 +484,20 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
pub(crate) fn includes(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn includes(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
let arg = args.get(0).cloned().unwrap_or_else(Value::undefined);
if Self::is_regexp_object(&arg) {
ctx.throw_type_error(
context.throw_type_error(
"First argument to String.prototype.includes must not be a regular expression",
)?;
}
let search_string = arg.to_string(ctx)?;
let search_string = arg.to_string(context)?;
let length = primitive_val.chars().count() as i32;
@ -493,7 +507,7 @@ impl String {
} else {
args.get(1)
.expect("Could not get argument")
.to_integer(ctx)? as i32
.to_integer(context)? as i32
};
let start = min(max(position, 0), length);
@ -543,9 +557,9 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
pub(crate) fn replace(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn replace(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// TODO: Support Symbol replacer
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
if args.is_empty() {
return Ok(Value::from(primitive_val));
}
@ -671,9 +685,9 @@ impl String {
// Push the whole string being examined
results.push(Value::from(primitive_val.to_string()));
let result = ctx.call(&replace_object, this, &results)?;
let result = context.call(&replace_object, this, &results)?;
result.to_string(ctx)?.to_string()
result.to_string(context)?.to_string()
}
_ => "undefined".to_string(),
}
@ -701,20 +715,20 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
pub(crate) fn index_of(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?;
pub(crate) fn index_of(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(context)?;
let string = this.to_string(context)?;
let search_string = args
.get(0)
.cloned()
.unwrap_or_else(Value::undefined)
.to_string(ctx)?;
.to_string(context)?;
let length = string.chars().count();
let start = args
.get(1)
.map(|position| position.to_integer(ctx))
.map(|position| position.to_integer(context))
.transpose()?
.map_or(0, |position| position.max(0.0).min(length as f64) as usize);
@ -744,20 +758,24 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
pub(crate) fn last_index_of(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?;
pub(crate) fn last_index_of(
this: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
let this = this.require_object_coercible(context)?;
let string = this.to_string(context)?;
let search_string = args
.get(0)
.cloned()
.unwrap_or_else(Value::undefined)
.to_string(ctx)?;
.to_string(context)?;
let length = string.chars().count();
let start = args
.get(1)
.map(|position| position.to_integer(ctx))
.map(|position| position.to_integer(context))
.transpose()?
.map_or(0, |position| position.max(0.0).min(length as f64) as usize);
@ -785,9 +803,9 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
pub(crate) fn r#match(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let re = RegExp::constructor(&Value::from(Object::default()), &[args[0].clone()], ctx)?;
RegExp::r#match(&re, this.to_string(ctx)?, ctx)
pub(crate) fn r#match(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let re = RegExp::constructor(&Value::from(Object::default()), &[args[0].clone()], context)?;
RegExp::r#match(&re, this.to_string(context)?, context)
}
/// Abstract method `StringPad`.
@ -836,17 +854,17 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
pub(crate) fn pad_end(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let primitive = this.to_string(ctx)?;
pub(crate) fn pad_end(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let primitive = this.to_string(context)?;
if args.is_empty() {
return Err(Value::from("padEnd requires maxLength argument"));
}
let max_length = args
.get(0)
.expect("failed to get argument for String method")
.to_integer(ctx)? as i32;
.to_integer(context)? as i32;
let fill_string = args.get(1).map(|arg| arg.to_string(ctx)).transpose()?;
let fill_string = args.get(1).map(|arg| arg.to_string(context)).transpose()?;
Self::string_pad(primitive, max_length, fill_string, false)
}
@ -863,17 +881,17 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
pub(crate) fn pad_start(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let primitive = this.to_string(ctx)?;
pub(crate) fn pad_start(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let primitive = this.to_string(context)?;
if args.is_empty() {
return Err(Value::from("padStart requires maxLength argument"));
}
let max_length = args
.get(0)
.expect("failed to get argument for String method")
.to_integer(ctx)? as i32;
.to_integer(context)? as i32;
let fill_string = args.get(1).map(|arg| arg.to_string(ctx)).transpose()?;
let fill_string = args.get(1).map(|arg| arg.to_string(context)).transpose()?;
Self::string_pad(primitive, max_length, fill_string, true)
}
@ -911,9 +929,9 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
pub(crate) fn trim(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?;
pub(crate) fn trim(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(context)?;
let string = this.to_string(context)?;
Ok(Value::from(
string.trim_matches(Self::is_trimmable_whitespace),
))
@ -931,8 +949,8 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart
pub(crate) fn trim_start(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let string = this.to_string(ctx)?;
pub(crate) fn trim_start(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let string = this.to_string(context)?;
Ok(Value::from(
string.trim_start_matches(Self::is_trimmable_whitespace),
))
@ -950,9 +968,9 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd
pub(crate) fn trim_end(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?;
pub(crate) fn trim_end(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(context)?;
let string = this.to_string(context)?;
Ok(Value::from(
string.trim_end_matches(Self::is_trimmable_whitespace),
))
@ -969,10 +987,10 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_lowercase(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn to_lowercase(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str = this.to_string(ctx)?;
let this_str = this.to_string(context)?;
// The Rust String is mapped to uppercase using the builtin .to_lowercase().
// There might be corner cases where it does not behave exactly like Javascript expects
Ok(Value::from(this_str.to_lowercase()))
@ -991,10 +1009,10 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_uppercase(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn to_uppercase(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str = this.to_string(ctx)?;
let this_str = this.to_string(context)?;
// The Rust String is mapped to uppercase using the builtin .to_uppercase().
// There might be corner cases where it does not behave exactly like Javascript expects
Ok(Value::from(this_str.to_uppercase()))
@ -1010,17 +1028,17 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring
pub(crate) fn substring(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn substring(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
// If no args are specified, start is 'undefined', defaults to 0
let start = if args.is_empty() {
0
} else {
args.get(0)
.expect("failed to get argument for String method")
.to_integer(ctx)? as i32
.to_integer(context)? as i32
};
let length = primitive_val.encode_utf16().count() as i32;
// If less than 2 args specified, end is the length of the this object converted to a String
@ -1029,7 +1047,7 @@ impl String {
} else {
args.get(1)
.expect("Could not get argument")
.to_integer(ctx)? as i32
.to_integer(context)? as i32
};
// Both start and end args replaced by 0 if they were negative
// or by the length of the String if they were greater
@ -1061,17 +1079,17 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
/// <https://tc39.es/ecma262/#sec-string.prototype.substr>
pub(crate) fn substr(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to.
pub(crate) fn substr(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the context has access to.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
let primitive_val = this.to_string(context)?;
// If no args are specified, start is 'undefined', defaults to 0
let mut start = if args.is_empty() {
0
} else {
args.get(0)
.expect("failed to get argument for String method")
.to_integer(ctx)? as i32
.to_integer(context)? as i32
};
let length = primitive_val.chars().count() as i32;
// If less than 2 args specified, end is +infinity, the maximum number value.
@ -1083,7 +1101,7 @@ impl String {
} else {
args.get(1)
.expect("Could not get argument")
.to_integer(ctx)? as i32
.to_integer(context)? as i32
};
// If start is negative it become the number of code units from the end of the string
if start < 0 {
@ -1117,9 +1135,9 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf
pub(crate) fn value_of(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn value_of(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// Use the to_string method because it is specified to do the same thing in this case
Self::to_string(this, args, ctx)
Self::to_string(this, args, context)
}
/// `String.prototype.matchAll( regexp )`
@ -1135,20 +1153,20 @@ impl String {
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
/// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges
// TODO: update this method to return iterator
pub(crate) fn match_all(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn match_all(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let re: Value = match args.get(0) {
Some(arg) => {
if arg.is_null() {
RegExp::constructor(
&Value::from(Object::default()),
&[Value::from(arg.to_string(ctx)?), Value::from("g")],
ctx,
&[Value::from(arg.to_string(context)?), Value::from("g")],
context,
)
} else if arg.is_undefined() {
RegExp::constructor(
&Value::from(Object::default()),
&[Value::undefined(), Value::from("g")],
ctx,
context,
)
} else {
Ok(arg.clone())
@ -1157,14 +1175,14 @@ impl String {
None => RegExp::constructor(
&Value::from(Object::default()),
&[Value::from(""), Value::from("g")],
ctx,
context,
),
}?;
RegExp::match_all(&re, this.to_string(ctx)?.to_string())
RegExp::match_all(&re, this.to_string(context)?.to_string())
}
pub(crate) fn iterator(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
StringIterator::create_string_iterator(ctx, this.clone())
pub(crate) fn iterator(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
StringIterator::create_string_iterator(context, this.clone())
}
}

37
boa/src/builtins/string/string_iterator.rs

@ -1,11 +1,12 @@
use crate::builtins::string::code_point_at;
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
builtins::{
function::make_builtin_fn, iterable::create_iter_result_object, string::code_point_at,
},
gc::{Finalize, Trace},
object::ObjectData,
property::{Attribute, DataDescriptor},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
#[derive(Debug, Clone, Finalize, Trace)]
pub struct StringIterator {
@ -21,29 +22,29 @@ impl StringIterator {
}
}
pub fn create_string_iterator(ctx: &mut Context, string: Value) -> Result<Value> {
let string_iterator = Value::new_object(Some(ctx.global_object()));
pub fn create_string_iterator(context: &mut Context, string: Value) -> Result<Value> {
let string_iterator = Value::new_object(Some(context.global_object()));
string_iterator.set_data(ObjectData::StringIterator(Self::new(string)));
string_iterator
.as_object()
.expect("array iterator object")
.set_prototype_instance(ctx.iterator_prototypes().string_iterator().into());
.set_prototype_instance(context.iterator_prototypes().string_iterator().into());
Ok(string_iterator)
}
pub fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
pub fn next(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
if let Value::Object(ref object) = this {
let mut object = object.borrow_mut();
if let Some(string_iterator) = object.as_string_iterator_mut() {
if string_iterator.string.is_undefined() {
return Ok(create_iter_result_object(ctx, Value::undefined(), true));
return Ok(create_iter_result_object(context, Value::undefined(), true));
}
let native_string = string_iterator.string.to_string(ctx)?;
let native_string = string_iterator.string.to_string(context)?;
let len = native_string.encode_utf16().count() as i32;
let position = string_iterator.next_index;
if position >= len {
string_iterator.string = Value::undefined();
return Ok(create_iter_result_object(ctx, Value::undefined(), true));
return Ok(create_iter_result_object(context, Value::undefined(), true));
}
let (_, code_unit_count, _) =
code_point_at(native_string, position).expect("Invalid code point position");
@ -51,14 +52,14 @@ impl StringIterator {
let result_string = crate::builtins::string::String::substring(
&string_iterator.string,
&[position.into(), string_iterator.next_index.into()],
ctx,
context,
)?;
Ok(create_iter_result_object(ctx, result_string, false))
Ok(create_iter_result_object(context, result_string, false))
} else {
ctx.throw_type_error("`this` is not an ArrayIterator")
context.throw_type_error("`this` is not an ArrayIterator")
}
} else {
ctx.throw_type_error("`this` is not an ArrayIterator")
context.throw_type_error("`this` is not an ArrayIterator")
}
}
@ -68,19 +69,19 @@ impl StringIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value {
let global = ctx.global_object();
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: Value) -> Value {
let global = context.global_object();
let _timer = BoaProfiler::global().start_event("String Iterator", "init");
// Create prototype
let array_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx);
make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
array_iterator
.as_object()
.expect("array iterator prototype object")
.set_prototype_instance(iterator_prototype);
let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol();
let to_string_tag = context.well_known_symbols().to_string_tag_symbol();
let to_string_tag_property =
DataDescriptor::new("String Iterator", Attribute::CONFIGURABLE);
array_iterator.set_property(to_string_tag, to_string_tag_property);

662
boa/src/builtins/string/tests.rs

File diff suppressed because it is too large Load Diff

14
boa/src/builtins/symbol/mod.rs

@ -315,16 +315,16 @@ impl Symbol {
///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-description
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol
pub(crate) fn constructor(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?),
Some(ref value) if !value.is_undefined() => Some(value.to_string(context)?),
_ => None,
};
Ok(ctx.construct_symbol(description).into())
Ok(context.construct_symbol(description).into())
}
fn this_symbol_value(value: &Value, ctx: &mut Context) -> Result<RcSymbol> {
fn this_symbol_value(value: &Value, context: &mut Context) -> Result<RcSymbol> {
match value {
Value::Symbol(ref symbol) => return Ok(symbol.clone()),
Value::Object(ref object) => {
@ -336,7 +336,7 @@ impl Symbol {
_ => {}
}
Err(ctx.construct_type_error("'this' is not a Symbol"))
Err(context.construct_type_error("'this' is not a Symbol"))
}
/// `Symbol.prototype.toString()`
@ -350,8 +350,8 @@ impl Symbol {
/// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let symbol = Self::this_symbol_value(this, ctx)?;
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let symbol = Self::this_symbol_value(this, context)?;
let description = symbol.description().unwrap_or("");
Ok(Value::from(format!("Symbol({})", description)))
}

22
boa/src/builtins/symbol/tests.rs

@ -2,29 +2,29 @@ use crate::{forward, forward_val, Context};
#[test]
fn call_symbol_and_check_return_type() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var sym = Symbol();
"#;
eprintln!("{}", forward(&mut engine, init));
let sym = forward_val(&mut engine, "sym").unwrap();
eprintln!("{}", forward(&mut context, init));
let sym = forward_val(&mut context, "sym").unwrap();
assert_eq!(sym.is_symbol(), true);
}
#[test]
fn print_symbol_expect_description() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var sym = Symbol("Hello");
"#;
eprintln!("{}", forward(&mut engine, init));
let sym = forward_val(&mut engine, "sym.toString()").unwrap();
eprintln!("{}", forward(&mut context, init));
let sym = forward_val(&mut context, "sym.toString()").unwrap();
assert_eq!(sym.display().to_string(), "\"Symbol(Hello)\"");
}
#[test]
fn symbol_access() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var x = {};
var sym1 = Symbol("Hello");
@ -32,8 +32,8 @@ fn symbol_access() {
x[sym1] = 10;
x[sym2] = 20;
"#;
forward_val(&mut engine, init).unwrap();
assert_eq!(forward(&mut engine, "x[sym1]"), "10");
assert_eq!(forward(&mut engine, "x[sym2]"), "20");
assert_eq!(forward(&mut engine, "x['Symbol(Hello)']"), "undefined");
forward_val(&mut context, init).unwrap();
assert_eq!(forward(&mut context, "x[sym1]"), "10");
assert_eq!(forward(&mut context, "x[sym2]"), "20");
assert_eq!(forward(&mut context, "x['Symbol(Hello)']"), "undefined");
}

2
boa/src/builtins/undefined/mod.rs

@ -25,7 +25,7 @@ impl BuiltIn for Undefined {
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
}
fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
fn init(_: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::undefined(), Self::attribute())

12
boa/src/class.rs

@ -25,9 +25,9 @@
//! const LENGTH: usize = 1;
//!
//! // This is what is called when we do `new Animal()`
//! fn constructor(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Self> {
//! fn constructor(_this: &Value, args: &[Value], context: &mut Context) -> Result<Self> {
//! // This is equivalent to `String(arg)`.
//! let kind = args.get(0).cloned().unwrap_or_default().to_string(ctx)?;
//! let kind = args.get(0).cloned().unwrap_or_default().to_string(context)?;
//!
//! let animal = match kind.as_str() {
//! "cat" => Self::Cat,
@ -77,7 +77,7 @@ pub trait Class: NativeObject + Sized {
const ATTRIBUTE: Attribute = Attribute::all();
/// The constructor of the class.
fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Self>;
fn constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Self>;
/// Initializes the internals and the methods of the class.
fn init(class: &mut ClassBuilder<'_>) -> Result<()>;
@ -88,17 +88,17 @@ pub trait Class: NativeObject + Sized {
/// This is automatically implemented, when a type implements `Class`.
pub trait ClassConstructor: Class {
/// The raw constructor that mathces the `NativeFunction` signature.
fn raw_constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value>
fn raw_constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Value>
where
Self: Sized;
}
impl<T: Class> ClassConstructor for T {
fn raw_constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value>
fn raw_constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Value>
where
Self: Sized,
{
let object_instance = Self::constructor(this, args, ctx)?;
let object_instance = Self::constructor(this, args, context)?;
this.set_data(ObjectData::NativeObject(Box::new(object_instance)));
Ok(this.clone())
}

24
boa/src/context.rs

@ -225,18 +225,22 @@ impl Default for Context {
impl Context {
/// Create a new `Context`.
#[inline]
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn realm(&self) -> &Realm {
&self.realm
}
#[inline]
pub fn realm_mut(&mut self) -> &mut Realm {
&mut self.realm
}
#[inline]
pub fn executor(&mut self) -> &mut Interpreter {
&mut self.executor
}
@ -249,11 +253,13 @@ impl Context {
/// A helper function for getting a mutable reference to the `console` object.
#[cfg(feature = "console")]
#[inline]
pub(crate) fn console_mut(&mut self) -> &mut Console {
&mut self.console
}
/// Sets up the default global objects within Global
#[inline]
fn create_intrinsics(&mut self) {
let _timer = BoaProfiler::global().start_event("create_intrinsics", "interpreter");
// Create intrinsics, add global objects here
@ -287,6 +293,7 @@ impl Context {
}
/// <https://tc39.es/ecma262/#sec-call>
#[inline]
pub(crate) fn call(&mut self, f: &Value, this: &Value, args: &[Value]) -> Result<Value> {
match *f {
Value::Object(ref object) => object.call(this, args, self),
@ -295,11 +302,13 @@ impl Context {
}
/// Return the global object.
#[inline]
pub fn global_object(&self) -> &Value {
&self.realm.global_obj
&self.realm().global_object
}
/// Constructs a `RangeError` with the specified message.
#[inline]
pub fn construct_range_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
@ -314,6 +323,7 @@ impl Context {
}
/// Throws a `RangeError` with the specified message.
#[inline]
pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
@ -322,6 +332,7 @@ impl Context {
}
/// Constructs a `TypeError` with the specified message.
#[inline]
pub fn construct_type_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
@ -336,6 +347,7 @@ impl Context {
}
/// Throws a `TypeError` with the specified message.
#[inline]
pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
@ -344,6 +356,7 @@ impl Context {
}
/// Constructs a `ReferenceError` with the specified message.
#[inline]
pub fn construct_reference_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
@ -357,6 +370,7 @@ impl Context {
}
/// Throws a `ReferenceError` with the specified message.
#[inline]
pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
@ -365,6 +379,7 @@ impl Context {
}
/// Constructs a `SyntaxError` with the specified message.
#[inline]
pub fn construct_syntax_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
@ -378,6 +393,7 @@ impl Context {
}
/// Throws a `SyntaxError` with the specified message.
#[inline]
pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
@ -494,6 +510,7 @@ impl Context {
}
/// Register a global function.
#[inline]
pub fn register_global_function(
&mut self,
name: &str,
@ -556,6 +573,7 @@ impl Context {
}
/// https://tc39.es/ecma262/#sec-hasproperty
#[inline]
pub(crate) fn has_property(&self, obj: &Value, key: &PropertyKey) -> bool {
if let Some(obj) = obj.as_object() {
obj.has_property(key)
@ -564,6 +582,7 @@ impl Context {
}
}
#[inline]
pub(crate) fn set_value(&mut self, node: &Node, value: Value) -> Result<Value> {
match node {
Node::Identifier(ref name) => {
@ -598,6 +617,7 @@ impl Context {
///
/// context.register_global_class::<MyClass>();
/// ```
#[inline]
pub fn register_global_class<T>(&mut self) -> Result<()>
where
T: Class,
@ -630,6 +650,7 @@ impl Context {
/// .build();
/// context.register_global_property("myObjectProperty", object, Attribute::all());
/// ```
#[inline]
pub fn register_global_property<K, V>(&mut self, key: K, value: V, attribute: Attribute)
where
K: Into<PropertyKey>,
@ -655,6 +676,7 @@ impl Context {
/// assert_eq!(value.as_number().unwrap(), 4.0);
/// ```
#[allow(clippy::unit_arg, clippy::drop_copy)]
#[inline]
pub fn eval(&mut self, src: &str) -> Result<Value> {
let main_timer = BoaProfiler::global().start_event("Main", "Main");

2
boa/src/environment/declarative_environment_record.rs

@ -10,9 +10,9 @@ use crate::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
gc::{Finalize, Trace},
Value,
};
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap;
/// Declarative Bindings have a few properties for book keeping purposes, such as mutability (const vs let).

2
boa/src/environment/environment_record_trait.rs

@ -10,9 +10,9 @@
//!
use crate::{
environment::lexical_environment::{Environment, EnvironmentType},
gc::{Finalize, Trace},
Value,
};
use gc::{Finalize, Trace};
use std::fmt::Debug;
/// <https://tc39.es/ecma262/#sec-environment-records>

4
boa/src/environment/function_environment_record.rs

@ -14,10 +14,10 @@ use crate::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
gc::{empty_trace, Finalize, Trace},
object::GcObject,
Value,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
use rustc_hash::FxHashMap;
/// Different binding status for `this`.
@ -33,7 +33,7 @@ pub enum BindingStatus {
}
unsafe impl Trace for BindingStatus {
unsafe_empty_trace!();
empty_trace!();
}
/// <https://tc39.es/ecma262/#table-16>

2
boa/src/environment/global_environment_record.rs

@ -14,10 +14,10 @@ use crate::{
lexical_environment::{Environment, EnvironmentType},
object_environment_record::ObjectEnvironmentRecord,
},
gc::{Finalize, Trace},
property::{Attribute, DataDescriptor},
Value,
};
use gc::{Finalize, Trace};
use rustc_hash::FxHashSet;
#[derive(Debug, Trace, Finalize, Clone)]

2
boa/src/environment/object_environment_record.rs

@ -11,10 +11,10 @@ use crate::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
gc::{Finalize, Trace},
property::{Attribute, DataDescriptor},
Value,
};
use gc::{Finalize, Trace};
#[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord {

2
boa/src/exec/mod.rs

@ -7,7 +7,7 @@ use crate::{Context, Result, Value};
pub trait Executable {
/// Runs this executable in the given context.
fn run(&self, interpreter: &mut Context) -> Result<Value>;
fn run(&self, context: &mut Context) -> Result<Value>;
}
#[derive(Debug, Eq, PartialEq)]

202
boa/src/exec/tests.rs

@ -118,7 +118,7 @@ fn object_field_set() {
#[test]
fn spread_with_arguments() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
const a = [1, "test", 3, 4];
@ -128,30 +128,30 @@ fn spread_with_arguments() {
var result = foo(...a);
"#;
forward(&mut engine, scenario);
let one = forward(&mut engine, "result[0]");
forward(&mut context, scenario);
let one = forward(&mut context, "result[0]");
assert_eq!(one, String::from("1"));
let two = forward(&mut engine, "result[1]");
let two = forward(&mut context, "result[1]");
assert_eq!(two, String::from("\"test\""));
let three = forward(&mut engine, "result[2]");
let three = forward(&mut context, "result[2]");
assert_eq!(three, String::from("3"));
let four = forward(&mut engine, "result[3]");
let four = forward(&mut context, "result[3]");
assert_eq!(four, String::from("4"));
}
#[test]
fn array_rest_with_arguments() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var b = [4, 5, 6]
var a = [1, 2, 3, ...b];
"#;
forward(&mut engine, scenario);
let one = forward(&mut engine, "a");
forward(&mut context, scenario);
let one = forward(&mut context, "a");
assert_eq!(one, String::from("[ 1, 2, 3, 4, 5, 6 ]"));
}
@ -722,7 +722,7 @@ mod in_operator {
#[test]
fn should_type_error_when_rhs_not_object() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var x = false;
@ -733,13 +733,13 @@ mod in_operator {
}
"#;
forward(&mut engine, scenario);
assert_eq!(forward(&mut engine, "x"), "true");
forward(&mut context, scenario);
assert_eq!(forward(&mut context, "x"), "true");
}
#[test]
fn should_set_this_value() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
function Foo() {
@ -749,37 +749,37 @@ mod in_operator {
var bar = new Foo();
"#;
forward(&mut engine, scenario);
assert_eq!(forward(&mut engine, "bar.a"), "\"a\"");
assert_eq!(forward(&mut engine, "bar.b"), "\"b\"");
forward(&mut context, scenario);
assert_eq!(forward(&mut context, "bar.a"), "\"a\"");
assert_eq!(forward(&mut context, "bar.b"), "\"b\"");
}
#[test]
fn should_type_error_when_new_is_not_constructor() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
const a = "";
new a();
"#;
let result = forward(&mut engine, scenario);
let result = forward(&mut context, scenario);
assert_eq!(result, "Uncaught \"TypeError\": \"a is not a constructor\"");
}
#[test]
fn new_instance_should_point_to_prototype() {
// A new instance should point to a prototype object created with the constructor function
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
function Foo() {}
var bar = new Foo();
"#;
forward(&mut engine, scenario);
let bar_val = forward_val(&mut engine, "bar").unwrap();
forward(&mut context, scenario);
let bar_val = forward_val(&mut context, "bar").unwrap();
let bar_obj = bar_val.as_object().unwrap();
let foo_val = forward_val(&mut engine, "Foo").unwrap();
let foo_val = forward_val(&mut context, "Foo").unwrap();
assert!(bar_obj
.prototype_instance()
.strict_equals(&foo_val.get_field("prototype")));
@ -897,98 +897,100 @@ fn function_decl_hoisting() {
#[test]
fn to_bigint() {
let mut engine = Context::new();
let mut context = Context::new();
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());
assert!(Value::null().to_bigint(&mut context).is_err());
assert!(Value::undefined().to_bigint(&mut context).is_err());
assert!(Value::integer(55).to_bigint(&mut context).is_ok());
assert!(Value::rational(10.0).to_bigint(&mut context).is_ok());
assert!(Value::string("100").to_bigint(&mut context).is_ok());
}
#[test]
fn to_index() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(Value::undefined().to_index(&mut engine).unwrap(), 0);
assert!(Value::integer(-1).to_index(&mut engine).is_err());
assert_eq!(Value::undefined().to_index(&mut context).unwrap(), 0);
assert!(Value::integer(-1).to_index(&mut context).is_err());
}
#[test]
fn to_integer() {
let mut engine = Context::new();
let mut context = Context::new();
assert!(Number::equal(
Value::number(f64::NAN).to_integer(&mut engine).unwrap(),
Value::number(f64::NAN).to_integer(&mut context).unwrap(),
0.0
));
assert!(Number::equal(
Value::number(f64::NEG_INFINITY)
.to_integer(&mut engine)
.to_integer(&mut context)
.unwrap(),
f64::NEG_INFINITY
));
assert!(Number::equal(
Value::number(f64::INFINITY)
.to_integer(&mut engine)
.to_integer(&mut context)
.unwrap(),
f64::INFINITY
));
assert!(Number::equal(
Value::number(0.0).to_integer(&mut engine).unwrap(),
Value::number(0.0).to_integer(&mut context).unwrap(),
0.0
));
let number = Value::number(-0.0).to_integer(&mut engine).unwrap();
let number = Value::number(-0.0).to_integer(&mut context).unwrap();
assert!(!number.is_sign_negative());
assert!(Number::equal(number, 0.0));
assert!(Number::equal(
Value::number(20.9).to_integer(&mut engine).unwrap(),
Value::number(20.9).to_integer(&mut context).unwrap(),
20.0
));
assert!(Number::equal(
Value::number(-20.9).to_integer(&mut engine).unwrap(),
Value::number(-20.9).to_integer(&mut context).unwrap(),
-20.0
));
}
#[test]
fn to_length() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(Value::number(f64::NAN).to_length(&mut engine).unwrap(), 0);
assert_eq!(Value::number(f64::NAN).to_length(&mut context).unwrap(), 0);
assert_eq!(
Value::number(f64::NEG_INFINITY)
.to_length(&mut engine)
.to_length(&mut context)
.unwrap(),
0
);
assert_eq!(
Value::number(f64::INFINITY).to_length(&mut engine).unwrap(),
Value::number(f64::INFINITY)
.to_length(&mut context)
.unwrap(),
Number::MAX_SAFE_INTEGER as usize
);
assert_eq!(Value::number(0.0).to_length(&mut engine).unwrap(), 0);
assert_eq!(Value::number(-0.0).to_length(&mut engine).unwrap(), 0);
assert_eq!(Value::number(20.9).to_length(&mut engine).unwrap(), 20);
assert_eq!(Value::number(-20.9).to_length(&mut engine).unwrap(), 0);
assert_eq!(Value::number(0.0).to_length(&mut context).unwrap(), 0);
assert_eq!(Value::number(-0.0).to_length(&mut context).unwrap(), 0);
assert_eq!(Value::number(20.9).to_length(&mut context).unwrap(), 20);
assert_eq!(Value::number(-20.9).to_length(&mut context).unwrap(), 0);
assert_eq!(
Value::number(100000000000.0)
.to_length(&mut engine)
.to_length(&mut context)
.unwrap() as u64,
100000000000
);
assert_eq!(
Value::number(4010101101.0).to_length(&mut engine).unwrap(),
Value::number(4010101101.0).to_length(&mut context).unwrap(),
4010101101
);
}
#[test]
fn to_int32() {
let mut engine = Context::new();
let mut context = Context::new();
macro_rules! check_to_int32 {
($from:expr => $to:expr) => {
assert_eq!(Value::from($from).to_i32(&mut engine).unwrap(), $to);
assert_eq!(Value::from($from).to_i32(&mut context).unwrap(), $to);
};
};
@ -1097,24 +1099,24 @@ fn to_int32() {
#[test]
fn to_string() {
let mut engine = Context::new();
let mut context = Context::new();
assert_eq!(Value::null().to_string(&mut engine).unwrap(), "null");
assert_eq!(Value::null().to_string(&mut context).unwrap(), "null");
assert_eq!(
Value::undefined().to_string(&mut engine).unwrap(),
Value::undefined().to_string(&mut context).unwrap(),
"undefined"
);
assert_eq!(Value::integer(55).to_string(&mut engine).unwrap(), "55");
assert_eq!(Value::rational(55.0).to_string(&mut engine).unwrap(), "55");
assert_eq!(Value::integer(55).to_string(&mut context).unwrap(), "55");
assert_eq!(Value::rational(55.0).to_string(&mut context).unwrap(), "55");
assert_eq!(
Value::string("hello").to_string(&mut engine).unwrap(),
Value::string("hello").to_string(&mut context).unwrap(),
"hello"
);
}
#[test]
fn calling_function_with_unspecified_arguments() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
function test(a, b) {
return b;
@ -1123,26 +1125,26 @@ fn calling_function_with_unspecified_arguments() {
test(10)
"#;
assert_eq!(forward(&mut engine, scenario), "undefined");
assert_eq!(forward(&mut context, scenario), "undefined");
}
#[test]
fn to_object() {
let mut engine = Context::new();
let mut context = Context::new();
assert!(Value::undefined()
.to_object(&mut engine)
.to_object(&mut context)
.unwrap_err()
.is_object());
assert!(Value::null()
.to_object(&mut engine)
.to_object(&mut context)
.unwrap_err()
.is_object());
}
#[test]
fn check_this_binding_in_object_literal() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
var foo = {
a: 3,
@ -1152,12 +1154,12 @@ fn check_this_binding_in_object_literal() {
foo.bar()
"#;
assert_eq!(forward(&mut engine, init), "8");
assert_eq!(forward(&mut context, init), "8");
}
#[test]
fn array_creation_benchmark() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
(function(){
let testArr = [];
@ -1169,12 +1171,12 @@ fn array_creation_benchmark() {
})();
"#;
assert_eq!(forward(&mut engine, init), "[ \"p0\", \"p1\", \"p2\", \"p3\", \"p4\", \"p5\", \"p6\", \"p7\", \"p8\", \"p9\", \"p10\", \"p11\", \"p12\", \"p13\", \"p14\", \"p15\", \"p16\", \"p17\", \"p18\", \"p19\", \"p20\", \"p21\", \"p22\", \"p23\", \"p24\", \"p25\", \"p26\", \"p27\", \"p28\", \"p29\", \"p30\", \"p31\", \"p32\", \"p33\", \"p34\", \"p35\", \"p36\", \"p37\", \"p38\", \"p39\", \"p40\", \"p41\", \"p42\", \"p43\", \"p44\", \"p45\", \"p46\", \"p47\", \"p48\", \"p49\", \"p50\", \"p51\", \"p52\", \"p53\", \"p54\", \"p55\", \"p56\", \"p57\", \"p58\", \"p59\", \"p60\", \"p61\", \"p62\", \"p63\", \"p64\", \"p65\", \"p66\", \"p67\", \"p68\", \"p69\", \"p70\", \"p71\", \"p72\", \"p73\", \"p74\", \"p75\", \"p76\", \"p77\", \"p78\", \"p79\", \"p80\", \"p81\", \"p82\", \"p83\", \"p84\", \"p85\", \"p86\", \"p87\", \"p88\", \"p89\", \"p90\", \"p91\", \"p92\", \"p93\", \"p94\", \"p95\", \"p96\", \"p97\", \"p98\", \"p99\", \"p100\", \"p101\", \"p102\", \"p103\", \"p104\", \"p105\", \"p106\", \"p107\", \"p108\", \"p109\", \"p110\", \"p111\", \"p112\", \"p113\", \"p114\", \"p115\", \"p116\", \"p117\", \"p118\", \"p119\", \"p120\", \"p121\", \"p122\", \"p123\", \"p124\", \"p125\", \"p126\", \"p127\", \"p128\", \"p129\", \"p130\", \"p131\", \"p132\", \"p133\", \"p134\", \"p135\", \"p136\", \"p137\", \"p138\", \"p139\", \"p140\", \"p141\", \"p142\", \"p143\", \"p144\", \"p145\", \"p146\", \"p147\", \"p148\", \"p149\", \"p150\", \"p151\", \"p152\", \"p153\", \"p154\", \"p155\", \"p156\", \"p157\", \"p158\", \"p159\", \"p160\", \"p161\", \"p162\", \"p163\", \"p164\", \"p165\", \"p166\", \"p167\", \"p168\", \"p169\", \"p170\", \"p171\", \"p172\", \"p173\", \"p174\", \"p175\", \"p176\", \"p177\", \"p178\", \"p179\", \"p180\", \"p181\", \"p182\", \"p183\", \"p184\", \"p185\", \"p186\", \"p187\", \"p188\", \"p189\", \"p190\", \"p191\", \"p192\", \"p193\", \"p194\", \"p195\", \"p196\", \"p197\", \"p198\", \"p199\", \"p200\", \"p201\", \"p202\", \"p203\", \"p204\", \"p205\", \"p206\", \"p207\", \"p208\", \"p209\", \"p210\", \"p211\", \"p212\", \"p213\", \"p214\", \"p215\", \"p216\", \"p217\", \"p218\", \"p219\", \"p220\", \"p221\", \"p222\", \"p223\", \"p224\", \"p225\", \"p226\", \"p227\", \"p228\", \"p229\", \"p230\", \"p231\", \"p232\", \"p233\", \"p234\", \"p235\", \"p236\", \"p237\", \"p238\", \"p239\", \"p240\", \"p241\", \"p242\", \"p243\", \"p244\", \"p245\", \"p246\", \"p247\", \"p248\", \"p249\", \"p250\", \"p251\", \"p252\", \"p253\", \"p254\", \"p255\", \"p256\", \"p257\", \"p258\", \"p259\", \"p260\", \"p261\", \"p262\", \"p263\", \"p264\", \"p265\", \"p266\", \"p267\", \"p268\", \"p269\", \"p270\", \"p271\", \"p272\", \"p273\", \"p274\", \"p275\", \"p276\", \"p277\", \"p278\", \"p279\", \"p280\", \"p281\", \"p282\", \"p283\", \"p284\", \"p285\", \"p286\", \"p287\", \"p288\", \"p289\", \"p290\", \"p291\", \"p292\", \"p293\", \"p294\", \"p295\", \"p296\", \"p297\", \"p298\", \"p299\", \"p300\", \"p301\", \"p302\", \"p303\", \"p304\", \"p305\", \"p306\", \"p307\", \"p308\", \"p309\", \"p310\", \"p311\", \"p312\", \"p313\", \"p314\", \"p315\", \"p316\", \"p317\", \"p318\", \"p319\", \"p320\", \"p321\", \"p322\", \"p323\", \"p324\", \"p325\", \"p326\", \"p327\", \"p328\", \"p329\", \"p330\", \"p331\", \"p332\", \"p333\", \"p334\", \"p335\", \"p336\", \"p337\", \"p338\", \"p339\", \"p340\", \"p341\", \"p342\", \"p343\", \"p344\", \"p345\", \"p346\", \"p347\", \"p348\", \"p349\", \"p350\", \"p351\", \"p352\", \"p353\", \"p354\", \"p355\", \"p356\", \"p357\", \"p358\", \"p359\", \"p360\", \"p361\", \"p362\", \"p363\", \"p364\", \"p365\", \"p366\", \"p367\", \"p368\", \"p369\", \"p370\", \"p371\", \"p372\", \"p373\", \"p374\", \"p375\", \"p376\", \"p377\", \"p378\", \"p379\", \"p380\", \"p381\", \"p382\", \"p383\", \"p384\", \"p385\", \"p386\", \"p387\", \"p388\", \"p389\", \"p390\", \"p391\", \"p392\", \"p393\", \"p394\", \"p395\", \"p396\", \"p397\", \"p398\", \"p399\", \"p400\", \"p401\", \"p402\", \"p403\", \"p404\", \"p405\", \"p406\", \"p407\", \"p408\", \"p409\", \"p410\", \"p411\", \"p412\", \"p413\", \"p414\", \"p415\", \"p416\", \"p417\", \"p418\", \"p419\", \"p420\", \"p421\", \"p422\", \"p423\", \"p424\", \"p425\", \"p426\", \"p427\", \"p428\", \"p429\", \"p430\", \"p431\", \"p432\", \"p433\", \"p434\", \"p435\", \"p436\", \"p437\", \"p438\", \"p439\", \"p440\", \"p441\", \"p442\", \"p443\", \"p444\", \"p445\", \"p446\", \"p447\", \"p448\", \"p449\", \"p450\", \"p451\", \"p452\", \"p453\", \"p454\", \"p455\", \"p456\", \"p457\", \"p458\", \"p459\", \"p460\", \"p461\", \"p462\", \"p463\", \"p464\", \"p465\", \"p466\", \"p467\", \"p468\", \"p469\", \"p470\", \"p471\", \"p472\", \"p473\", \"p474\", \"p475\", \"p476\", \"p477\", \"p478\", \"p479\", \"p480\", \"p481\", \"p482\", \"p483\", \"p484\", \"p485\", \"p486\", \"p487\", \"p488\", \"p489\", \"p490\", \"p491\", \"p492\", \"p493\", \"p494\", \"p495\", \"p496\", \"p497\", \"p498\", \"p499\", \"p500\" ]");
assert_eq!(forward(&mut context, init), "[ \"p0\", \"p1\", \"p2\", \"p3\", \"p4\", \"p5\", \"p6\", \"p7\", \"p8\", \"p9\", \"p10\", \"p11\", \"p12\", \"p13\", \"p14\", \"p15\", \"p16\", \"p17\", \"p18\", \"p19\", \"p20\", \"p21\", \"p22\", \"p23\", \"p24\", \"p25\", \"p26\", \"p27\", \"p28\", \"p29\", \"p30\", \"p31\", \"p32\", \"p33\", \"p34\", \"p35\", \"p36\", \"p37\", \"p38\", \"p39\", \"p40\", \"p41\", \"p42\", \"p43\", \"p44\", \"p45\", \"p46\", \"p47\", \"p48\", \"p49\", \"p50\", \"p51\", \"p52\", \"p53\", \"p54\", \"p55\", \"p56\", \"p57\", \"p58\", \"p59\", \"p60\", \"p61\", \"p62\", \"p63\", \"p64\", \"p65\", \"p66\", \"p67\", \"p68\", \"p69\", \"p70\", \"p71\", \"p72\", \"p73\", \"p74\", \"p75\", \"p76\", \"p77\", \"p78\", \"p79\", \"p80\", \"p81\", \"p82\", \"p83\", \"p84\", \"p85\", \"p86\", \"p87\", \"p88\", \"p89\", \"p90\", \"p91\", \"p92\", \"p93\", \"p94\", \"p95\", \"p96\", \"p97\", \"p98\", \"p99\", \"p100\", \"p101\", \"p102\", \"p103\", \"p104\", \"p105\", \"p106\", \"p107\", \"p108\", \"p109\", \"p110\", \"p111\", \"p112\", \"p113\", \"p114\", \"p115\", \"p116\", \"p117\", \"p118\", \"p119\", \"p120\", \"p121\", \"p122\", \"p123\", \"p124\", \"p125\", \"p126\", \"p127\", \"p128\", \"p129\", \"p130\", \"p131\", \"p132\", \"p133\", \"p134\", \"p135\", \"p136\", \"p137\", \"p138\", \"p139\", \"p140\", \"p141\", \"p142\", \"p143\", \"p144\", \"p145\", \"p146\", \"p147\", \"p148\", \"p149\", \"p150\", \"p151\", \"p152\", \"p153\", \"p154\", \"p155\", \"p156\", \"p157\", \"p158\", \"p159\", \"p160\", \"p161\", \"p162\", \"p163\", \"p164\", \"p165\", \"p166\", \"p167\", \"p168\", \"p169\", \"p170\", \"p171\", \"p172\", \"p173\", \"p174\", \"p175\", \"p176\", \"p177\", \"p178\", \"p179\", \"p180\", \"p181\", \"p182\", \"p183\", \"p184\", \"p185\", \"p186\", \"p187\", \"p188\", \"p189\", \"p190\", \"p191\", \"p192\", \"p193\", \"p194\", \"p195\", \"p196\", \"p197\", \"p198\", \"p199\", \"p200\", \"p201\", \"p202\", \"p203\", \"p204\", \"p205\", \"p206\", \"p207\", \"p208\", \"p209\", \"p210\", \"p211\", \"p212\", \"p213\", \"p214\", \"p215\", \"p216\", \"p217\", \"p218\", \"p219\", \"p220\", \"p221\", \"p222\", \"p223\", \"p224\", \"p225\", \"p226\", \"p227\", \"p228\", \"p229\", \"p230\", \"p231\", \"p232\", \"p233\", \"p234\", \"p235\", \"p236\", \"p237\", \"p238\", \"p239\", \"p240\", \"p241\", \"p242\", \"p243\", \"p244\", \"p245\", \"p246\", \"p247\", \"p248\", \"p249\", \"p250\", \"p251\", \"p252\", \"p253\", \"p254\", \"p255\", \"p256\", \"p257\", \"p258\", \"p259\", \"p260\", \"p261\", \"p262\", \"p263\", \"p264\", \"p265\", \"p266\", \"p267\", \"p268\", \"p269\", \"p270\", \"p271\", \"p272\", \"p273\", \"p274\", \"p275\", \"p276\", \"p277\", \"p278\", \"p279\", \"p280\", \"p281\", \"p282\", \"p283\", \"p284\", \"p285\", \"p286\", \"p287\", \"p288\", \"p289\", \"p290\", \"p291\", \"p292\", \"p293\", \"p294\", \"p295\", \"p296\", \"p297\", \"p298\", \"p299\", \"p300\", \"p301\", \"p302\", \"p303\", \"p304\", \"p305\", \"p306\", \"p307\", \"p308\", \"p309\", \"p310\", \"p311\", \"p312\", \"p313\", \"p314\", \"p315\", \"p316\", \"p317\", \"p318\", \"p319\", \"p320\", \"p321\", \"p322\", \"p323\", \"p324\", \"p325\", \"p326\", \"p327\", \"p328\", \"p329\", \"p330\", \"p331\", \"p332\", \"p333\", \"p334\", \"p335\", \"p336\", \"p337\", \"p338\", \"p339\", \"p340\", \"p341\", \"p342\", \"p343\", \"p344\", \"p345\", \"p346\", \"p347\", \"p348\", \"p349\", \"p350\", \"p351\", \"p352\", \"p353\", \"p354\", \"p355\", \"p356\", \"p357\", \"p358\", \"p359\", \"p360\", \"p361\", \"p362\", \"p363\", \"p364\", \"p365\", \"p366\", \"p367\", \"p368\", \"p369\", \"p370\", \"p371\", \"p372\", \"p373\", \"p374\", \"p375\", \"p376\", \"p377\", \"p378\", \"p379\", \"p380\", \"p381\", \"p382\", \"p383\", \"p384\", \"p385\", \"p386\", \"p387\", \"p388\", \"p389\", \"p390\", \"p391\", \"p392\", \"p393\", \"p394\", \"p395\", \"p396\", \"p397\", \"p398\", \"p399\", \"p400\", \"p401\", \"p402\", \"p403\", \"p404\", \"p405\", \"p406\", \"p407\", \"p408\", \"p409\", \"p410\", \"p411\", \"p412\", \"p413\", \"p414\", \"p415\", \"p416\", \"p417\", \"p418\", \"p419\", \"p420\", \"p421\", \"p422\", \"p423\", \"p424\", \"p425\", \"p426\", \"p427\", \"p428\", \"p429\", \"p430\", \"p431\", \"p432\", \"p433\", \"p434\", \"p435\", \"p436\", \"p437\", \"p438\", \"p439\", \"p440\", \"p441\", \"p442\", \"p443\", \"p444\", \"p445\", \"p446\", \"p447\", \"p448\", \"p449\", \"p450\", \"p451\", \"p452\", \"p453\", \"p454\", \"p455\", \"p456\", \"p457\", \"p458\", \"p459\", \"p460\", \"p461\", \"p462\", \"p463\", \"p464\", \"p465\", \"p466\", \"p467\", \"p468\", \"p469\", \"p470\", \"p471\", \"p472\", \"p473\", \"p474\", \"p475\", \"p476\", \"p477\", \"p478\", \"p479\", \"p480\", \"p481\", \"p482\", \"p483\", \"p484\", \"p485\", \"p486\", \"p487\", \"p488\", \"p489\", \"p490\", \"p491\", \"p492\", \"p493\", \"p494\", \"p495\", \"p496\", \"p497\", \"p498\", \"p499\", \"p500\" ]");
}
#[test]
fn array_pop_benchmark() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
(function(){
let testArray = [83, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32,
@ -1202,12 +1204,12 @@ fn array_pop_benchmark() {
})();
"#;
assert_eq!(forward(&mut engine, init), "[]");
assert_eq!(forward(&mut context, init), "[]");
}
#[test]
fn number_object_access_benchmark() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
new Number(
new Number(
@ -1218,17 +1220,17 @@ fn number_object_access_benchmark() {
)
"#;
assert!(forward_val(&mut engine, init).is_ok());
assert!(forward_val(&mut context, init).is_ok());
}
#[test]
fn not_a_function() {
let mut engine = Context::new();
let mut context = Context::new();
let init = r#"
let a = {};
let b = true;
"#;
forward(&mut engine, init);
forward(&mut context, init);
let scenario = r#"
try {
a();
@ -1237,7 +1239,7 @@ fn not_a_function() {
}
"#;
assert_eq!(
forward(&mut engine, scenario),
forward(&mut context, scenario),
"\"TypeError: not a function\""
);
let scenario = r#"
@ -1248,7 +1250,7 @@ fn not_a_function() {
}
"#;
assert_eq!(
forward(&mut engine, scenario),
forward(&mut context, scenario),
"\"TypeError: not a function\""
);
let scenario = r#"
@ -1259,7 +1261,7 @@ fn not_a_function() {
}
"#;
assert_eq!(
forward(&mut engine, scenario),
forward(&mut context, scenario),
"\"TypeError: not a function\""
);
}
@ -1287,7 +1289,7 @@ fn comma_operator() {
fn assignment_to_non_assignable() {
// Relates to the behaviour described at
// https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors
let mut engine = Context::new();
let mut context = Context::new();
// Tests all assignment operators as per [spec] and [mdn]
//
@ -1298,7 +1300,7 @@ fn assignment_to_non_assignable() {
];
for case in test_cases.iter() {
let string = forward(&mut engine, case);
let string = forward(&mut context, case);
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
assert!(string.contains("1:3"));
@ -1309,12 +1311,12 @@ fn assignment_to_non_assignable() {
fn multicharacter_assignment_to_non_assignable() {
// Relates to the behaviour described at
// https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors
let mut engine = Context::new();
let mut context = Context::new();
let test_cases = ["3 **= 5", "3 <<= 5", "3 >>= 5"];
for case in test_cases.iter() {
let string = dbg!(forward(&mut engine, case));
let string = dbg!(forward(&mut context, case));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
assert!(string.contains("1:3"));
@ -1324,13 +1326,13 @@ fn multicharacter_assignment_to_non_assignable() {
#[test]
#[ignore]
fn multicharacter_bitwise_assignment_to_non_assignable() {
let mut engine = Context::new();
let mut context = Context::new();
// Disabled - awaiting implementation.
let test_cases = ["3 >>>= 5", "3 &&= 5", "3 ||= 5", "3 ??= 5"];
for case in test_cases.iter() {
let string = dbg!(forward(&mut engine, case));
let string = dbg!(forward(&mut context, case));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
assert!(string.contains("1:3"));
@ -1339,22 +1341,22 @@ fn multicharacter_bitwise_assignment_to_non_assignable() {
#[test]
fn assign_to_array_decl() {
let mut engine = Context::new();
let mut context = Context::new();
assert!(forward(&mut engine, "[1] = [2]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut engine, "[3, 5] = [7, 8]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut engine, "[6, 8] = [2]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut engine, "[6] = [2, 9]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut context, "[1] = [2]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut context, "[3, 5] = [7, 8]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut context, "[6, 8] = [2]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut context, "[6] = [2, 9]").starts_with("Uncaught \"SyntaxError\": "));
}
#[test]
fn assign_to_object_decl() {
let mut engine = Context::new();
let mut context = Context::new();
const ERR_MSG: &str =
"Uncaught \"SyntaxError\": \"unexpected token '=', primary expression at line 1, col 8\"";
assert_eq!(forward(&mut engine, "{a: 3} = {a: 5};"), ERR_MSG);
assert_eq!(forward(&mut context, "{a: 3} = {a: 5};"), ERR_MSG);
}
#[test]
@ -1407,9 +1409,9 @@ fn test_strict_mode_octal() {
var n = 023;
"#;
let mut engine = Context::new();
let mut context = Context::new();
let string = dbg!(forward(&mut engine, scenario));
let string = dbg!(forward(&mut context, scenario));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
}
@ -1428,9 +1430,9 @@ fn test_strict_mode_with() {
}
"#;
let mut engine = Context::new();
let mut context = Context::new();
let string = dbg!(forward(&mut engine, scenario));
let string = dbg!(forward(&mut context, scenario));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
}
@ -1446,9 +1448,9 @@ fn test_strict_mode_delete() {
delete x;
"#;
let mut engine = Context::new();
let mut context = Context::new();
let string = dbg!(forward(&mut engine, scenario));
let string = dbg!(forward(&mut context, scenario));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
}
@ -1473,10 +1475,10 @@ fn test_strict_mode_reserved_name() {
];
for case in test_cases.iter() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = format!("'use strict'; \n {}", case);
let string = dbg!(forward(&mut engine, &scenario));
let string = dbg!(forward(&mut context, &scenario));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
}
@ -1494,9 +1496,9 @@ fn test_strict_mode_func_decl_in_block() {
if (a < b) { function f() {} }
"#;
let mut engine = Context::new();
let mut context = Context::new();
let string = dbg!(forward(&mut engine, scenario));
let string = dbg!(forward(&mut context, scenario));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
}
@ -1511,9 +1513,9 @@ fn test_strict_mode_dup_func_parameters() {
function f(a, b, b) {}
"#;
let mut engine = Context::new();
let mut context = Context::new();
let string = dbg!(forward(&mut engine, scenario));
let string = dbg!(forward(&mut context, scenario));
assert!(string.starts_with("Uncaught \"SyntaxError\": "));
}

12
boa/src/lib.rs

@ -85,21 +85,21 @@ pub fn parse(src: &str, strict_mode: bool) -> StdResult<StatementList, ParseErro
/// Execute the code using an existing Context
/// The str is consumed and the state of the Context is changed
#[cfg(test)]
pub(crate) fn forward(engine: &mut Context, src: &str) -> String {
pub(crate) fn forward(context: &mut Context, src: &str) -> String {
// Setup executor
let expr = match parse(src, false) {
Ok(res) => res,
Err(e) => {
return format!(
"Uncaught {}",
engine
context
.throw_syntax_error(e.to_string())
.expect_err("interpreter.throw_syntax_error() did not return an error")
.display()
);
}
};
expr.run(engine).map_or_else(
expr.run(context).map_or_else(
|e| format!("Uncaught {}", e.display()),
|v| v.display().to_string(),
)
@ -111,16 +111,16 @@ pub(crate) fn forward(engine: &mut Context, src: &str) -> String {
/// If the interpreter fails parsing an error value is returned instead (error object)
#[allow(clippy::unit_arg, clippy::drop_copy)]
#[cfg(test)]
pub(crate) fn forward_val(engine: &mut Context, src: &str) -> Result<Value> {
pub(crate) fn forward_val(context: &mut Context, src: &str) -> Result<Value> {
let main_timer = BoaProfiler::global().start_event("Main", "Main");
// Setup executor
let result = parse(src, false)
.map_err(|e| {
engine
context
.throw_syntax_error(e.to_string())
.expect_err("interpreter.throw_syntax_error() did not return an error")
})
.and_then(|expr| expr.run(engine));
.and_then(|expr| expr.run(context));
// The main_timer needs to be dropped before the BoaProfiler is.
drop(main_timer);

46
boa/src/object/gcobject.rs

@ -114,7 +114,7 @@ impl GcObject {
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
#[track_caller]
pub fn call(&self, this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub fn call(&self, this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let this_function_object = self.clone();
let f_body = if let Some(function) = self.borrow().as_function() {
if function.is_callable() {
@ -150,7 +150,7 @@ impl GcObject {
for (i, param) in params.iter().enumerate() {
// Rest Parameters
if param.is_rest_param() {
function.add_rest_param(param, i, args, ctx, &local_env);
function.add_rest_param(param, i, args, context, &local_env);
break;
}
@ -167,23 +167,23 @@ impl GcObject {
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
ctx.realm_mut().environment.push(local_env);
context.realm_mut().environment.push(local_env);
FunctionBody::Ordinary(body.clone())
}
}
} else {
return ctx.throw_type_error("function object is not callable");
return context.throw_type_error("function object is not callable");
}
} else {
return ctx.throw_type_error("not a function");
return context.throw_type_error("not a function");
};
match f_body {
FunctionBody::BuiltIn(func) => func(this, args, ctx),
FunctionBody::BuiltIn(func) => func(this, args, context),
FunctionBody::Ordinary(body) => {
let result = body.run(ctx);
ctx.realm_mut().environment.pop();
let result = body.run(context);
context.realm_mut().environment.pop();
result
}
@ -197,7 +197,7 @@ impl GcObject {
/// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
#[track_caller]
pub fn construct(&self, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub fn construct(&self, args: &[Value], context: &mut Context) -> Result<Value> {
let this: Value = Object::create(self.get(&PROTOTYPE.into())).into();
let this_function_object = self.clone();
@ -231,7 +231,7 @@ impl GcObject {
for (i, param) in params.iter().enumerate() {
// Rest Parameters
if param.is_rest_param() {
function.add_rest_param(param, i, args, ctx, &local_env);
function.add_rest_param(param, i, args, context, &local_env);
break;
}
@ -248,29 +248,29 @@ impl GcObject {
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
ctx.realm_mut().environment.push(local_env);
context.realm_mut().environment.push(local_env);
FunctionBody::Ordinary(body.clone())
}
}
} else {
let name = this.get_field("name").display().to_string();
return ctx.throw_type_error(format!("{} is not a constructor", name));
return context.throw_type_error(format!("{} is not a constructor", name));
}
} else {
return ctx.throw_type_error("not a function");
return context.throw_type_error("not a function");
};
match body {
FunctionBody::BuiltIn(function) => {
function(&this, args, ctx)?;
function(&this, args, context)?;
Ok(this)
}
FunctionBody::Ordinary(body) => {
let _ = body.run(ctx);
let _ = body.run(context);
// local_env gets dropped here, its no longer needed
let binding = ctx.realm_mut().environment.get_this_binding();
let binding = context.realm_mut().environment.get_this_binding();
Ok(binding)
}
}
@ -296,7 +296,7 @@ impl GcObject {
/// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive
pub(crate) fn ordinary_to_primitive(
&self,
interpreter: &mut Context,
context: &mut Context,
hint: PreferredType,
) -> Result<Value> {
// 1. Assert: Type(O) is Object.
@ -336,7 +336,7 @@ impl GcObject {
// b. If IsCallable(method) is true, then
if method.is_function() {
// i. Let result be ? Call(method, O).
let result = interpreter.call(&method, &this, &[])?;
let result = context.call(&method, &this, &[])?;
// ii. If Type(result) is not Object, return result.
if !result.is_object() {
return Ok(result);
@ -345,14 +345,14 @@ impl GcObject {
}
// 6. Throw a TypeError exception.
interpreter.throw_type_error("cannot convert object to primitive value")
context.throw_type_error("cannot convert object to primitive value")
}
/// Converts an object to JSON, checking for reference cycles and throwing a TypeError if one is found
pub(crate) fn to_json(&self, interpreter: &mut Context) -> Result<JSONValue> {
pub(crate) fn to_json(&self, context: &mut Context) -> Result<JSONValue> {
let rec_limiter = RecursionLimiter::new(self);
if rec_limiter.live {
Err(interpreter.construct_type_error("cyclic object value"))
Err(context.construct_type_error("cyclic object value"))
} else if self.is_array() {
let mut keys: Vec<u32> = self.borrow().index_property_keys().cloned().collect();
keys.sort_unstable();
@ -363,7 +363,7 @@ impl GcObject {
if value.is_undefined() || value.is_function() || value.is_symbol() {
arr.push(JSONValue::Null);
} else {
arr.push(value.to_json(interpreter)?);
arr.push(value.to_json(context)?);
}
}
Ok(JSONValue::Array(arr))
@ -374,7 +374,7 @@ impl GcObject {
let key = k.clone();
let value = this.get_field(k.to_string());
if !value.is_undefined() && !value.is_function() && !value.is_symbol() {
new_obj.insert(key.to_string(), value.to_json(interpreter)?);
new_obj.insert(key.to_string(), value.to_json(context)?);
}
}
Ok(JSONValue::Object(new_obj))

6
boa/src/object/internal_methods.rs

@ -252,8 +252,8 @@ impl GcObject {
///
/// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties
#[inline]
pub fn define_properties(&mut self, props: Value, ctx: &mut Context) -> Result<()> {
let props = props.to_object(ctx)?;
pub fn define_properties(&mut self, props: Value, context: &mut Context) -> Result<()> {
let props = props.to_object(context)?;
let keys = props.own_property_keys();
let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new();
@ -261,7 +261,7 @@ impl GcObject {
if let Some(prop_desc) = props.get_own_property(&next_key) {
if prop_desc.enumerable() {
let desc_obj = props.get(&next_key);
let desc = desc_obj.to_property_descriptor(ctx)?;
let desc = desc_obj.to_property_descriptor(context)?;
descriptors.push((next_key, desc));
}
}

4
boa/src/property/attribute/mod.rs

@ -1,7 +1,7 @@
//! This module implements the `Attribute` struct which contains the attibutes for property descriptors.
use crate::gc::{empty_trace, Finalize, Trace};
use bitflags::bitflags;
use gc::{unsafe_empty_trace, Finalize, Trace};
#[cfg(test)]
mod tests;
@ -44,7 +44,7 @@ bitflags! {
// SAFETY: The `Attribute` struct only contains an `u8`
// and therefore it should be safe to implement an empty trace.
unsafe impl Trace for Attribute {
unsafe_empty_trace!();
empty_trace!();
}
impl Attribute {

4
boa/src/realm.rs

@ -21,7 +21,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
/// In the specification these are called Realm Records.
#[derive(Debug)]
pub struct Realm {
pub global_obj: Value,
pub global_object: Value,
pub global_env: Gc<GcCell<GlobalEnvironmentRecord>>,
pub environment: LexicalEnvironment,
}
@ -40,7 +40,7 @@ impl Realm {
let global_env = new_global_environment(global.clone(), global.clone());
Self {
global_obj: global.clone(),
global_object: global.clone(),
global_env,
environment: LexicalEnvironment::new(global),
}

6
boa/src/syntax/ast/constant.rs

@ -7,8 +7,10 @@
//! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
use crate::builtins::bigint::BigInt;
use gc::{Finalize, Trace};
use crate::{
builtins::bigint::BigInt,
gc::{Finalize, Trace},
};
use std::fmt::{Display, Formatter, Result};
#[cfg(feature = "serde")]

14
boa/src/syntax/ast/node/array/mod.rs

@ -4,9 +4,9 @@ use super::{join_nodes, Node};
use crate::{
builtins::{iterable, Array},
exec::Executable,
gc::{Finalize, Trace},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -36,19 +36,19 @@ pub struct ArrayDecl {
}
impl Executable for ArrayDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec");
let array = Array::new_array(interpreter)?;
let array = Array::new_array(context)?;
let mut elements = Vec::new();
for elem in self.as_ref() {
if let Node::Spread(ref x) = elem {
let val = x.run(interpreter)?;
let iterator_record = iterable::get_iterator(interpreter, val)?;
let val = x.run(context)?;
let iterator_record = iterable::get_iterator(context, val)?;
// TODO after proper internal Array representation as per https://github.com/boa-dev/boa/pull/811#discussion_r502460858
// next_index variable should be utilized here as per https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
// let mut next_index = 0;
loop {
let next = iterator_record.next(interpreter)?;
let next = iterator_record.next(context)?;
if next.is_done() {
break;
}
@ -57,7 +57,7 @@ impl Executable for ArrayDecl {
elements.push(next_value.clone());
}
} else {
elements.push(elem.run(interpreter)?);
elements.push(elem.run(context)?);
}
}

18
boa/src/syntax/ast/node/block/mod.rs

@ -2,10 +2,12 @@
use super::{Node, StatementList};
use crate::{
environment::lexical_environment::new_declarative_environment, exec::Executable,
exec::InterpreterState, BoaProfiler, Context, Result, Value,
environment::lexical_environment::new_declarative_environment,
exec::Executable,
exec::InterpreterState,
gc::{Finalize, Trace},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -49,10 +51,10 @@ impl Block {
}
impl Executable for Block {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Block", "exec");
{
let env = &mut interpreter.realm_mut().environment;
let env = &mut context.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
@ -62,9 +64,9 @@ impl Executable for Block {
// The return value is uninitialized, which means it defaults to Value::Undefined
let mut obj = Value::default();
for statement in self.statements() {
obj = statement.run(interpreter)?;
obj = statement.run(context)?;
match interpreter.executor().get_current_state() {
match context.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
break;
@ -86,7 +88,7 @@ impl Executable for Block {
}
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
let _ = context.realm_mut().environment.pop();
Ok(obj)
}

12
boa/src/syntax/ast/node/break_node/mod.rs

@ -1,6 +1,10 @@
use super::Node;
use crate::{exec::Executable, exec::InterpreterState, Context, Result, Value};
use gc::{Finalize, Trace};
use crate::{
exec::Executable,
exec::InterpreterState,
gc::{Finalize, Trace},
Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "serde")]
@ -48,8 +52,8 @@ impl Break {
}
impl Executable for Break {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
fn run(&self, context: &mut Context) -> Result<Value> {
context
.executor()
.set_current_state(InterpreterState::Break(self.label().map(Box::from)));

6
boa/src/syntax/ast/node/break_node/tests.rs

@ -6,14 +6,14 @@ use crate::{
#[test]
fn check_post_state() {
let mut engine = Context::new();
let mut context = Context::new();
let brk: Break = Break::new("label");
brk.run(&mut engine).unwrap();
brk.run(&mut context).unwrap();
assert_eq!(
engine.executor().get_current_state(),
context.executor().get_current_state(),
&InterpreterState::Break(Some("label".into()))
);
}

32
boa/src/syntax/ast/node/call/mod.rs

@ -1,11 +1,11 @@
use crate::{
exec::Executable,
exec::InterpreterState,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, Node},
value::{Type, Value},
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -57,45 +57,39 @@ impl Call {
}
impl Executable for Call {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Call", "exec");
let (this, func) = match self.expr() {
Node::GetConstField(ref get_const_field) => {
let mut obj = get_const_field.obj().run(interpreter)?;
let mut obj = get_const_field.obj().run(context)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
obj = Value::Object(obj.to_object(context)?);
}
(obj.clone(), obj.get_field(get_const_field.field()))
}
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(interpreter)?;
let field = get_field.field().run(interpreter)?;
(
obj.clone(),
obj.get_field(field.to_property_key(interpreter)?),
)
let obj = get_field.obj().run(context)?;
let field = get_field.field().run(context)?;
(obj.clone(), obj.get_field(field.to_property_key(context)?))
}
_ => (
interpreter.realm().global_obj.clone(),
self.expr().run(interpreter)?,
), // 'this' binding should come from the function's self-contained environment
_ => (context.global_object().clone(), self.expr().run(context)?), // 'this' binding should come from the function's self-contained environment
};
let mut v_args = Vec::with_capacity(self.args().len());
for arg in self.args() {
if let Node::Spread(ref x) = arg {
let val = x.run(interpreter)?;
let mut vals = interpreter.extract_array_properties(&val).unwrap();
let val = x.run(context)?;
let mut vals = context.extract_array_properties(&val).unwrap();
v_args.append(&mut vals);
break; // after spread we don't accept any new arguments
}
v_args.push(arg.run(interpreter)?);
v_args.push(arg.run(context)?);
}
// execute the function call itself
let fnct_result = interpreter.call(&func, &this, &v_args);
let fnct_result = context.call(&func, &this, &v_args);
// unset the early return flag
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);

16
boa/src/syntax/ast/node/conditional/conditional_op/mod.rs

@ -1,5 +1,9 @@
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "serde")]
@ -56,11 +60,11 @@ impl ConditionalOp {
}
impl Executable for ConditionalOp {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.if_true().run(interpreter)?
fn run(&self, context: &mut Context) -> Result<Value> {
Ok(if self.cond().run(context)?.to_boolean() {
self.if_true().run(context)?
} else {
self.if_false().run(interpreter)?
self.if_false().run(context)?
})
}
}

16
boa/src/syntax/ast/node/conditional/if_node/mod.rs

@ -1,5 +1,9 @@
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "serde")]
@ -75,11 +79,11 @@ impl If {
}
impl Executable for If {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.body().run(interpreter)?
fn run(&self, context: &mut Context) -> Result<Value> {
Ok(if self.cond().run(context)?.to_boolean() {
self.body().run(context)?
} else if let Some(ref else_e) = self.else_node() {
else_e.run(interpreter)?
else_e.run(context)?
} else {
Value::undefined()
})

6
boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs

@ -1,10 +1,10 @@
use crate::{
builtins::function::FunctionFlags,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -67,8 +67,8 @@ impl ArrowFunctionDecl {
}
impl Executable for ArrowFunctionDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(interpreter.create_function(
fn run(&self, context: &mut Context) -> Result<Value> {
Ok(context.create_function(
self.params().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE

19
boa/src/syntax/ast/node/declaration/const_decl_list/mod.rs

@ -1,10 +1,10 @@
use crate::{
environment::lexical_environment::VariableScope,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, Identifier, Node},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -36,20 +36,21 @@ pub struct ConstDeclList {
}
impl Executable for ConstDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
for decl in self.as_ref() {
let val = if let Some(init) = decl.init() {
init.run(interpreter)?
init.run(context)?
} else {
return interpreter.throw_syntax_error("missing = in const declaration");
return context.throw_syntax_error("missing = in const declaration");
};
interpreter
.realm_mut()
.environment
.create_immutable_binding(decl.name().to_owned(), false, VariableScope::Block);
context.realm_mut().environment.create_immutable_binding(
decl.name().to_owned(),
false,
VariableScope::Block,
);
interpreter
context
.realm_mut()
.environment
.initialize_binding(decl.name(), val);

10
boa/src/syntax/ast/node/declaration/function_decl/mod.rs

@ -2,10 +2,10 @@ use crate::{
builtins::function::FunctionFlags,
environment::lexical_environment::VariableScope,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -84,9 +84,9 @@ impl FunctionDecl {
}
impl Executable for FunctionDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec");
let val = interpreter.create_function(
let val = context.create_function(
self.parameters().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
@ -94,13 +94,13 @@ impl Executable for FunctionDecl {
// Set the name and assign it in the current environment
val.set_field("name", self.name());
interpreter.realm_mut().environment.create_mutable_binding(
context.realm_mut().environment.create_mutable_binding(
self.name().to_owned(),
false,
VariableScope::Function,
);
interpreter
context
.realm_mut()
.environment
.initialize_binding(self.name(), val);

6
boa/src/syntax/ast/node/declaration/function_expr/mod.rs

@ -1,10 +1,10 @@
use crate::{
builtins::function::FunctionFlags,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -85,8 +85,8 @@ impl FunctionExpr {
}
impl Executable for FunctionExpr {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = interpreter.create_function(
fn run(&self, context: &mut Context) -> Result<Value> {
let val = context.create_function(
self.parameters().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,

10
boa/src/syntax/ast/node/declaration/let_decl_list/mod.rs

@ -1,10 +1,10 @@
use crate::{
environment::lexical_environment::VariableScope,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, Identifier, Node},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -35,18 +35,18 @@ pub struct LetDeclList {
}
impl Executable for LetDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
Some(v) => v.run(context)?,
None => Value::undefined(),
};
interpreter.realm_mut().environment.create_mutable_binding(
context.realm_mut().environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
);
interpreter
context
.realm_mut()
.environment
.initialize_binding(var.name(), val);

8
boa/src/syntax/ast/node/declaration/var_decl_list/mod.rs

@ -1,10 +1,10 @@
use crate::{
environment::lexical_environment::VariableScope,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, Identifier, Node},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -36,13 +36,13 @@ pub struct VarDeclList {
}
impl Executable for VarDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
Some(v) => v.run(context)?,
None => Value::undefined(),
};
let environment = &mut interpreter.realm_mut().environment;
let environment = &mut context.realm_mut().environment;
if environment.has_binding(var.name()) {
if var.init().is_some() {

8
boa/src/syntax/ast/node/field/get_const_field/mod.rs

@ -1,10 +1,10 @@
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
value::{Type, Value},
Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -63,10 +63,10 @@ impl GetConstField {
}
impl Executable for GetConstField {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
fn run(&self, context: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(context)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
obj = Value::Object(obj.to_object(context)?);
}
Ok(obj.get_field(self.field()))

12
boa/src/syntax/ast/node/field/get_field/mod.rs

@ -1,10 +1,10 @@
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
value::{Type, Value},
Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -62,14 +62,14 @@ impl GetField {
}
impl Executable for GetField {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
fn run(&self, context: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(context)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
obj = Value::Object(obj.to_object(context)?);
}
let field = self.field().run(interpreter)?;
let field = self.field().run(context)?;
Ok(obj.get_field(field.to_property_key(interpreter)?))
Ok(obj.get_field(field.to_property_key(context)?))
}
}

14
boa/src/syntax/ast/node/identifier/mod.rs

@ -1,7 +1,11 @@
//! Local identifier node.
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "serde")]
@ -31,12 +35,12 @@ pub struct Identifier {
}
impl Executable for Identifier {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
fn run(&self, context: &mut Context) -> Result<Value> {
context
.realm()
.environment
.get_binding_value(self.as_ref())
.ok_or_else(|| interpreter.construct_reference_error(self.as_ref()))
.ok_or_else(|| context.construct_reference_error(self.as_ref()))
}
}

6
boa/src/syntax/ast/node/iteration/continue_node/mod.rs

@ -1,9 +1,9 @@
use crate::{
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -46,8 +46,8 @@ impl Continue {
}
impl Executable for Continue {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
fn run(&self, context: &mut Context) -> Result<Value> {
context
.executor()
.set_current_state(InterpreterState::Continue(self.label().map(Box::from)));

22
boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs

@ -1,9 +1,9 @@
use crate::{
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -67,21 +67,21 @@ impl DoWhileLoop {
}
impl Executable for DoWhileLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
fn run(&self, context: &mut Context) -> Result<Value> {
let mut result = self.body().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
return Ok(result);
}
InterpreterState::Continue(_label) => {
// TODO continue to label;
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
@ -94,21 +94,21 @@ impl Executable for DoWhileLoop {
}
}
while self.cond().run(interpreter)?.to_boolean() {
result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
while self.cond().run(context)?.to_boolean() {
result = self.body().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop

22
boa/src/syntax/ast/node/iteration/for_loop/mod.rs

@ -1,10 +1,10 @@
use crate::{
environment::lexical_environment::new_declarative_environment,
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -98,35 +98,35 @@ impl ForLoop {
}
impl Executable for ForLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
// Create the block environment.
let _timer = BoaProfiler::global().start_event("ForLoop", "exec");
{
let env = &mut interpreter.realm_mut().environment;
let env = &mut context.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}
if let Some(init) = self.init() {
init.run(interpreter)?;
init.run(context)?;
}
while self
.condition()
.map(|cond| cond.run(interpreter).map(|v| v.to_boolean()))
.map(|cond| cond.run(context).map(|v| v.to_boolean()))
.transpose()?
.unwrap_or(true)
{
let result = self.body().run(interpreter)?;
let result = self.body().run(context)?;
match interpreter.executor().get_current_state() {
match context.executor().get_current_state() {
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, interpreter, break);
handle_state_with_labels!(self, label, context, break);
break;
}
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, interpreter, continue);
handle_state_with_labels!(self, label, context, continue);
}
InterpreterState::Return => {
@ -138,12 +138,12 @@ impl Executable for ForLoop {
}
if let Some(final_expr) = self.final_expr() {
final_expr.run(interpreter)?;
final_expr.run(context)?;
}
}
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
let _ = context.realm_mut().environment.pop();
Ok(Value::undefined())
}

46
boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs

@ -2,10 +2,10 @@ use crate::{
builtins::iterable::get_iterator,
environment::lexical_environment::{new_declarative_environment, VariableScope},
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -65,20 +65,20 @@ impl From<ForOfLoop> for Node {
}
impl Executable for ForOfLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("ForOf", "exec");
let iterable = self.iterable().run(interpreter)?;
let iterator = get_iterator(interpreter, iterable)?;
let iterable = self.iterable().run(context)?;
let iterator = get_iterator(context, iterable)?;
let mut result = Value::undefined();
loop {
{
let env = &mut interpreter.realm_mut().environment;
let env = &mut context.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}
let iterator_result = iterator.next(interpreter)?;
let iterator_result = iterator.next(context)?;
if iterator_result.is_done() {
break;
}
@ -86,7 +86,7 @@ impl Executable for ForOfLoop {
match self.variable() {
Node::Identifier(ref name) => {
let environment = &mut interpreter.realm_mut().environment;
let environment = &mut context.realm_mut().environment;
if environment.has_binding(name.as_ref()) {
// Binding already exists
@ -102,10 +102,10 @@ impl Executable for ForOfLoop {
}
Node::VarDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut interpreter.realm_mut().environment;
let environment = &mut context.realm_mut().environment;
if var.init().is_some() {
return interpreter.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
}
if environment.has_binding(var.name()) {
@ -120,17 +120,17 @@ impl Executable for ForOfLoop {
}
}
_ => {
return interpreter.throw_syntax_error(
return context.throw_syntax_error(
"only one variable can be declared in the head of a for-of loop",
)
}
},
Node::LetDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut interpreter.realm_mut().environment;
let environment = &mut context.realm_mut().environment;
if var.init().is_some() {
return interpreter.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
}
environment.create_mutable_binding(
@ -141,17 +141,17 @@ impl Executable for ForOfLoop {
environment.initialize_binding(var.name(), next_result);
}
_ => {
return interpreter.throw_syntax_error(
return context.throw_syntax_error(
"only one variable can be declared in the head of a for-of loop",
)
}
},
Node::ConstDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut interpreter.realm_mut().environment;
let environment = &mut context.realm_mut().environment;
if var.init().is_some() {
return interpreter.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
}
environment.create_immutable_binding(
@ -162,36 +162,36 @@ impl Executable for ForOfLoop {
environment.initialize_binding(var.name(), next_result);
}
_ => {
return interpreter.throw_syntax_error(
return context.throw_syntax_error(
"only one variable can be declared in the head of a for-of loop",
)
}
},
Node::Assign(_) => {
return interpreter.throw_syntax_error(
return context.throw_syntax_error(
"a declaration in the head of a for-of loop can't have an initializer",
);
}
_ => {
return interpreter
return context
.throw_syntax_error("unknown left hand side in head of for-of loop")
}
}
result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
result = self.body().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
@ -201,7 +201,7 @@ impl Executable for ForOfLoop {
// Continue execution.
}
}
let _ = interpreter.realm_mut().environment.pop();
let _ = context.realm_mut().environment.pop();
}
Ok(result)
}

54
boa/src/syntax/ast/node/iteration/tests.rs

@ -194,46 +194,46 @@ fn do_while_loop_continue() {
#[test]
fn for_of_loop_declaration() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var result = 0;
for (i of [1, 2, 3]) {
result = i;
}
"#;
engine.eval(scenario).unwrap();
assert_eq!(&forward(&mut engine, "result"), "3");
assert_eq!(&forward(&mut engine, "i"), "3");
context.eval(scenario).unwrap();
assert_eq!(&forward(&mut context, "result"), "3");
assert_eq!(&forward(&mut context, "i"), "3");
}
#[test]
fn for_of_loop_var() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var result = 0;
for (var i of [1, 2, 3]) {
result = i;
}
"#;
engine.eval(scenario).unwrap();
assert_eq!(&forward(&mut engine, "result"), "3");
assert_eq!(&forward(&mut engine, "i"), "3");
context.eval(scenario).unwrap();
assert_eq!(&forward(&mut context, "result"), "3");
assert_eq!(&forward(&mut context, "i"), "3");
}
#[test]
fn for_of_loop_let() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var result = 0;
for (let i of [1, 2, 3]) {
result = i;
}
"#;
engine.eval(scenario).unwrap();
assert_eq!(&forward(&mut engine, "result"), "3");
context.eval(scenario).unwrap();
assert_eq!(&forward(&mut context, "result"), "3");
assert_eq!(
&forward(
&mut engine,
&mut context,
r#"
try {
i
@ -248,18 +248,18 @@ fn for_of_loop_let() {
#[test]
fn for_of_loop_const() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var result = 0;
for (let i of [1, 2, 3]) {
result = i;
}
"#;
engine.eval(scenario).unwrap();
assert_eq!(&forward(&mut engine, "result"), "3");
context.eval(scenario).unwrap();
assert_eq!(&forward(&mut context, "result"), "3");
assert_eq!(
&forward(
&mut engine,
&mut context,
r#"
try {
i
@ -274,7 +274,7 @@ fn for_of_loop_const() {
#[test]
fn for_of_loop_break() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var result = 0;
for (var i of [1, 2, 3]) {
@ -283,14 +283,14 @@ fn for_of_loop_break() {
result = i
}
"#;
engine.eval(scenario).unwrap();
assert_eq!(&forward(&mut engine, "result"), "1");
assert_eq!(&forward(&mut engine, "i"), "2");
context.eval(scenario).unwrap();
assert_eq!(&forward(&mut context, "result"), "1");
assert_eq!(&forward(&mut context, "i"), "2");
}
#[test]
fn for_of_loop_continue() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
var result = 0;
for (var i of [1, 2, 3]) {
@ -299,14 +299,14 @@ fn for_of_loop_continue() {
result = i
}
"#;
engine.eval(scenario).unwrap();
assert_eq!(&forward(&mut engine, "result"), "2");
assert_eq!(&forward(&mut engine, "i"), "3");
context.eval(scenario).unwrap();
assert_eq!(&forward(&mut context, "result"), "2");
assert_eq!(&forward(&mut context, "i"), "3");
}
#[test]
fn for_of_loop_return() {
let mut engine = Context::new();
let mut context = Context::new();
let scenario = r#"
function foo() {
for (i of [1, 2, 3]) {
@ -315,8 +315,8 @@ fn for_of_loop_return() {
}
}
"#;
engine.eval(scenario).unwrap();
assert_eq!(&forward(&mut engine, "foo()"), "2");
context.eval(scenario).unwrap();
assert_eq!(&forward(&mut context, "foo()"), "2");
}
#[test]

14
boa/src/syntax/ast/node/iteration/while_loop/mod.rs

@ -1,9 +1,9 @@
use crate::{
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -65,17 +65,17 @@ impl WhileLoop {
}
impl Executable for WhileLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let mut result = Value::undefined();
while self.cond().run(interpreter)?.to_boolean() {
result = self.expr().run(interpreter)?;
match interpreter.executor().get_current_state() {
while self.cond().run(context)?.to_boolean() {
result = self.expr().run(context)?;
match context.executor().get_current_state() {
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, interpreter, break);
handle_state_with_labels!(self, label, context, break);
break;
}
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, interpreter, continue)
handle_state_with_labels!(self, label, context, continue)
}
InterpreterState::Return => {
return Ok(result);

79
boa/src/syntax/ast/node/mod.rs

@ -45,8 +45,11 @@ pub use self::{
try_node::{Catch, Finally, Try},
};
use super::Const;
use crate::{exec::Executable, BoaProfiler, Context, Result, Value};
use gc::{unsafe_empty_trace, Finalize, Trace};
use crate::{
exec::Executable,
gc::{empty_trace, Finalize, Trace},
BoaProfiler, Context, Result, Value,
};
use std::{
cmp::Ordering,
fmt::{self, Display},
@ -262,13 +265,13 @@ impl Node {
}
impl Executable for Node {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Executable", "exec");
match *self {
Node::AsyncFunctionDecl(ref decl) => decl.run(interpreter),
Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(interpreter),
Node::AwaitExpr(ref expr) => expr.run(interpreter),
Node::Call(ref call) => call.run(interpreter),
Node::AsyncFunctionDecl(ref decl) => decl.run(context),
Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(context),
Node::AwaitExpr(ref expr) => expr.run(context),
Node::Call(ref call) => call.run(context),
Node::Const(Const::Null) => Ok(Value::null()),
Node::Const(Const::Num(num)) => Ok(Value::rational(num)),
Node::Const(Const::Int(num)) => Ok(Value::integer(num)),
@ -279,41 +282,41 @@ impl Executable for Node {
// Do Const values need to be garbage collected? We no longer need them once we've generated Values
Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())),
Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)),
Node::Block(ref block) => block.run(interpreter),
Node::Identifier(ref identifier) => identifier.run(interpreter),
Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(interpreter),
Node::GetField(ref get_field) => get_field.run(interpreter),
Node::WhileLoop(ref while_loop) => while_loop.run(interpreter),
Node::DoWhileLoop(ref do_while) => do_while.run(interpreter),
Node::ForLoop(ref for_loop) => for_loop.run(interpreter),
Node::ForOfLoop(ref for_of_loop) => for_of_loop.run(interpreter),
Node::If(ref if_smt) => if_smt.run(interpreter),
Node::ConditionalOp(ref op) => op.run(interpreter),
Node::Switch(ref switch) => switch.run(interpreter),
Node::Object(ref obj) => obj.run(interpreter),
Node::ArrayDecl(ref arr) => arr.run(interpreter),
Node::Block(ref block) => block.run(context),
Node::Identifier(ref identifier) => identifier.run(context),
Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(context),
Node::GetField(ref get_field) => get_field.run(context),
Node::WhileLoop(ref while_loop) => while_loop.run(context),
Node::DoWhileLoop(ref do_while) => do_while.run(context),
Node::ForLoop(ref for_loop) => for_loop.run(context),
Node::ForOfLoop(ref for_of_loop) => for_of_loop.run(context),
Node::If(ref if_smt) => if_smt.run(context),
Node::ConditionalOp(ref op) => op.run(context),
Node::Switch(ref switch) => switch.run(context),
Node::Object(ref obj) => obj.run(context),
Node::ArrayDecl(ref arr) => arr.run(context),
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionDecl(ref decl) => decl.run(interpreter),
Node::FunctionDecl(ref decl) => decl.run(context),
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionExpr(ref function_expr) => function_expr.run(interpreter),
Node::ArrowFunctionDecl(ref decl) => decl.run(interpreter),
Node::BinOp(ref op) => op.run(interpreter),
Node::UnaryOp(ref op) => op.run(interpreter),
Node::New(ref call) => call.run(interpreter),
Node::Return(ref ret) => ret.run(interpreter),
Node::Throw(ref throw) => throw.run(interpreter),
Node::Assign(ref op) => op.run(interpreter),
Node::VarDeclList(ref decl) => decl.run(interpreter),
Node::LetDeclList(ref decl) => decl.run(interpreter),
Node::ConstDeclList(ref decl) => decl.run(interpreter),
Node::Spread(ref spread) => spread.run(interpreter),
Node::FunctionExpr(ref function_expr) => function_expr.run(context),
Node::ArrowFunctionDecl(ref decl) => decl.run(context),
Node::BinOp(ref op) => op.run(context),
Node::UnaryOp(ref op) => op.run(context),
Node::New(ref call) => call.run(context),
Node::Return(ref ret) => ret.run(context),
Node::Throw(ref throw) => throw.run(context),
Node::Assign(ref op) => op.run(context),
Node::VarDeclList(ref decl) => decl.run(context),
Node::LetDeclList(ref decl) => decl.run(context),
Node::ConstDeclList(ref decl) => decl.run(context),
Node::Spread(ref spread) => spread.run(context),
Node::This => {
// Will either return `this` binding or undefined
Ok(interpreter.realm().environment.get_this_binding())
Ok(context.realm().environment.get_this_binding())
}
Node::Try(ref try_node) => try_node.run(interpreter),
Node::Break(ref break_node) => break_node.run(interpreter),
Node::Continue(ref continue_node) => continue_node.run(interpreter),
Node::Try(ref try_node) => try_node.run(context),
Node::Break(ref break_node) => break_node.run(context),
Node::Continue(ref continue_node) => continue_node.run(context),
}
}
}
@ -552,5 +555,5 @@ pub enum MethodDefinitionKind {
}
unsafe impl Trace for MethodDefinitionKind {
unsafe_empty_trace!();
empty_trace!();
}

12
boa/src/syntax/ast/node/new/mod.rs

@ -1,10 +1,10 @@
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{Call, Node},
value::Value,
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -44,18 +44,18 @@ impl New {
}
impl Executable for New {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("New", "exec");
let func_object = self.expr().run(interpreter)?;
let func_object = self.expr().run(context)?;
let mut v_args = Vec::with_capacity(self.args().len());
for arg in self.args() {
v_args.push(arg.run(interpreter)?);
v_args.push(arg.run(context)?);
}
match func_object {
Value::Object(ref object) => object.construct(&v_args, interpreter),
_ => interpreter
Value::Object(ref object) => object.construct(&v_args, context),
_ => context
.throw_type_error(format!("{} is not a constructor", self.expr().to_string(),)),
}
}

10
boa/src/syntax/ast/node/object/mod.rs

@ -2,10 +2,10 @@
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -71,8 +71,8 @@ impl Object {
}
impl Executable for Object {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let global_val = &interpreter
fn run(&self, context: &mut Context) -> Result<Value> {
let global_val = &context
.realm()
.environment
.get_global_object()
@ -83,11 +83,11 @@ impl Executable for Object {
for property in self.properties().iter() {
match property {
PropertyDefinition::Property(key, value) => {
obj.set_field(key.clone(), value.run(interpreter)?);
obj.set_field(key.clone(), value.run(context)?);
}
PropertyDefinition::MethodDefinition(kind, name, func) => {
if let MethodDefinitionKind::Ordinary = kind {
obj.set_field(name.clone(), func.run(interpreter)?);
obj.set_field(name.clone(), func.run(context)?);
} else {
// TODO: Implement other types of MethodDefinitionKinds.
//unimplemented!("other types of property method definitions.");

20
boa/src/syntax/ast/node/operator/assign/mod.rs

@ -1,8 +1,10 @@
use crate::{
environment::lexical_environment::VariableScope, exec::Executable, syntax::ast::node::Node,
environment::lexical_environment::VariableScope,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -51,12 +53,12 @@ impl Assign {
}
impl Executable for Assign {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Assign", "exec");
let val = self.rhs().run(interpreter)?;
let val = self.rhs().run(context)?;
match self.lhs() {
Node::Identifier(ref name) => {
let environment = &mut interpreter.realm_mut().environment;
let environment = &mut context.realm_mut().environment;
if environment.has_binding(name.as_ref()) {
// Binding already exists
@ -71,13 +73,13 @@ impl Executable for Assign {
}
}
Node::GetConstField(ref get_const_field) => {
let val_obj = get_const_field.obj().run(interpreter)?;
let val_obj = get_const_field.obj().run(context)?;
val_obj.set_field(get_const_field.field(), val.clone());
}
Node::GetField(ref get_field) => {
let object = get_field.obj().run(interpreter)?;
let field = get_field.field().run(interpreter)?;
let key = field.to_property_key(interpreter)?;
let object = get_field.obj().run(context)?;
let field = get_field.field().run(context)?;
let key = field.to_property_key(context)?;
object.set_field(key, val.clone());
}
_ => (),

126
boa/src/syntax/ast/node/operator/bin_op/mod.rs

@ -1,12 +1,12 @@
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::{
node::Node,
op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp},
},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -57,92 +57,92 @@ impl BinOp {
}
/// Runs the assignment operators.
fn run_assign(op: AssignOp, x: Value, y: Value, interpreter: &mut Context) -> Result<Value> {
fn run_assign(op: AssignOp, x: Value, y: Value, context: &mut Context) -> Result<Value> {
match op {
AssignOp::Add => x.add(&y, interpreter),
AssignOp::Sub => x.sub(&y, interpreter),
AssignOp::Mul => x.mul(&y, interpreter),
AssignOp::Exp => x.pow(&y, interpreter),
AssignOp::Div => x.div(&y, interpreter),
AssignOp::Mod => x.rem(&y, interpreter),
AssignOp::And => x.bitand(&y, interpreter),
AssignOp::Or => x.bitor(&y, interpreter),
AssignOp::Xor => x.bitxor(&y, interpreter),
AssignOp::Shl => x.shl(&y, interpreter),
AssignOp::Shr => x.shr(&y, interpreter),
AssignOp::Ushr => x.ushr(&y, interpreter),
AssignOp::Add => x.add(&y, context),
AssignOp::Sub => x.sub(&y, context),
AssignOp::Mul => x.mul(&y, context),
AssignOp::Exp => x.pow(&y, context),
AssignOp::Div => x.div(&y, context),
AssignOp::Mod => x.rem(&y, context),
AssignOp::And => x.bitand(&y, context),
AssignOp::Or => x.bitor(&y, context),
AssignOp::Xor => x.bitxor(&y, context),
AssignOp::Shl => x.shl(&y, context),
AssignOp::Shr => x.shr(&y, context),
AssignOp::Ushr => x.ushr(&y, context),
}
}
}
impl Executable for BinOp {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
match self.op() {
op::BinOp::Num(op) => {
let x = self.lhs().run(interpreter)?;
let y = self.rhs().run(interpreter)?;
let x = self.lhs().run(context)?;
let y = self.rhs().run(context)?;
match op {
NumOp::Add => x.add(&y, interpreter),
NumOp::Sub => x.sub(&y, interpreter),
NumOp::Mul => x.mul(&y, interpreter),
NumOp::Exp => x.pow(&y, interpreter),
NumOp::Div => x.div(&y, interpreter),
NumOp::Mod => x.rem(&y, interpreter),
NumOp::Add => x.add(&y, context),
NumOp::Sub => x.sub(&y, context),
NumOp::Mul => x.mul(&y, context),
NumOp::Exp => x.pow(&y, context),
NumOp::Div => x.div(&y, context),
NumOp::Mod => x.rem(&y, context),
}
}
op::BinOp::Bit(op) => {
let x = self.lhs().run(interpreter)?;
let y = self.rhs().run(interpreter)?;
let x = self.lhs().run(context)?;
let y = self.rhs().run(context)?;
match op {
BitOp::And => x.bitand(&y, interpreter),
BitOp::Or => x.bitor(&y, interpreter),
BitOp::Xor => x.bitxor(&y, interpreter),
BitOp::Shl => x.shl(&y, interpreter),
BitOp::Shr => x.shr(&y, interpreter),
BitOp::UShr => x.ushr(&y, interpreter),
BitOp::And => x.bitand(&y, context),
BitOp::Or => x.bitor(&y, context),
BitOp::Xor => x.bitxor(&y, context),
BitOp::Shl => x.shl(&y, context),
BitOp::Shr => x.shr(&y, context),
BitOp::UShr => x.ushr(&y, context),
}
}
op::BinOp::Comp(op) => {
let x = self.lhs().run(interpreter)?;
let y = self.rhs().run(interpreter)?;
let x = self.lhs().run(context)?;
let y = self.rhs().run(context)?;
Ok(Value::from(match op {
CompOp::Equal => x.equals(&y, interpreter)?,
CompOp::NotEqual => !x.equals(&y, interpreter)?,
CompOp::Equal => x.equals(&y, context)?,
CompOp::NotEqual => !x.equals(&y, context)?,
CompOp::StrictEqual => x.strict_equals(&y),
CompOp::StrictNotEqual => !x.strict_equals(&y),
CompOp::GreaterThan => x.gt(&y, interpreter)?,
CompOp::GreaterThanOrEqual => x.ge(&y, interpreter)?,
CompOp::LessThan => x.lt(&y, interpreter)?,
CompOp::LessThanOrEqual => x.le(&y, interpreter)?,
CompOp::GreaterThan => x.gt(&y, context)?,
CompOp::GreaterThanOrEqual => x.ge(&y, context)?,
CompOp::LessThan => x.lt(&y, context)?,
CompOp::LessThanOrEqual => x.le(&y, context)?,
CompOp::In => {
if !y.is_object() {
return interpreter.throw_type_error(format!(
return context.throw_type_error(format!(
"right-hand side of 'in' should be an object, got {}",
y.get_type().as_str()
));
}
let key = x.to_property_key(interpreter)?;
interpreter.has_property(&y, &key)
let key = x.to_property_key(context)?;
context.has_property(&y, &key)
}
CompOp::InstanceOf => {
if let Some(object) = y.as_object() {
let key = interpreter.well_known_symbols().has_instance_symbol();
let key = context.well_known_symbols().has_instance_symbol();
match object.get_method(interpreter, key)? {
Some(instance_of_handler) => instance_of_handler
.call(&y, &[x], interpreter)?
.to_boolean(),
match object.get_method(context, key)? {
Some(instance_of_handler) => {
instance_of_handler.call(&y, &[x], context)?.to_boolean()
}
None if object.is_callable() => {
object.ordinary_has_instance(interpreter, &x)?
object.ordinary_has_instance(context, &x)?
}
None => {
return interpreter.throw_type_error(
return context.throw_type_error(
"right-hand side of 'instanceof' is not callable",
);
}
}
} else {
return interpreter.throw_type_error(format!(
return context.throw_type_error(format!(
"right-hand side of 'instanceof' should be an object, got {}",
y.get_type().as_str()
));
@ -155,25 +155,23 @@ impl Executable for BinOp {
let to_bool = |value| bool::from(&value);
Ok(match op {
LogOp::And => Value::from(
to_bool(self.lhs().run(interpreter)?)
&& to_bool(self.rhs().run(interpreter)?),
to_bool(self.lhs().run(context)?) && to_bool(self.rhs().run(context)?),
),
LogOp::Or => Value::from(
to_bool(self.lhs().run(interpreter)?)
|| to_bool(self.rhs().run(interpreter)?),
to_bool(self.lhs().run(context)?) || to_bool(self.rhs().run(context)?),
),
})
}
op::BinOp::Assign(op) => match self.lhs() {
Node::Identifier(ref name) => {
let v_a = interpreter
let v_a = context
.realm()
.environment
.get_binding_value(name.as_ref())
.ok_or_else(|| interpreter.construct_reference_error(name.as_ref()))?;
let v_b = self.rhs().run(interpreter)?;
let value = Self::run_assign(op, v_a, v_b, interpreter)?;
interpreter.realm_mut().environment.set_mutable_binding(
.ok_or_else(|| context.construct_reference_error(name.as_ref()))?;
let v_b = self.rhs().run(context)?;
let value = Self::run_assign(op, v_a, v_b, context)?;
context.realm_mut().environment.set_mutable_binding(
name.as_ref(),
value.clone(),
true,
@ -181,18 +179,18 @@ impl Executable for BinOp {
Ok(value)
}
Node::GetConstField(ref get_const_field) => {
let v_r_a = get_const_field.obj().run(interpreter)?;
let v_r_a = get_const_field.obj().run(context)?;
let v_a = v_r_a.get_field(get_const_field.field());
let v_b = self.rhs().run(interpreter)?;
let value = Self::run_assign(op, v_a, v_b, interpreter)?;
let v_b = self.rhs().run(context)?;
let value = Self::run_assign(op, v_a, v_b, context)?;
v_r_a.set_field(get_const_field.field(), value.clone());
Ok(value)
}
_ => Ok(Value::undefined()),
},
op::BinOp::Comma => {
self.lhs().run(interpreter)?;
Ok(self.rhs().run(interpreter)?)
self.lhs().run(context)?;
Ok(self.rhs().run(context)?)
}
}
}

38
boa/src/syntax/ast/node/operator/unary_op/mod.rs

@ -1,9 +1,9 @@
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::{node::Node, op},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -48,35 +48,35 @@ impl UnaryOp {
}
impl Executable for UnaryOp {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let x = self.target().run(interpreter)?;
fn run(&self, context: &mut Context) -> Result<Value> {
let x = self.target().run(context)?;
Ok(match self.op() {
op::UnaryOp::Minus => x.neg(interpreter)?,
op::UnaryOp::Plus => Value::from(x.to_number(interpreter)?),
op::UnaryOp::Minus => x.neg(context)?,
op::UnaryOp::Plus => Value::from(x.to_number(context)?),
op::UnaryOp::IncrementPost => {
let ret = x.clone();
let result = x.to_number(interpreter)? + 1.0;
interpreter.set_value(self.target(), result.into())?;
let result = x.to_number(context)? + 1.0;
context.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::IncrementPre => {
let result = x.to_number(interpreter)? + 1.0;
interpreter.set_value(self.target(), result.into())?
let result = x.to_number(context)? + 1.0;
context.set_value(self.target(), result.into())?
}
op::UnaryOp::DecrementPost => {
let ret = x.clone();
let result = x.to_number(interpreter)? - 1.0;
interpreter.set_value(self.target(), result.into())?;
let result = x.to_number(context)? - 1.0;
context.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::DecrementPre => {
let result = x.to_number(interpreter)? - 1.0;
interpreter.set_value(self.target(), result.into())?
let result = x.to_number(context)? - 1.0;
context.set_value(self.target(), result.into())?
}
op::UnaryOp::Not => x.not(interpreter)?.into(),
op::UnaryOp::Not => x.not(context)?.into(),
op::UnaryOp::Tilde => {
let num_v_a = x.to_number(interpreter)?;
let num_v_a = x.to_number(context)?;
Value::from(if num_v_a.is_nan() {
-1
} else {
@ -89,13 +89,13 @@ impl Executable for UnaryOp {
Node::GetConstField(ref get_const_field) => Value::boolean(
get_const_field
.obj()
.run(interpreter)?
.run(context)?
.remove_property(get_const_field.field()),
),
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(interpreter)?;
let field = &get_field.field().run(interpreter)?;
let res = obj.remove_property(field.to_string(interpreter)?.as_str());
let obj = get_field.obj().run(context)?;
let field = &get_field.field().run(context)?;
let res = obj.remove_property(field.to_string(context)?.as_str());
return Ok(Value::boolean(res));
}
Node::Identifier(_) => Value::boolean(false),

8
boa/src/syntax/ast/node/return_smt/mod.rs

@ -1,9 +1,9 @@
use crate::{
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -58,13 +58,13 @@ impl Return {
}
impl Executable for Return {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let result = match self.expr() {
Some(ref v) => v.run(interpreter),
Some(ref v) => v.run(context),
None => Ok(Value::undefined()),
};
// Set flag for return
interpreter
context
.executor()
.set_current_state(InterpreterState::Return);
result

12
boa/src/syntax/ast/node/spread/mod.rs

@ -1,5 +1,9 @@
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "serde")]
@ -45,9 +49,9 @@ impl Spread {
}
impl Executable for Spread {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
// TODO: for now we can do nothing but return the value as-is
self.val().run(interpreter)
self.val().run(context)
}
}

16
boa/src/syntax/ast/node/statement_list/mod.rs

@ -2,13 +2,11 @@
use crate::{
exec::{Executable, InterpreterState},
gc::{empty_trace, Finalize, Trace},
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;
use std::{fmt, ops::Deref, rc::Rc};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -57,18 +55,18 @@ impl StatementList {
}
impl Executable for StatementList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("StatementList", "exec");
// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
// The return value is uninitialized, which means it defaults to Value::Undefined
let mut obj = Value::default();
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in self.statements().iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.executor().get_current_state() {
let val = item.run(context)?;
match context.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
obj = val;
@ -133,5 +131,5 @@ impl From<StatementList> for RcStatementList {
// SAFETY: This is safe for types not containing any `Trace` types.
unsafe impl Trace for RcStatementList {
unsafe_empty_trace!();
empty_trace!();
}

22
boa/src/syntax/ast/node/switch/mod.rs

@ -2,10 +2,10 @@
//!
use crate::{
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
use crate::syntax::ast::node::StatementList;
@ -122,11 +122,11 @@ impl Switch {
}
impl Executable for Switch {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = self.val().run(interpreter)?;
fn run(&self, context: &mut Context) -> Result<Value> {
let val = self.val().run(context)?;
let mut result = Value::null();
let mut matched = false;
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
@ -137,10 +137,10 @@ impl Executable for Switch {
for case in self.cases().iter() {
let cond = case.condition();
let block = case.body();
if fall_through || val.strict_equals(&cond.run(interpreter)?) {
if fall_through || val.strict_equals(&cond.run(context)?) {
matched = true;
let result = block.run(interpreter)?;
match interpreter.executor().get_current_state() {
let result = block.run(context)?;
match context.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
return Ok(result);
@ -148,7 +148,7 @@ impl Executable for Switch {
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Break statement encountered so therefore end switch statement.
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
break;
@ -167,12 +167,12 @@ impl Executable for Switch {
if !matched {
if let Some(default) = self.default() {
interpreter
context
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in default.iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.executor().get_current_state() {
let val = item.run(context)?;
match context.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
result = val;

12
boa/src/syntax/ast/node/throw/mod.rs

@ -1,5 +1,9 @@
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "serde")]
@ -43,8 +47,8 @@ impl Throw {
impl Executable for Throw {
#[inline]
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Err(self.expr().run(interpreter)?)
fn run(&self, context: &mut Context) -> Result<Value> {
Err(self.expr().run(context)?)
}
}

14
boa/src/syntax/ast/node/try_node/mod.rs

@ -1,10 +1,10 @@
use crate::{
environment::lexical_environment::{new_declarative_environment, VariableScope},
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{Block, Identifier, Node},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
@ -92,13 +92,13 @@ impl Try {
}
impl Executable for Try {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Try", "exec");
let res = self.block().run(interpreter).map_or_else(
let res = self.block().run(context).map_or_else(
|err| {
if let Some(catch) = self.catch() {
{
let env = &mut interpreter.realm_mut().environment;
let env = &mut context.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
@ -114,10 +114,10 @@ impl Executable for Try {
}
}
let res = catch.block().run(interpreter);
let res = catch.block().run(context);
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
let _ = context.realm_mut().environment.pop();
res
} else {
@ -128,7 +128,7 @@ impl Executable for Try {
);
if let Some(finally) = self.finally() {
finally.run(interpreter)?;
finally.run(context)?;
}
res

16
boa/src/syntax/ast/op.rs

@ -1,6 +1,6 @@
//! This module implements various structure for logic handling.
use gc::{unsafe_empty_trace, Finalize, Trace};
use crate::gc::{empty_trace, Finalize, Trace};
use std::fmt::{Display, Formatter, Result};
#[cfg(feature = "serde")]
@ -113,7 +113,7 @@ impl Display for NumOp {
}
unsafe impl Trace for NumOp {
unsafe_empty_trace!();
empty_trace!();
}
/// A unary operator is one that takes a single operand/argument and performs an operation.
@ -329,7 +329,7 @@ impl Display for UnaryOp {
}
unsafe impl Trace for UnaryOp {
unsafe_empty_trace!();
empty_trace!();
}
/// A bitwise operator is an operator used to perform bitwise operations
@ -444,7 +444,7 @@ impl Display for BitOp {
}
unsafe impl Trace for BitOp {
unsafe_empty_trace!();
empty_trace!();
}
/// A comparison operator compares its operands and returns a logical value based on whether the comparison is true.
@ -641,7 +641,7 @@ impl Display for CompOp {
}
unsafe impl Trace for CompOp {
unsafe_empty_trace!();
empty_trace!();
}
/// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value.
@ -699,7 +699,7 @@ impl Display for LogOp {
}
unsafe impl Trace for LogOp {
unsafe_empty_trace!();
empty_trace!();
}
/// This represents a binary operation between two values.
@ -783,7 +783,7 @@ impl Display for BinOp {
}
unsafe impl Trace for BinOp {
unsafe_empty_trace!();
empty_trace!();
}
/// An assignment operator assigns a value to its left operand based on the value of its right operand.
@ -953,7 +953,7 @@ pub enum AssignOp {
}
unsafe impl Trace for AssignOp {
unsafe_empty_trace!();
empty_trace!();
}
impl Display for AssignOp {

18
boa/src/value/equality.rs

@ -37,7 +37,7 @@ impl Value {
/// This method is executed when doing abstract equality comparisons with the `==` operator.
/// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
#[allow(clippy::float_cmp)]
pub fn equals(&self, other: &Self, interpreter: &mut Context) -> Result<bool> {
pub fn equals(&self, other: &Self, context: &mut Context) -> Result<bool> {
// 1. If Type(x) is the same as Type(y), then
// a. Return the result of performing Strict Equality Comparison x === y.
if self.get_type() == other.get_type() {
@ -59,8 +59,8 @@ impl Value {
| (Self::String(_), Self::Rational(_))
| (Self::Rational(_), Self::Boolean(_))
| (Self::Integer(_), Self::Boolean(_)) => {
let x = self.to_number(interpreter)?;
let y = other.to_number(interpreter)?;
let x = self.to_number(context)?;
let y = other.to_number(context)?;
Number::equal(x, y)
}
@ -80,23 +80,23 @@ impl Value {
},
// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
(Self::Boolean(x), _) => return other.equals(&Value::from(*x as i32), interpreter),
(Self::Boolean(x), _) => return other.equals(&Value::from(*x as i32), context),
// 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
(_, Self::Boolean(y)) => return self.equals(&Value::from(*y as i32), interpreter),
(_, Self::Boolean(y)) => return self.equals(&Value::from(*y as i32), context),
// 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 = self.to_primitive(interpreter, PreferredType::Default)?;
return primitive.equals(other, interpreter);
let primitive = self.to_primitive(context, PreferredType::Default)?;
return primitive.equals(other, context);
}
// 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 = other.to_primitive(interpreter, PreferredType::Default)?;
return primitive.equals(self, interpreter);
let primitive = other.to_primitive(context, PreferredType::Default)?;
return primitive.equals(self, context);
}
// 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then

118
boa/src/value/mod.rs

@ -215,24 +215,24 @@ impl Value {
}
/// Converts the `Value` to `JSON`.
pub fn to_json(&self, interpreter: &mut Context) -> Result<JSONValue> {
pub fn to_json(&self, context: &mut Context) -> Result<JSONValue> {
let to_json = self.get_field("toJSON");
if to_json.is_function() {
let json_value = interpreter.call(&to_json, self, &[])?;
return json_value.to_json(interpreter);
let json_value = context.call(&to_json, self, &[])?;
return json_value.to_json(context);
}
match *self {
Self::Null => Ok(JSONValue::Null),
Self::Boolean(b) => Ok(JSONValue::Bool(b)),
Self::Object(ref obj) => obj.to_json(interpreter),
Self::Object(ref obj) => obj.to_json(context),
Self::String(ref str) => Ok(JSONValue::String(str.to_string())),
Self::Rational(num) => Ok(JSONValue::Number(
JSONNumber::from_str(&Number::to_native_string(num)).unwrap(),
)),
Self::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))),
Self::BigInt(_) => {
Err(interpreter.construct_type_error("BigInt value can't be serialized in JSON"))
Err(context.construct_type_error("BigInt value can't be serialized in JSON"))
}
Self::Symbol(_) | Self::Undefined => {
unreachable!("Symbols and Undefined JSON Values depend on parent type");
@ -523,7 +523,11 @@ impl Value {
/// 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 Context, preferred_type: PreferredType) -> Result<Value> {
pub fn to_primitive(
&self,
context: &mut Context,
preferred_type: PreferredType,
) -> Result<Value> {
// 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(obj) = self {
@ -537,7 +541,7 @@ impl Value {
};
// g. Return ? OrdinaryToPrimitive(input, hint).
obj.ordinary_to_primitive(ctx, hint)
obj.ordinary_to_primitive(context, hint)
} else {
// 3. Return input.
Ok(self.clone())
@ -547,13 +551,13 @@ impl Value {
/// Converts the value to a `BigInt`.
///
/// This function is equivelent to `BigInt(value)` in JavaScript.
pub fn to_bigint(&self, ctx: &mut Context) -> Result<RcBigInt> {
pub fn to_bigint(&self, context: &mut Context) -> Result<RcBigInt> {
match self {
Value::Null => Err(ctx.construct_type_error("cannot convert null to a BigInt")),
Value::Null => Err(context.construct_type_error("cannot convert null to a BigInt")),
Value::Undefined => {
Err(ctx.construct_type_error("cannot convert undefined to a BigInt"))
Err(context.construct_type_error("cannot convert undefined to a BigInt"))
}
Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, ctx)?)),
Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, context)?)),
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))),
@ -561,17 +565,19 @@ impl Value {
if let Ok(bigint) = BigInt::try_from(*num) {
return Ok(RcBigInt::from(bigint));
}
Err(ctx.construct_type_error(format!(
Err(context.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)
let primitive = self.to_primitive(context, PreferredType::Number)?;
primitive.to_bigint(context)
}
Value::Symbol(_) => {
Err(context.construct_type_error("cannot convert Symbol to a BigInt"))
}
Value::Symbol(_) => Err(ctx.construct_type_error("cannot convert Symbol to a BigInt")),
}
}
@ -594,7 +600,7 @@ impl Value {
/// Converts the value to a string.
///
/// This function is equivalent to `String(value)` in JavaScript.
pub fn to_string(&self, ctx: &mut Context) -> Result<RcString> {
pub fn to_string(&self, context: &mut Context) -> Result<RcString> {
match self {
Value::Null => Ok("null".into()),
Value::Undefined => Ok("undefined".into()),
@ -602,11 +608,11 @@ impl Value {
Value::Rational(rational) => Ok(Number::to_native_string(*rational).into()),
Value::Integer(integer) => Ok(integer.to_string().into()),
Value::String(string) => Ok(string.clone()),
Value::Symbol(_) => Err(ctx.construct_type_error("can't convert symbol to string")),
Value::Symbol(_) => Err(context.construct_type_error("can't convert symbol to string")),
Value::BigInt(ref bigint) => Ok(bigint.to_string().into()),
Value::Object(_) => {
let primitive = self.to_primitive(ctx, PreferredType::String)?;
primitive.to_string(ctx)
let primitive = self.to_primitive(context, PreferredType::String)?;
primitive.to_string(context)
}
}
}
@ -616,34 +622,34 @@ impl Value {
/// This function is equivalent to `Object(value)` in JavaScript
///
/// See: <https://tc39.es/ecma262/#sec-toobject>
pub fn to_object(&self, ctx: &mut Context) -> Result<GcObject> {
pub fn to_object(&self, context: &mut Context) -> Result<GcObject> {
match self {
Value::Undefined | Value::Null => {
Err(ctx.construct_type_error("cannot convert 'null' or 'undefined' to object"))
Err(context.construct_type_error("cannot convert 'null' or 'undefined' to object"))
}
Value::Boolean(boolean) => {
let prototype = ctx.standard_objects().boolean_object().prototype();
let prototype = context.standard_objects().boolean_object().prototype();
Ok(GcObject::new(Object::with_prototype(
prototype.into(),
ObjectData::Boolean(*boolean),
)))
}
Value::Integer(integer) => {
let prototype = ctx.standard_objects().number_object().prototype();
let prototype = context.standard_objects().number_object().prototype();
Ok(GcObject::new(Object::with_prototype(
prototype.into(),
ObjectData::Number(f64::from(*integer)),
)))
}
Value::Rational(rational) => {
let prototype = ctx.standard_objects().number_object().prototype();
let prototype = context.standard_objects().number_object().prototype();
Ok(GcObject::new(Object::with_prototype(
prototype.into(),
ObjectData::Number(*rational),
)))
}
Value::String(ref string) => {
let prototype = ctx.standard_objects().string_object().prototype();
let prototype = context.standard_objects().string_object().prototype();
let mut object = GcObject::new(Object::with_prototype(
prototype.into(),
@ -654,14 +660,14 @@ impl Value {
Ok(object)
}
Value::Symbol(ref symbol) => {
let prototype = ctx.standard_objects().symbol_object().prototype();
let prototype = context.standard_objects().symbol_object().prototype();
Ok(GcObject::new(Object::with_prototype(
prototype.into(),
ObjectData::Symbol(symbol.clone()),
)))
}
Value::BigInt(ref bigint) => {
let prototype = ctx.standard_objects().bigint_object().prototype();
let prototype = context.standard_objects().bigint_object().prototype();
Ok(GcObject::new(Object::with_prototype(
prototype.into(),
ObjectData::BigInt(bigint.clone()),
@ -674,16 +680,16 @@ impl Value {
/// Converts the value to a `PropertyKey`, that can be used as a key for properties.
///
/// See <https://tc39.es/ecma262/#sec-topropertykey>
pub fn to_property_key(&self, ctx: &mut Context) -> Result<PropertyKey> {
pub fn to_property_key(&self, context: &mut Context) -> Result<PropertyKey> {
Ok(match self {
// Fast path:
Value::String(string) => string.clone().into(),
Value::Symbol(symbol) => symbol.clone().into(),
// Slow path:
_ => match self.to_primitive(ctx, PreferredType::String)? {
_ => match self.to_primitive(context, PreferredType::String)? {
Value::String(ref string) => string.clone().into(),
Value::Symbol(ref symbol) => symbol.clone().into(),
primitive => primitive.to_string(ctx)?.into(),
primitive => primitive.to_string(context)?.into(),
},
})
}
@ -691,12 +697,12 @@ impl Value {
/// It returns value converted to a numeric value of type `Number` or `BigInt`.
///
/// See: <https://tc39.es/ecma262/#sec-tonumeric>
pub fn to_numeric(&self, ctx: &mut Context) -> Result<Numeric> {
let primitive = self.to_primitive(ctx, PreferredType::Number)?;
pub fn to_numeric(&self, context: &mut Context) -> Result<Numeric> {
let primitive = self.to_primitive(context, PreferredType::Number)?;
if let Some(bigint) = primitive.as_bigint() {
return Ok(bigint.clone().into());
}
Ok(self.to_number(ctx)?.into())
Ok(self.to_number(context)?.into())
}
/// Converts a value to an integral 32 bit unsigned integer.
@ -704,12 +710,12 @@ impl Value {
/// This function is equivalent to `value | 0` in JavaScript
///
/// See: <https://tc39.es/ecma262/#sec-toint32>
pub fn to_u32(&self, ctx: &mut Context) -> Result<u32> {
pub fn to_u32(&self, context: &mut Context) -> Result<u32> {
// This is the fast path, if the value is Integer we can just return it.
if let Value::Integer(number) = *self {
return Ok(number as u32);
}
let number = self.to_number(ctx)?;
let number = self.to_number(context)?;
Ok(f64_to_uint32(number))
}
@ -717,12 +723,12 @@ impl Value {
/// Converts a value to an integral 32 bit signed integer.
///
/// See: <https://tc39.es/ecma262/#sec-toint32>
pub fn to_i32(&self, ctx: &mut Context) -> Result<i32> {
pub fn to_i32(&self, context: &mut Context) -> Result<i32> {
// This is the fast path, if the value is Integer we can just return it.
if let Value::Integer(number) = *self {
return Ok(number);
}
let number = self.to_number(ctx)?;
let number = self.to_number(context)?;
Ok(f64_to_int32(number))
}
@ -730,19 +736,21 @@ impl Value {
/// Converts a value to a non-negative integer if it is a valid integer index value.
///
/// See: <https://tc39.es/ecma262/#sec-toindex>
pub fn to_index(&self, ctx: &mut Context) -> Result<usize> {
pub fn to_index(&self, context: &mut Context) -> Result<usize> {
if self.is_undefined() {
return Ok(0);
}
let integer_index = self.to_integer(ctx)?;
let integer_index = self.to_integer(context)?;
if integer_index < 0.0 {
return Err(ctx.construct_range_error("Integer index must be >= 0"));
return Err(context.construct_range_error("Integer index must be >= 0"));
}
if integer_index > Number::MAX_SAFE_INTEGER {
return Err(ctx.construct_range_error("Integer index must be less than 2**(53) - 1"));
return Err(
context.construct_range_error("Integer index must be less than 2**(53) - 1")
);
}
Ok(integer_index as usize)
@ -751,9 +759,9 @@ impl Value {
/// Converts argument to an integer suitable for use as the length of an array-like object.
///
/// See: <https://tc39.es/ecma262/#sec-tolength>
pub fn to_length(&self, ctx: &mut Context) -> Result<usize> {
pub fn to_length(&self, context: &mut Context) -> Result<usize> {
// 1. Let len be ? ToInteger(argument).
let len = self.to_integer(ctx)?;
let len = self.to_integer(context)?;
// 2. If len ≤ +0, return +0.
if len < 0.0 {
@ -767,9 +775,9 @@ impl Value {
/// Converts a value to an integral Number value.
///
/// See: <https://tc39.es/ecma262/#sec-tointeger>
pub fn to_integer(&self, ctx: &mut Context) -> Result<f64> {
pub fn to_integer(&self, context: &mut Context) -> Result<f64> {
// 1. Let number be ? ToNumber(argument).
let number = self.to_number(ctx)?;
let number = self.to_number(context)?;
// 2. If number is +∞ or -∞, return number.
if !number.is_finite() {
@ -791,7 +799,7 @@ impl Value {
/// This function is equivalent to the unary `+` operator (`+value`) in JavaScript
///
/// See: https://tc39.es/ecma262/#sec-tonumber
pub fn to_number(&self, ctx: &mut Context) -> Result<f64> {
pub fn to_number(&self, context: &mut Context) -> Result<f64> {
match *self {
Value::Null => Ok(0.0),
Value::Undefined => Ok(f64::NAN),
@ -805,11 +813,11 @@ impl Value {
}
Value::Rational(number) => Ok(number),
Value::Integer(integer) => Ok(f64::from(integer)),
Value::Symbol(_) => Err(ctx.construct_type_error("argument must not be a symbol")),
Value::BigInt(_) => Err(ctx.construct_type_error("argument must not be a bigint")),
Value::Symbol(_) => Err(context.construct_type_error("argument must not be a symbol")),
Value::BigInt(_) => Err(context.construct_type_error("argument must not be a bigint")),
Value::Object(_) => {
let primitive = self.to_primitive(ctx, PreferredType::Number)?;
primitive.to_number(ctx)
let primitive = self.to_primitive(context, PreferredType::Number)?;
primitive.to_number(context)
}
}
}
@ -819,12 +827,12 @@ impl Value {
/// This function is equivalent to `Number(value)` in JavaScript
///
/// See: <https://tc39.es/ecma262/#sec-tonumeric>
pub fn to_numeric_number(&self, ctx: &mut Context) -> Result<f64> {
let primitive = self.to_primitive(ctx, PreferredType::Number)?;
pub fn to_numeric_number(&self, context: &mut Context) -> Result<f64> {
let primitive = self.to_primitive(context, PreferredType::Number)?;
if let Some(ref bigint) = primitive.as_bigint() {
return Ok(bigint.to_f64());
}
primitive.to_number(ctx)
primitive.to_number(context)
}
/// Check if the `Value` can be converted to an `Object`
@ -839,9 +847,9 @@ impl Value {
/// [table]: https://tc39.es/ecma262/#table-14
/// [spec]: https://tc39.es/ecma262/#sec-requireobjectcoercible
#[inline]
pub fn require_object_coercible(&self, ctx: &mut Context) -> Result<&Value> {
pub fn require_object_coercible(&self, context: &mut Context) -> Result<&Value> {
if self.is_null_or_undefined() {
Err(ctx.construct_type_error("cannot convert null or undefined to Object"))
Err(context.construct_type_error("cannot convert null or undefined to Object"))
} else {
Ok(self)
}

140
boa/src/value/operations.rs

@ -3,7 +3,7 @@ use crate::builtins::number::{f64_to_int32, f64_to_uint32, Number};
impl Value {
#[inline]
pub fn add(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn add(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) + f64::from(*y)),
@ -12,26 +12,30 @@ impl Value {
(Self::Rational(x), Self::Integer(y)) => Self::rational(x + f64::from(*y)),
(Self::String(ref x), Self::String(ref y)) => Self::string(format!("{}{}", x, y)),
(Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(ctx)?)),
(ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(ctx)?, y)),
(Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(context)?)),
(ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(context)?, y)),
(Self::BigInt(ref n1), Self::BigInt(ref n2)) => {
Self::bigint(n1.as_inner().clone() + n2.as_inner().clone())
}
// Slow path:
(_, _) => match (
self.to_primitive(ctx, PreferredType::Default)?,
other.to_primitive(ctx, PreferredType::Default)?,
self.to_primitive(context, PreferredType::Default)?,
other.to_primitive(context, PreferredType::Default)?,
) {
(Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(ctx)?)),
(ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(ctx)?, y)),
(x, y) => match (x.to_numeric(ctx)?, y.to_numeric(ctx)?) {
(Self::String(ref x), ref y) => {
Self::string(format!("{}{}", x, y.to_string(context)?))
}
(ref x, Self::String(ref y)) => {
Self::string(format!("{}{}", x.to_string(context)?, y))
}
(x, y) => match (x.to_numeric(context)?, y.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => Self::rational(x + y),
(Numeric::BigInt(ref n1), Numeric::BigInt(ref n2)) => {
Self::bigint(n1.as_inner().clone() + n2.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
)
}
@ -41,7 +45,7 @@ impl Value {
}
#[inline]
pub fn sub(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn sub(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) - f64::from(*y)),
@ -54,13 +58,13 @@ impl Value {
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => Self::rational(a - b),
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() - b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -69,7 +73,7 @@ impl Value {
}
#[inline]
pub fn mul(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn mul(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) * f64::from(*y)),
@ -82,13 +86,13 @@ impl Value {
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => Self::rational(a * b),
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() * b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -97,7 +101,7 @@ impl Value {
}
#[inline]
pub fn div(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn div(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) / f64::from(*y)),
@ -107,22 +111,22 @@ impl Value {
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
if *b.as_inner() == BigInt::from(0) {
return ctx.throw_range_error("BigInt division by zero");
return context.throw_range_error("BigInt division by zero");
}
Self::bigint(a.as_inner().clone() / b.as_inner().clone())
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => Self::rational(a / b),
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => {
if *b.as_inner() == BigInt::from(0) {
return ctx.throw_range_error("BigInt division by zero");
return context.throw_range_error("BigInt division by zero");
}
Self::bigint(a.as_inner().clone() / b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -131,7 +135,7 @@ impl Value {
}
#[inline]
pub fn rem(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn rem(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => {
@ -147,19 +151,19 @@ impl Value {
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
if *b.as_inner() == BigInt::from(0) {
return ctx.throw_range_error("BigInt division by zero");
return context.throw_range_error("BigInt division by zero");
}
Self::bigint(a.as_inner().clone() % b.as_inner().clone())
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => Self::rational(a % b),
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() % b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -168,7 +172,7 @@ impl Value {
}
#[inline]
pub fn pow(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn pow(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x).powi(*y)),
@ -180,20 +184,20 @@ impl Value {
a.as_inner()
.clone()
.pow(b)
.map_err(|msg| ctx.construct_range_error(msg))?,
.map_err(|msg| context.construct_range_error(msg))?,
),
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => Self::rational(a.powf(b)),
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => Self::bigint(
a.as_inner()
.clone()
.pow(b)
.map_err(|msg| ctx.construct_range_error(msg))?,
.map_err(|msg| context.construct_range_error(msg))?,
),
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -202,7 +206,7 @@ impl Value {
}
#[inline]
pub fn bitand(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn bitand(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x & y),
@ -217,7 +221,7 @@ impl Value {
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => {
Self::integer(f64_to_int32(a) & f64_to_int32(b))
}
@ -225,7 +229,7 @@ impl Value {
Self::bigint(a.as_inner().clone() & b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -234,7 +238,7 @@ impl Value {
}
#[inline]
pub fn bitor(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn bitor(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x | y),
@ -249,7 +253,7 @@ impl Value {
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => {
Self::integer(f64_to_int32(a) | f64_to_int32(b))
}
@ -257,7 +261,7 @@ impl Value {
Self::bigint(a.as_inner().clone() | b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -266,7 +270,7 @@ impl Value {
}
#[inline]
pub fn bitxor(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn bitxor(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x ^ y),
@ -281,7 +285,7 @@ impl Value {
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(a), Numeric::Number(b)) => {
Self::integer(f64_to_int32(a) ^ f64_to_int32(b))
}
@ -289,7 +293,7 @@ impl Value {
Self::bigint(a.as_inner().clone() ^ b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -298,7 +302,7 @@ impl Value {
}
#[inline]
pub fn shl(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn shl(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shl(*y as u32)),
@ -316,11 +320,11 @@ impl Value {
a.as_inner()
.clone()
.shift_left(b.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
.map_err(|msg| context.construct_range_error(&msg))?,
),
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => {
Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y)))
}
@ -328,10 +332,10 @@ impl Value {
x.as_inner()
.clone()
.shift_left(y.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
.map_err(|msg| context.construct_range_error(&msg))?,
),
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -340,7 +344,7 @@ impl Value {
}
#[inline]
pub fn shr(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn shr(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shr(*y as u32)),
@ -358,11 +362,11 @@ impl Value {
a.as_inner()
.clone()
.shift_right(b.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
.map_err(|msg| context.construct_range_error(&msg))?,
),
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => {
Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y)))
}
@ -370,10 +374,10 @@ impl Value {
x.as_inner()
.clone()
.shift_right(y.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
.map_err(|msg| context.construct_range_error(&msg))?,
),
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -382,7 +386,7 @@ impl Value {
}
#[inline]
pub fn ushr(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
pub fn ushr(&self, other: &Self, context: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => {
@ -399,16 +403,16 @@ impl Value {
}
// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => {
Self::rational(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y)))
}
(Numeric::BigInt(_), Numeric::BigInt(_)) => {
return ctx
return context
.throw_type_error("BigInts have no unsigned right shift, use >> instead");
}
(_, _) => {
return ctx.throw_type_error(
return context.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
@ -417,10 +421,10 @@ impl Value {
}
#[inline]
pub fn neg(&self, interpreter: &mut Context) -> Result<Value> {
pub fn neg(&self, context: &mut Context) -> Result<Value> {
Ok(match *self {
Self::Symbol(_) | Self::Undefined => Self::rational(NAN),
Self::Object(_) => Self::rational(match self.to_numeric_number(interpreter) {
Self::Object(_) => Self::rational(match self.to_numeric_number(context) {
Ok(num) => -num,
Err(_) => NAN,
}),
@ -462,7 +466,7 @@ impl Value {
&self,
other: &Self,
left_first: bool,
ctx: &mut Context,
context: &mut Context,
) -> Result<AbstractRelation> {
Ok(match (self, other) {
// Fast path (for some common operations):
@ -475,13 +479,13 @@ impl Value {
// Slow path:
(_, _) => {
let (px, py) = if left_first {
let px = self.to_primitive(ctx, PreferredType::Number)?;
let py = other.to_primitive(ctx, PreferredType::Number)?;
let px = self.to_primitive(context, PreferredType::Number)?;
let py = other.to_primitive(context, PreferredType::Number)?;
(px, py)
} else {
// NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.
let py = other.to_primitive(ctx, PreferredType::Number)?;
let px = self.to_primitive(ctx, PreferredType::Number)?;
let py = other.to_primitive(context, PreferredType::Number)?;
let px = self.to_primitive(context, PreferredType::Number)?;
(px, py)
};
@ -514,7 +518,7 @@ impl Value {
AbstractRelation::Undefined
}
}
(px, py) => match (px.to_numeric(ctx)?, py.to_numeric(ctx)?) {
(px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y),
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => (x < y).into(),
(Numeric::BigInt(ref x), Numeric::Number(y)) => {
@ -561,8 +565,8 @@ impl Value {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn lt(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match self.abstract_relation(other, true, ctx)? {
pub fn lt(&self, other: &Self, context: &mut Context) -> Result<bool> {
match self.abstract_relation(other, true, context)? {
AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
}
@ -578,8 +582,8 @@ impl Value {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than_or_equal
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn le(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match other.abstract_relation(self, false, ctx)? {
pub fn le(&self, other: &Self, context: &mut Context) -> Result<bool> {
match other.abstract_relation(self, false, context)? {
AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false),
}
@ -595,8 +599,8 @@ impl Value {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn gt(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match other.abstract_relation(self, false, ctx)? {
pub fn gt(&self, other: &Self, context: &mut Context) -> Result<bool> {
match other.abstract_relation(self, false, context)? {
AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
}
@ -612,8 +616,8 @@ impl Value {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than_or_equal
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn ge(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match self.abstract_relation(other, true, ctx)? {
pub fn ge(&self, other: &Self, context: &mut Context) -> Result<bool> {
match self.abstract_relation(other, true, context)? {
AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false),
}

17
boa/src/value/rcbigint.rs

@ -1,16 +1,19 @@
use crate::builtins::BigInt;
use crate::{
builtins::BigInt,
gc::{empty_trace, Finalize, Trace},
};
use std::fmt::{self, Display};
use std::ops::Deref;
use std::rc::Rc;
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::{
fmt::{self, Display},
ops::Deref,
rc::Rc,
};
#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RcBigInt(Rc<BigInt>);
unsafe impl Trace for RcBigInt {
unsafe_empty_trace!();
empty_trace!();
}
impl RcBigInt {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save